Thin Jobs, Fat Services
Summary
Jobs should be executable and readable, but they should not be the primary home for business logic. Reusable behavior belongs in services, libs, integrations, or pipelines.
Problem
Jobs are easy places to put logic because they already parse parameters and run from the CLI, worker, or scheduler. Over time, a job can become a private application with hidden policy, provider calls, state handling, and result formatting that no other surface can reuse.
Principle
Keep job entrypoints thin. They should parse arguments, construct explicit runtime parameters, call reusable components, and return structured results.
Platform Shape
The job layout reference defines the standard shape: jobs/<namespace>/<job_key>/run.py for executable entrypoints, jobs/<namespace>/job.yaml for metadata, and reusable helpers under services/<domain>/jobs/, libs/, integrations/, or pipelines/.
The repo enforces part of this with infra/hooks/check_job_entrypoints.py. That hook checks entrypoint shape and size so job files stay readable. The point is not small files for their own sake; it is preserving a clean boundary between orchestration and reusable behavior.
Source Evidence
docs/reference/job-layout.mdinfra/hooks/check_job_entrypoints.pyjobsservices
Related Reading
docs/engineering/concepts/pipelines-jobs-services-review-surfaces.mddocs/engineering/concepts/failures-are-data.md