Architecture & Technical Design · master area
State Machine Design
Lifecycle transitions for entities with more than three states. The disallowed transitions — the ones you forget to forbid — are the silent source of half the system's bugs.
Owners: Tech Lead, Developer Phase it lives in: What We Build (Volume III) The corpus principle this enacts: Name every state the person can be in — and every state the entity can be in.
Where it lives in the chain
- What We Build · Sequence, Schema, API — where state lifecycles get drawn
- What We Build · Design Shaping — where UI states reflect entity states
How to do this
For any entity with more than three states (draft, submitted, grading, graded, published, revoked…), draw the state machine explicitly:
- States — every state the entity can be in, named.
- Transitions — which state can move to which, and what triggers each transition.
- Disallowed transitions — what is explicitly not allowed, with a reason.
graded→draftis forbidden; the system rejects the operation rather than corrupting history. - Terminal states — which states are end-of-life. From
revoked, no transitions. - Side effects — which transitions fire events, send emails, charge cards.
submitted→gradingtriggers nothing.grading→gradedpublishesExamGradedand notifies the student.
What good practice looks like
The state machine is drawn, not described in prose. Mermaid in the ADR, or a hand-sketched diagram next to the schema. The states are enforced in code — enum, union type, or constraint table — so the type system rejects an Exam in an unknown state.
The state machine is read aloud in the amigos session. "What happens if a teacher submits a grade twice in 500ms?" — leads to a transition the diagram either allows (with idempotency) or forbids (with a clear error). "What happens if a student withdraws from the class mid-grading?" — leads to a withdrawn state and a transition the diagram either has or needs.
A team without a state machine ends up with boolean flags that combine into illegal states (isSubmitted=true, isDraft=true), and the bugs all live where two booleans disagreed. The state machine is what makes those bugs impossible to write, not just impossible to ship.
Related crafts
- Domain Modelling — the entity the state machine belongs to
- User Flow Design — the UI states that mirror entity states