RepoSCALE-SPECIFICDOMAIN-SPECIFICCONTESTED

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.

The question

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?

The requirement

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.

The obvious build

We do code review, and that catches design problems. A reviewer who sees the pull request can say if the design is wrong.

Why it breaks

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).

How it breaks as requirements change
  • 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).
RequirementConstraintsInvariantsResponsibilitiesBoundariesInterfacesStateDependenciesFailureImplementationTestsFeedbackEvolution

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.

Constraints
  • 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).
Invariants
  • 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.

Responsibilities
  • 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).
Boundaries
  • 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.

One hour, ten questions, in this order
  1. 1
    Requirement

    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).

  2. 2
    Invariants

    What must never stop being true once this ships?

    fails by A rule enforced in three places and guaranteed in none (Where Invariants Live).

  3. 3
    State 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).

  4. 4
    Interfaces

    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).

  5. 5
    Options 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).

  6. 6
    Migration

    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).

  7. 7
    Failure 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).

  8. 8
    Security 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).

  9. 9
    Observability

    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).

  10. 10
    Tests 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.

Subscription pausing, and then "pause should also suspend entitlements"
The change

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.

No design review. Three engineers start in parallel; each stores what it needs
billing/Subscription (a `paused` flag)entitlements/Access (a `suspendedUntil` date)notifications/Preferences (a `muted` boolean)reporting/Snapshot (derives paused from invoice gaps)
testsfour module test suites that each pass, plus one integration test that was written to match the behaviour that emerged
4 modules · 1 test file

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.

One design review before implementation; pause is a state on the subscription lifecycle with one owner and an event
billing/SubscriptionLifecycle (owns the state and its transitions)three consumers of a `SubscriptionPaused` event
testsa state machine test enumerating legal and illegal transitionsthree consumer testsone end-to-end test
2 modules · 3 test files

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 it cost The reviewed design is slower to start and more constrained: entitlements now waits for an event rather than reading a flag directly, which adds latency and a failure mode — a consumer that misses the event — that the flag version did not have. It also concentrates change on one module, so two teams touching subscription states in the same sprint now conflict where before they edited different files. And the review itself cost five senior hours before a line was written, on a feature that might have been cancelled. The honest claim is not that the review made everything cheaper; it is that it made the anticipated change cheap and named the price (State Ownership).

What has to be in the room

SCALE-SPECIFICThis one-page format is calibrated for a team where the reviewers already share the context — roughly one to five teams. Below that, the page is overhead and a whiteboard is faster. Well above it, the same content has to carry its own context for readers who have never seen this system, which is how it grows into an RFC of several pages read asynchronously; the failure at that size is writing the short version and having reviewers who cannot evaluate it.

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.

The proposal that goes out the day before — one page, no longer
1SUBSCRIPTION PAUSINGdesign review, 45 min
2
3REQUIREMENT Customers on annual plans want to pause for up to 3
4 months instead of cancelling. Retention asked; ~40
5 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 else
13 writes it. Consumers react to SubscriptionPaused.
14
15OPTIONS A. State on the subscription + event <- recommended
16 B. Boolean flag read directly by each consumer
17 C. Cancel + re-subscribe with a grace period
18
19TRADE-OFF A costs an event bus dependency and a consumer that
20 can miss an event. B is simpler today and gives four
21 definitions of "paused" (see: reporting). C reuses
22 everything and loses the original start date, which
23 breaks anniversary pricing.
24
25MIGRATION No existing rows change. Paused is additive.
26 Reporting needs a backfill? NOno history to rewrite.
27
28FAILS WHEN Event delivered twice (consumers must be idempotent);
29 pause set during an in-flight invoice run (guard on
30 the transition, not in the consumer).
31
32SECURITY Who may pause? Account owner or support with an
33 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 durationproduct has not decided.
39 2. Does pausing extend the renewal date? (I think
40 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.

Cost of the next change
  • 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).
What the recommended approach costs
  • 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

Failure modes
  • 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).
Dependencies, and their direction
  • 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.
Misreads
  • "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

What to test, and at which boundary
  • 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.
How this design ages
  • 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.

Domains that do not exist yet
  • 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.