Model states, not guesses
Use explicit states and transitions to make invalid behavior easier to detect.
BY THE END, YOU CAN
- Model a finite state machine
- Separate static type hints from runtime validation
- Choose composition around meaningful responsibilities
Diagram description: queued → running (claim); running → succeeded (finish); running → failed (error); failed → queued (retry if attempts remain)
Find the states that matter
A job can be pending, running, succeeded, or failed.
Those labels are useful only when transitions are defined. Can a succeeded job become running again? Can a failed job be retried?
Does cancellation apply to running work? Draw the allowed transitions before adding conditionals throughout the code. Each transition has preconditions and effects.
A state machine makes missing cases visible and gives tests a natural structure. It also prevents several Boolean flags from representing contradictory combinations such as both completed and pending.
Let the model carry constraints
Python dataclasses can group related fields, while enums can name a finite set of states.
Type hints communicate expected shapes and help static analysis detect mismatches before execution. They do not replace validation of external JSON or database rows. Put validation at trust boundaries and maintain stronger assumptions inside.
Prefer a model whose fields have a clear meaning at each stage. If one giant object has dozens of optional fields, consider whether distinct states or smaller objects would express the lifecycle more precisely.
Compose behavior where it varies
A worker combines scheduling, storage, and job handling.
Passing these collaborators explicitly makes their responsibilities visible and allows focused substitutes in tests. Inheritance is useful when a genuine substitutable relationship exists, but a deep hierarchy often makes behavior harder to locate. Choose a pattern because it solves a named problem, not because the pattern is familiar.
For Relay, different job handlers can share a small callable contract while storage and retry policy remain independent. Good design reduces the number of facts a maintainer must hold at once.
COMMON FAILURE MODE
Adding an enum does not enforce valid transitions by itself. Centralize transition rules and validate changes through that boundary.
TRY IT YOURSELF
Draw Relay's state machine including cancellation and retry. List two transitions that must be rejected and the evidence needed to authorize a completion.
Make the call in the laboratory.
The two-question checkpoint, your notes, and your saved progress are in the laboratory. Free, with no account.
Open in the laboratory ↗CONNECT IT TO RELAY
Centralize Relay's job transitions and test an invalid transition, such as acknowledging a job that was never claimed.