Filter concepts by levelShowing all levels.

System Design · Section 104

System Design Review Checklist

Level
intermediate
Read
14 min
Concepts
1

Eleven questions, asked by someone who did not write the design, about a design that is finished. They run in four passes, and the order is load-bearing. Does it do the job: are the functional requirements met, is the data model correct, are concurrency and invariants handled? Does it survive: is there a single point of failure, are retries safe to run twice, what happens when the cache, the database, the queue and the external provider each fail, and how does the system recover? Does it grow: where is the first bottleneck, and can it scale horizontally? Can we run it: how is it monitored, and what does it cost? Correctness comes first because a design that misses its requirement does not need a throughput discussion, and survival comes before growth because a system that falls over at current load has no interesting behaviour at ten times that. Every item stays phrased as an open question on purpose — a closed question can be answered yes by someone who never thought about it, while "what does a customer see when the provider returns 503" cannot. The four dependency-failure questions each have a different right answer, and the provider one carries the most weight because it is the failure the author cannot fix during an incident. Two habits make the difference: follow every answer with "and what happens then", which is where findings actually come from, and never fill an item in on the author's behalf — a reviewer who supplies plausible answers is testing their own familiarity with other systems, which is precisely what does not transfer.

System Design overview

What is true here

  1. Four passes in order: does it do the job, does it survive, does it grow, can we run it.
  2. Every item is an open question — a closed one can be answered "yes" by someone who never thought about it.
  3. Cache, database, queue and provider failures are four questions with four different right answers.
  4. The findings come from the follow-up: "and what happens then?" after an answer that sounded true.
  5. A reviewer who answers on the author's behalf is reviewing their own assumptions, and the gap is always where this system differs.

What you will be able to do

  • Run the eleven questions over someone else's design and produce findings the author must answer
  • Give four distinct answers to the cache / database / queue / provider failure questions for a real system
  • Tell a non-answer ("we use transactions", "it scales horizontally") from an answer, and ask the follow-up that exposes it
  • Separate a defect in the design from a preference for a different design, and keep the review on the first

The review

Eleven questions in four passes, run by a reviewer who did not write the design.

The eleven review questions

coreintermediate

Eleven questions, asked by somebody who did not write the design, about a design that is finished. They run in four passes. Does it do the job — does it meet the functional requirements, is the data model correct, are the invariants and concurrent cases handled? Does it survive — is there a single point of failure, are retries safe to run twice, what happens when the cache, the database, the queue or the provider fails, and how does it recover? Does it grow — where is the bottleneck, and can the system scale horizontally? Can we run it — how is it monitored, and what does it cost? Every item is a question rather than a checkbox, and that is deliberate: the reviewer's job is to make the author say something specific, because a reviewer who answers on the author's behalf is reviewing their own understanding. The order matters too. Correctness first, because a design that does not meet the requirement does not need a scaling discussion, and the failure-handling questions before the growth ones, because a system that falls over at current load has no interesting behaviour at ten times current load. The strongest single question on the list is "what happens when the provider fails", because it is the one the author cannot fix during an incident and therefore the one that sets the real availability ceiling.

Think of it as

A viva, not a form. The value comes from the author having to answer out loud, in specifics, to someone who does not already share their assumptions — which is why the questions are open ("what happens when the queue backs up") rather than closed ("is the queue handled"). A closed question can be answered yes by someone who has not thought about it; an open one cannot.

text
Four passes, eleven questions

  DOES IT DO THE JOB
    functional requirements met?
    data model correct?
    concurrency + invariants handled?

  DOES IT SURVIVE
    any single point of failure?
    are retries safe?
    cache / DB / queue / provider fails?
    how does it recover?

  DOES IT GROW
    bottlenecks identified?
    scales horizontally?

  CAN WE RUN IT
    how is it monitored?
    what does it cost?

Order is load-bearing: a design that misses
the requirement does not need pass three.

What we're doing: Run passes 1 and 2 over a colleague's design and produce findings the author has to answer.

review-transcript.txttext
Design: order checkout, v3. Reviewed by
someone who did not write it.

PASS 1 — DOES IT DO THE JOB

Q: "Requirement 4 says a customer can
    cancel within 30 minutes. Which
    component enforces the 30 minutes?"
A: "The cancel endpoint checks created_at."
Q: "And if fulfilment already picked the
    item at minute 25?"
A: "...then we would have shipped a
    cancelled order."
FINDING: the invariant is time-based on one
side and event-based on the other. Needs a
state transition (picked blocks cancel), not
a timestamp comparison.

Q: "Two cancel requests arrive together."
A: "The second sees state=cancelled."
Q: "Does it refund twice?"
A: "...the refund call is outside the
    transaction."
FINDING: refund is not idempotent. Needs an
idempotency key derived from the order id.

PASS 2 — DOES IT SURVIVE

Q: "The payment provider is returning 503.
    What does a customer see?"
A: "Checkout fails."
Q: "For how long, and what retries?"
A: "The client retries."
FINDING: no circuit breaker, no cap. A
provider outage becomes a retry storm and
the failure lasts as long as the outage.
Options: queue the payment intent and
confirm asynchronously, or fail fast with a
clear message. Both are designs. Neither is
in the document.

Q: "Redis is down."
A: "Sessions are in Redis, so everyone is
    logged out."
Q: "Is that acceptable?"
A: "It is not great but it is survivable."
FINDING: acceptable, and now on the record
as a decision rather than a discovery.

