The eleven-item low-level design checklist
coreintermediateEleven items, run against one component after you have designed it. They fall into four groups. What exists: entities, interfaces, class responsibilities — the nouns, the contracts between them, and who owns each rule. How it changes: state machines, database schema, indexes — the legal transitions, the shape that outlives the code, and the access paths that make queries survive growth. What goes wrong: error handling, concurrency controls, retries — what a caller sees on failure, what happens when two requests touch the same row, and which operations are safe to run twice. How it holds together: module boundaries and testability. The schema and the state machine carry the most weight. A schema binds every future writer, because changing it later means migrating data that already exists. A state machine turns "what if the payment succeeds after the order was cancelled" from an unasked question into a missing arrow you can see. Where the high-level checklist finds missing behaviour, this one finds missing rules — the transitions, constraints and concurrent cases nobody enumerated. Each item gets the same three answers as any checklist: handled in this specific way, deliberately not handled because X, or not yet considered.
Think of it as
A building inspection after the walls are up, not the architect's sketch. The sketch already said where the rooms go; the inspection asks the questions that only have answers once something concrete exists — is this load-bearing, what is the wiring rated for, what happens when the water is on and the power is out. Every item here is that kind of question: it has no useful answer against a box on a diagram, and a very specific one against a component someone is about to build.
What we're doing: Run the "how it changes" and "what goes wrong" groups against a seat-hold component that already has a diagram.
- 10
- The expiry-versus-payment race is the single most common missing arrow in reservation systems, because both paths are correct in isolation and only collide on a timer.
- 21
- Check-then-insert reads as a rule and behaves as a race. Moving it into a constraint means the database enforces it for every writer, including the one someone adds next year.
- 37
- A retry that creates a second hold is not a bug in the client. It is a missing decision in this component, and the checklist is the moment it gets made.
Why this works: None of these four are architecture mistakes. They are rules nobody was prompted to write down, and every one of them fails only under a condition that is hard to reach by hand — a timer expiring mid-payment, two clicks in the same millisecond, a network timeout on a write. That is exactly the class of defect a low-level review is cheap at catching and production is expensive at catching.
Running the low-level list across the whole system at once
Wrong
Better
What you see: Every item is answered, the review takes twenty minutes, and no gap is found — because at system scope the honest answer to "concurrency controls" really is "transactions", and that sentence hides every place there are none.
Why: These items are only answerable against a specific set of rows, states and callers. Widen the scope and each one collapses into a technology name, which is always true and never reviewable. Scope is what makes the list produce findings rather than agreement.
- What exists
- Entities
- Interfaces
- Class responsibilities
- How it changes
- State machines — an unasked case is a missing arrow
- Database schema — outlives the code
- Indexes
- What goes wrong
- Error handling
- Concurrency controls — only fails under load
- Retries
- How it holds together
- Module boundaries
- Testability
The eleven items, grouped by the question each answers
The same item, answered two ways — only one of them is reviewable
Remember: Eleven items, one component, run after the design exists: entities, interfaces and class responsibilities (what exists); state machines, schema and indexes (how it changes); error handling, concurrency controls and retries (what goes wrong); module boundaries and testability (how it holds together). The schema and the state machine earn the most, because a schema binds every future writer and a state machine turns an unasked case into a visibly missing arrow. Same three answers as any checklist — handled this way, deliberately not, or not yet considered.
See also: the high level design checklist · what belongs in a low level design · status and lifecycle modeling · optimistic vs pessimistic · idempotency keys for post requests · holds expiry and avoiding double booking

