← Code you can trust STAGE 03 · 34 MIN

Model states, not guesses

Use explicit states and transitions to make invalid behavior easier to detect.

Open in the laboratory ↗

BY THE END, YOU CAN

  • Model a finite state machine
  • Separate static type hints from runtime validation
  • Choose composition around meaningful responsibilities
Model the states that matter. Only the arrows drawn here are legal moves.

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.

RETRIEVAL CHECK / APPLY THE IDEA

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.