Four findings. Three are changes. One is a
decision that was already true and had never
been written down.
12
The follow-up question is where the finding lives. "The endpoint checks created_at" is a true answer to the question as asked, and completely hides the race with fulfilment.
22
Asking "does it refund twice" rather than "is it idempotent" is deliberate: the second version can be answered yes by someone who has not traced the path.
34
Naming two acceptable designs rather than one keeps the finding a finding. The reviewer identifies the gap; the author still owns which way to close it.
46
Not every finding is a change. Converting an unstated assumption into a recorded decision is a real output of a review, and it is what stops the same question being re-asked next quarter.

Why this works: Every finding here came from a follow-up, not from the checklist question itself. The list gets the conversation to the right place; the second question — "and what happens then?" — is what turns a true-sounding answer into a discovered gap.

Reviewing the design you would have written

Wrong

text
Review comment:
  "I would have used an event log here
   instead of a job table. Please
   reconsider the approach."
# A different design, not a defect in this
# one. The author now defends a choice
# rather than answering a question.

Better

text
Review comment:
  "With a job table, how does a consumer
   that crashed mid-batch avoid reprocessing
   rows 40-100 on restart?"
# If the design has an answer, it is fine.
# If it does not, that is the actual finding
# and the author can fix it their way.

What you see: Reviews that take a long time, generate defensiveness, and end with the original design shipping unchanged — because the conversation was about preference and both people knew it.

Why: A review has authority over whether a design meets its requirements and survives its failure modes, not over which of several adequate shapes it takes. Substituting your design for the author's spends the review's credibility on a question it cannot settle, and the real defects go unexamined.

The review, in four passes

Pass 1 — Does it do the job?

Functional requirements traced to components, a data model whose access paths serve every query in the design, and named concurrency controls for every contended entity. Stop here if the answer is no — the later passes are about a system that does not exist yet.

Pass 2 — Does it survive?

Walk every component and kill one instance of it. Then kill the cache, the database, the queue and the external provider separately — four questions, four different right answers. Then ask how it comes back, with an RPO and an RTO that have numbers.

Pass 3 — Does it grow?

Name the first component to saturate and the load at which it does. "It scales horizontally" is a claim about the stateless tier; the interesting answer is about the part that is not stateless, and what its ceiling is.

Pass 4 — Can we run it?

One symptom-based alert per user-visible promise, with a named team it pages. Then the monthly cost at current load, and the cost per unit of whatever grows — the number almost no design computes until finance asks.

  1. Pass 1 — Does it do the job? — Functional requirements traced to components, a data model whose access paths serve every query in the design, and named concurrency controls for every contended entity. Stop here if the answer is no — the later passes are about a system that does not exist yet.
  2. Pass 2 — Does it survive? — Walk every component and kill one instance of it. Then kill the cache, the database, the queue and the external provider separately — four questions, four different right answers. Then ask how it comes back, with an RPO and an RTO that have numbers.
  3. Pass 3 — Does it grow? — Name the first component to saturate and the load at which it does. "It scales horizontally" is a claim about the stateless tier; the interesting answer is about the part that is not stateless, and what its ceiling is.
  4. Pass 4 — Can we run it? — One symptom-based alert per user-visible promise, with a named team it pages. Then the monthly cost at current load, and the cost per unit of whatever grows — the number almost no design computes until finance asks.

The eleven questions, by pass — and what separates an answer from a non-answer

The eleven questions, by pass — and what separates an answer from a non-answer
PassQuestionA non-answerAn answer
Does it do the jobDoes it meet the functional requirements?"Yes, it covers the use cases."Each requirement traced to the component and endpoint that satisfies it
Does it do the jobIs the data model correct?"It is normalised."Every query in the design served by a named access path; every invariant a constraint
Does it do the jobAre concurrency and invariants handled?"We use transactions."Named row locks or version checks per contended entity, and what the loser sees
Does it surviveAny single point of failure?"Everything is redundant."A walk of every component naming what happens when exactly one instance of it dies
Does it surviveAre retries safe?"The client retries."Every unsafe write has an idempotency key; the second call returns the first result
Does it surviveWhat happens when cache/DB/queue/provider fails?"We would fail over."Four separate answers — degraded read, read-only, backlog, queued-with-fallback
Does it surviveHow does it recover?"We restore from backup."RPO and RTO with a number, and the date the restore was last actually run
Does it growAre bottlenecks identified?"It should scale."The first component to saturate, at what load, and how that was calculated
Does it growCan it scale horizontally?"The app is stateless."What is stateless, what is not, and what the non-stateless part's ceiling is
Can we run itHow is it monitored?"We have dashboards."One symptom-based alert per user-visible promise, and who it pages
Can we run itWhat does it cost?"It runs on our existing cluster."Monthly cost at current load, and cost per unit of the thing that grows

The four dependency-failure questions have four different right answers

The four dependency-failure questions have four different right answers
Dependency failsWhat must not happenA good answer names
CacheA miss returning a wrong answer, or a flush losing dataReads fall through to the store; the store can carry the miss storm, or requests are shed
DatabaseSilent partial writes, or every endpoint failing togetherWhich endpoints go read-only, which fail, and what the user is shown
QueueUnbounded growth, or producers blocking the request pathBacklog limits, consumer lag alerting, and what is dropped or shed first
External providerThe whole product going down with itCircuit breaker, a degraded mode, and whether the work can be queued for later

Remember: Eleven questions in four passes, asked by someone who did not write the design. Does it do the job (requirements, data model, concurrency and invariants), does it survive (SPOF, safe retries, cache/DB/queue/provider failure, recovery), does it grow (bottlenecks, horizontal scaling), can we run it (monitoring, cost). Keep every item an open question, make the author answer, and follow each answer with "and what happens then?" — that second question is where the findings actually come from.

See also: the high level design checklist · the low level design checklist · the failure mode question checklist · eliminating single points of failure · rpo vs rto · symptom based alerting · unit cost estimation

Advertisement