Summary
A job becomes fragile when the only complete account of its behavior is the script that happened to run it. Jobs should make work executable and legible. They should not become the private home of business policy, provider behavior, and state transitions.
Problem
A job is a tempting place to keep adding logic. It already receives parameters, has a runtime, and can be launched from a CLI, worker, or scheduler. The first addition looks harmless. Over time, the entrypoint can become a private application: policy, provider calls, state handling, retry behavior, and result formatting all coupled to one execution mode.
The cost appears when the same capability is needed from an API, a review surface, or another job. The organization either duplicates behavior or turns the original script into an accidental service.
Principle
Keep job entrypoints thin. They translate execution context: parse arguments, construct explicit runtime parameters, call an owned capability, and return a structured result. Reusable behavior belongs in services, libs, integrations, or pipelines.
Platform Shape
The job layout reference makes that boundary concrete: 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. The hook checks entrypoint shape and size. Small files are not the objective. The objective is to keep execution wiring visible while the behavior it invokes remains reusable and testable outside that one process.
Tradeoffs
A thin entrypoint depends on another owned boundary, so readers may follow one more link to understand the full workflow. That is preferable to hiding the same policy in several scripts. Thin does not mean empty: execution-specific validation and adaptation still belong at the edge.