Design Review
Before a high-impact change, discuss requirements, options, trade-offs, migration, failure modes, security and observability — because this is the last point at which a boundary can still be moved.
The requirement, the obvious build, and why it breaks
Every lesson starts where the work starts: someone asked for something, and the first implementation that comes to mind survives until the requirement changes.
When is the right moment to review a design, and what has to be on the table for the review to be worth an hour of several people?
A team is about to build subscription pausing. It touches billing, entitlements, notifications and reporting. Three engineers have already started on different parts of it.
We do code review, and that catches design problems. A reviewer who sees the pull request can say if the design is wrong.
By pull-request time the design is a fact. Moving a boundary means discarding a week of somebody's work, and reviewers know it, so they raise the objection as a comment and then approve (Review as Design Feedback — and Why It Arrives Too Late).
- By pull-request time the design is a fact. Moving a boundary means discarding a week of somebody's work, and reviewers know it, so they raise the objection as a comment and then approve (Review as Design Feedback — and Why It Arrives Too Late).
- The reviewer also sees only one diff. A design that spreads subscription state across four modules is invisible in any single pull request, and it is exactly the kind of thing a design review exists to catch (Change Amplification).
- Three engineers have already started, which means three implicit designs are already partly built and the review is now a negotiation rather than a decision.
- And the parts that fail hardest — migration, failure modes, observability, security — do not appear in a first pull request at all. They are retrofitted later into a structure with no room for them (Designing the Happy Path Last).
- Code review remains valuable and it is a different instrument: it catches defects and spreads local knowledge, and it is structurally incapable of catching a boundary (What Code Review Is For).
What limits the solution, and what must never stop being true
This domain leads with these two. A design that ignores its constraints is not a design, and an invariant nobody named is one nothing is protecting.
- The review costs an hour of four or five experienced people, which is the most expensive hour the team spends in a week.
- It has to happen before implementation, which means before anyone can demonstrate that their approach works.
- It cannot become a gate that every change passes through, or the team routes around it and the mechanism dies (Review Size is the same effect at code-review scale).
- The review happens while the boundary can still move. After the interfaces exist and other code calls them, a boundary objection is no longer actionable (Stable Boundaries).
- Every review produces a written decision, or the discussion is repeated in three months by different people (Decision Records).
- Disagreement is allowed to be unresolved and recorded as such. A review that must produce consensus produces false consensus.
Who owns what, and where the seams fall
Responsibilities decide boundaries; boundaries decide what an interface has to say.
- The proposer owns bringing a real proposal, not a blank page: the requirement, at least two options, and a recommendation with its cost (The Trade-off Matrix).
- The reviewers own asking the questions that will be expensive later — ownership, state, migration, failure, security, observability — rather than reviewing taste.
- Somebody owns writing the decision down, including the objections that were overruled and why (Architecture Decision Records).
- The team owns deciding what needs a review at all, and keeping that list short enough that the mechanism stays cheap (The Complexity Budget).
- The review sits between requirements and implementation. Earlier and there is nothing concrete to react to; later and nothing can change (The Design Loop).
- It covers what crosses module boundaries: who owns which state, what the interfaces are, what migrates, what fails. It does not cover what happens inside a module, which is the implementer's business (Local Reasoning).
- The threshold for requiring one should be about blast radius, not size: a fifty-line change that introduces a new state machine warrants a review, and a two-thousand-line change inside one module does not (Designing a Feature Before Writing It).
The agenda, and what each item catches
The value of a fixed agenda is not consistency, it is that the expensive questions get asked in the second half of the hour when everyone has stopped concentrating. Migration, failure and observability are always in the second half, and they are always the ones that hurt later.
Each item below has a failure attached, and the failures are what justify the item. An agenda item with no named failure should be removed.
- 1Requirement
What was actually asked for, in the requester's words, and what happens if we do nothing?
fails by Reviewing a solution to a requirement nobody has stated, which is how half of these end up cancelled (Requirements Before Design).
- 2Invariants
What must never stop being true once this ships?
fails by A rule enforced in three places and guaranteed in none (Where Invariants Live).
- 3State and ownership
What new state exists, who may mutate it, and what its legal transitions are.
fails by Pause status stored in three modules with no owner, which is the specific failure this feature invites (State Ownership).
- 4Interfaces
What crosses a module boundary, and what stays hidden.
fails by An interface shaped by the implementation that then cannot change (Designing a Module Interface).
- 5Options and trade-offs
At least two designs, compared on the axes that matter, with a recommendation.
fails by One proposal, which produces validation rather than comparison (The Trade-off Matrix).
- 6Migration
What happens to existing subscriptions, and what the intermediate state looks like.
fails by Being discovered during rollout, when the intermediate state is production (Expand and Contract).
- 7Failure modes
What happens when billing is slow, the event is delivered twice, or the pause half-applies.
fails by Error handling retrofitted into a structure with no room for it (Failure-Aware Feature Design).
- 8Security and data
Who may pause whose subscription, and what personal or financial data moves.
fails by An authorisation check placed in the controller that a second entry point bypasses (Trust Boundaries).
- 9Observability
How we will know it is working, and how we will debug it at 3am.
fails by Being added after the first incident, by which time the state that mattered was never recorded (Debuggability by Design).
- 10Tests and decision
What is tested at which boundary; then write the decision and its revisit trigger.
fails by No record, so the discussion recurs in three months with different people (Decision Records).
If the hour runs out, the items that get dropped are always the last four, which are the four that are most expensive to retrofit. Reversing the order occasionally is a cheap way to find out what the agenda is hiding.
The same feature, reviewed and not
The argument for spending the most expensive hour of the week on this is quantitative, and it is worth doing the arithmetic once on a real feature rather than asserting that design review is valuable.
The critical detail is *when* the objection is available. In both scenarios somebody eventually notices that pause status has no owner. In one, noticing costs a whiteboard eraser; in the other it costs three engineers a fortnight.
Build subscription pausing across billing, entitlements, notifications and reporting. Then, six weeks later, the requirement arrives that a paused subscription must also suspend feature entitlements immediately.
Four representations of one concept, none authoritative. The follow-up requirement means deciding, retroactively and under pressure, which one is the truth — and reporting has already shipped numbers derived from a fourth definition that nobody knew existed.
The follow-up requirement is a new consumer of an event that already exists. One module changes, and reporting is consistent by construction because it derives from the same state everything else does.
What has to be in the room
A review with a blank page produces a design session, which is a different and usually less valuable meeting. What makes an hour productive is that the proposer has already done the thinking and the room is checking it.
The proposal below is deliberately short. Anything longer will not be read, and a design review whose material was not read is an hour of people reading in silence.
1SUBSCRIPTION PAUSING — design review, 45 min2 3REQUIREMENT Customers on annual plans want to pause for up to 34 months instead of cancelling. Retention asked; ~405 cancellations/month cite "not using it right now".6 7INVARIANTS - A paused subscription is never billed.8 - Entitlements match billing state at all times.9 - Reporting MRR excludes paused, and says so.10 11STATE New: Subscription.state gains Paused.12 Owner: billing/SubscriptionLifecycle. Nothing else13 writes it. Consumers react to SubscriptionPaused.14 15OPTIONS A. State on the subscription + event <- recommended16 B. Boolean flag read directly by each consumer17 C. Cancel + re-subscribe with a grace period18 19TRADE-OFF A costs an event bus dependency and a consumer that20 can miss an event. B is simpler today and gives four21 definitions of "paused" (see: reporting). C reuses22 everything and loses the original start date, which23 breaks anniversary pricing.24 25MIGRATION No existing rows change. Paused is additive.26 Reporting needs a backfill? NO — no history to rewrite.27 28FAILS WHEN Event delivered twice (consumers must be idempotent);29 pause set during an in-flight invoice run (guard on30 the transition, not in the consumer).31 32SECURITY Who may pause? Account owner or support with an33 audit entry. Not any team member. OPEN.34 35OBSERVE Counter per transition; alert on paused-but-billed,36 which is the invariant that costs us money.37 38OPEN 1. Max pause duration — product has not decided.39 2. Does pausing extend the renewal date? (I think40 yes; finance disagrees.)The two most useful sections are the last two. OPEN is what the meeting is for — a proposal with no open questions is either trivial or hiding something — and FAILS WHEN is the section that would otherwise be written after the first incident. Note also that the migration section says "no" and says why: an explicit nothing is worth a line, because it is the difference between considered and forgotten.
How to build it
Most important first.
- Hold it before implementation and after a spike, if a spike is needed to know anything. Reviewing pure speculation is as wasteful as reviewing finished code is futile.
- Require at least two options and a recommendation. A single proposal produces validation; two produce a comparison, which is where the reasoning surfaces (The Trade-off Matrix).
- Use a fixed agenda so the expensive questions get asked when everyone is tired: requirements, invariants, ownership and state, interfaces, options and trade-offs, migration, failure modes, security, observability, tests (A Feature Design Template).
- Timebox it to an hour, with material circulated beforehand and read in the room if necessary. A review that requires prior reading which nobody did is the most common way this fails.
- Write the outcome as a decision record with the revisit trigger, and link it from the code that implements it (Docs Close to Code).
- Keep the required list short: new persistent state, a new external dependency, a new module boundary, anything touching money, auth or personal data. Everything else is optional and available on request (Trust Boundaries).
What the next change costs
The field this whole domain exists for. A structure is only better if it makes the change after this one cheaper — and it is worth saying which changes it does not help.
- Without a review: the boundary is discovered to be wrong during integration, typically two to four weeks in, when three modules already depend on it. Cost is the rework plus the coordination between three engineers plus whatever has been built on top.
- With a review: an hour of five people, and a boundary objection costs a whiteboard eraser. The ratio is the entire argument and it is roughly two orders of magnitude for the changes worth reviewing.
- The change *after* this one is where the return compounds: a design with clear state ownership absorbs "pause should also suspend entitlements" locally, while one without it re-opens all four modules (Change Amplification).
- The cost of over-applying it: an hour of five people for a change where the boundary was never in question, repeated weekly, is a real tax and it is what makes teams abandon the practice (When Design Does Not Pay).
- Reviewing before implementation means reviewing something nobody has proven works, so some review time is spent on options that a day of coding would have eliminated.
- It spends the most expensive hour on the calendar, and that hour is not spent on code review, incidents or delivery.
- A fixed agenda makes the expensive questions reliable and makes the review feel bureaucratic, and the second effect is what erodes attendance.
What can go wrong
- It becomes a gate. Everything needs one, the queue grows, and teams either wait or route around it — and routing around it is the healthier of the two responses, which tells you the mechanism has failed.
- It becomes a presentation. The proposer defends rather than explores, the audience asks polite questions, and nothing changes; the tell is that no design review has ever changed a proposal.
- It is held after implementation, where it produces cosmetic comments and resentment on both sides.
- It produces no written record, so the same discussion recurs and the decisions cannot be revisited because nobody knows what was decided (Documentation Decay).
- The mitigation fails too: a template is introduced to make reviews consistent, and it becomes a form to complete, so proposals are written to fill fields rather than to think (RFCs).
- The review depends on the requirement being understood. Half of all design reviews discover a requirements problem, and that is a success rather than a derailment (Requirements Before Design).
- It depends on senior attention, and it competes for that attention with code review, incidents and their own delivery.
- It creates a scheduling dependency: work waits for a review slot, so the cadence has to be frequent enough that waiting is measured in days.
- "So review every change." The cost is linear and the benefit is concentrated in changes that create boundaries or state. A universal gate is how the practice dies (Over-Design and Under-Design).
- "The design review approves the design." It surfaces what the proposer missed. Approval framing produces defence rather than exploration, and a defended proposal learns nothing (Tone, Disagreement and Receiving Review).
- "This is waterfall." It is one hour, before a feature, about boundaries and failure. Waterfall is committing to an architecture for a year before building anything (The Design Loop).
- "Consensus is the goal." Recorded disagreement is a better outcome than manufactured agreement, because it tells the next reader that this was contested and on what grounds.
Testing it, and how it ages
- The review should decide what is tested at which boundary, because that is a design decision and deferring it means the tests describe whatever got built (Testing as Design Feedback).
- The honest test of the mechanism itself: how often does a design review change the proposal? If the answer is never, it is a presentation and should be stopped.
- A second test: how often does a decision record get revisited because its trigger fired? Never means the triggers are decorative.
- Design reviews start informal and useful, become formal and less useful, and either get pruned back or die. The pruning is the healthy path and it requires someone to be willing to shrink their own process.
- As a team grows, the review becomes the main place cross-team boundaries are negotiated, and its agenda drifts from design toward ownership. That drift is legitimate and should be made explicit rather than resisted.
- What eventually forces a change is asynchrony: past a certain size, getting five people in a room is the constraint, and the review turns into a written document with comments — which is the RFC (RFCs).
Where this applies
This domain's advice is contested more than most. These labels say what each claim is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view rather than a caricature.
- SCALE-SPECIFICAt one team of five or six, the design review is a twenty-minute whiteboard conversation with no agenda and no document, and formalising it removes most of its value. It becomes a real mechanism when the people affected by a boundary are not in the room by default — usually the second or third team. In a large organisation it inevitably turns written and asynchronous, because assembling the right five people costs more than the review saves, and at that point it is an RFC process with a different name. Applying the large-organisation form to a five-person team produces documents nobody needed and a decision that was already made at the whiteboard.
- DOMAIN-SPECIFICWhere a design mistake is regulatory, financial or safety-critical, the review is a control rather than a courtesy: it is mandatory, it has named required participants, and recording dissent is part of the audit trail rather than a nicety. In an internal tool the same formality is theatre. The distinguishing question is whether being wrong produces a bug or an obligation.
- CONTESTEDThe strongest opposing view: design reviews before implementation are speculation dressed as rigour, because the important problems only become visible once code exists, and a review of a whiteboard drawing systematically over-weights the concerns that are easy to articulate. Teams that hold this position build a spike first and review working code, and they can point to real cases where an hour of confident architectural discussion produced a worse design than a day of prototyping. The counter is that a spike routinely becomes the implementation, at which point the boundary is fixed and the review has become a formality — which is precisely the failure this lesson is about.
Where the depth lives
This domain teaches the codebase-level structure and hands the rest off.
- — System Design — a design review for anything crossing a network boundary has to add capacity, latency budget and failure isolation to this agenda, and those questions belong to System Design rather than here.