Filter concepts by levelShowing all levels.

System Design · Section 109

Final Senior-Level Standard

Level
advanced
Read
24 min
Concepts
3

Three questions, three scopes — and they are levels of scope rather than levels of worth. The junior question, "how do I implement this feature", is correct and narrow: it takes the requirement, the architecture and the technology as given. Its failure mode is not asking it but asking only it, on a problem where those things were never decided, because then the implementation decides them invisibly — a status field becomes the state machine, a timeout value becomes the failure boundary, a retry loop becomes the duplication policy. The signal is having to invent something unspecified, and the whole upgrade is naming it out loud instead of picking one silently. The intermediate question, "what architecture and technology should I use", is a real step up and the most dangerous of the three, because its answers are the most plausible: "microservices with Kafka and Postgres" sounds like a design, can be defended at length, and needs no reference to any requirement. The tell is direction — architecture-first bends the requirements to fit through a series of small reasonable accommodations, while requirements-first produces the same words in the opposite order. The senior question is seven inputs and one output: requirements, invariants, scale assumptions, failure modes, consistency needs, operational cost and long-term trade-offs, and then the simplest design that satisfies them. That last clause is the load-bearing part, because every one of the seven inputs argues for adding a mechanism and each addition is individually defensible — which is why over-engineering is the senior failure mode rather than the junior one. The filter is a number: what would make this necessary, and are we at it. The senior question does not replace the other two. It contains them: it ends in an architecture, which ends in an implementation.

System Design overview

What is true here

  1. Three scopes, not three levels of worth — each question is correct at its level and incomplete at the next.
  2. Decisions that are never named get made by the implementation, because a schema shape does not look like a decision.
  3. The technology question is the most dangerous: plausible answers that were never derived from a requirement.
  4. Seven inputs, one output — and the output clause "simplest that satisfies them" is what stops the analysis becoming a design.
  5. Invariants are the only absolute part of a design and are almost never written down.

What you will be able to do

  • Notice a decision escaping into code and name it before choosing one
  • Tell an architecture-first design from a requirements-first one by the direction of the reasoning
  • Run the seven inputs over a feature and then reject each suggested mechanism with a number
  • Write a system's invariants as sentences that must never be false, and say where each is enforced

The narrower questions

Both are correct within their scope; both cause damage when they are the only question asked.

"How do I implement this feature?"

standardbeginner

The implementation question, and it is a good question — the code has to work, and someone has to write it. What defines it is scope: it takes the requirement, the architecture and the technology as given, and asks how to build within them. That is exactly right when those things really have been decided, and it is where most of the actual value in software gets produced. The failure mode is not asking it; it is asking only it, on a problem whose decisions have not been made. Then the implementation quietly makes them: a schema shape becomes the data model, a synchronous call becomes the coupling, a retry loop becomes the failure policy, and none of it was reviewed because none of it looked like a decision. The way to keep the question at its correct scope is to notice when an answer requires inventing something that was not specified — a status field nobody described, a timeout value nobody chose, a rule about what happens when the third-party call fails. Each of those is a decision escaping into code, and the useful move is to name it out loud rather than to pick one silently.

Think of it as

Building the room from a plan. It is skilled work with real judgement in it, and it assumes somebody has decided where the room goes and what it is for. When the plan is silent, the builder still has to do something — and that something becomes the design, whether or not anyone meant it to.

text
The upgrade is one sentence, not a promotion

  Instead of:
    "I'll add a status field with pending,
     done and error."

  Say:
    "The ticket does not say what happens
     when the provider call fails. I am going
     to need a state for it -- can we decide
     whether that is retryable or terminal
     before I pick one?"

  Same work. The difference is that a
  decision that was going to be made anyway
  is now made by someone who can see the
  whole workflow.
The question at its correct scope, and past it

In scope — a good question

  • +The requirement is written and testable
  • +The architecture and the store are chosen
  • +The failure behaviour is specified
  • +What is left is genuinely how to build it well

Past scope — decisions escaping into code

  • A status value nobody described
  • A timeout nobody chose
  • A retry around a call nobody said was safe to repeat
  • A query inside a loop nobody sized
  • In scope — a good question
    • The requirement is written and testable
    • The architecture and the store are chosen
    • The failure behaviour is specified
    • What is left is genuinely how to build it well
  • Past scope — decisions escaping into code
    • A status value nobody described
    • A timeout nobody chose
    • A retry around a call nobody said was safe to repeat
    • A query inside a loop nobody sized

What the implementation question covers, and what it assumes

What the implementation question covers, and what it assumes
Covered by the questionAssumed already decided
How the code is structuredWhether this is one service or two
Which library or framework call to useWhether the operation is synchronous or queued
How the tests are writtenWhat the system does when the dependency is down
How the data is read and writtenWhat the schema is, and which invariants it enforces

Decisions that escape into code when nobody names them

Decisions that escape into code when nobody names them
What the implementer inventsThe decision it silently makes
A status column with three valuesThe workflow's state machine, including which transitions are legal
A five-second timeout, because it felt rightThe system's failure boundary and its worst-case request latency
A retry loop around a provider callThe duplication policy, if the call is not idempotent
Reading a related row inside a loopThe query count per request, and the point where the endpoint stops scaling

Remember: The implementation question is correct and narrow: it takes the requirement, the architecture and the technology as given. The failure is asking only it on a problem where those were never decided — then the implementation decides them, invisibly, because a schema shape and a timeout value do not look like design decisions. The signal is having to invent something unspecified, and the whole upgrade is naming it out loud rather than picking one silently.

See also: the intermediate question · the senior question · the low level design checklist · what belongs in a low level design

"What architecture and technology should I use?"

standardintermediate

A real step up: it operates on shape rather than on code, and asks a question the implementation question cannot. It is also the most dangerous of the three, because its answers are the most plausible. "Microservices with Kafka and Postgres" sounds like a design, can be drawn on a whiteboard, and can be defended at length — entirely without reference to the requirement it was supposed to satisfy. That is what makes it hard to challenge: nothing in the answer is wrong, and nothing in it is derived. The tell is direction. A design that starts from architecture and technology has to bend the requirements to fit the choice, and the bending is invisible because it happens as a series of small reasonable-sounding accommodations. A design that starts from requirements produces the same words in the opposite order — the load and consistency needs and failure tolerance come first, and the technology falls out as the thing that satisfies them. So the upgrade is not to know more technologies. It is to answer this question last instead of first, and to be able to say which specific requirement each choice serves.

Think of it as

Choosing materials before knowing what is being built. Steel and glass is a defensible answer to almost any construction question, and it is only a good answer once someone has said whether this is a bridge, a greenhouse or a house. The materials conversation is real work — it is just not the first work.

text
Same question, answered last instead of first

  ASKED FIRST
    "What should we use?"
    -> Kafka, Kubernetes, Postgres
    -> requirements now get shaped to fit

  ASKED LAST
    "3,000 writes/sec, two independent
     consumers, one needs 7-day replay,
     ordering per customer."
    -> that is a partitioned log
    -> which is Kafka
    Same answer. Now it has a reason, and a
    condition under which it would change.
Where each question operates

Requirements and constraints

What must be true: load, consistency, failure tolerance, cost, compliance. The senior question starts here.

Architecture and technology

Shape and components. The intermediate question starts here — which is why it has to bend the layer above to fit.

Implementation

Code, schema, tests. The junior question starts here, and inherits whatever the two layers above left unstated.

  1. Requirements and constraints — What must be true: load, consistency, failure tolerance, cost, compliance. The senior question starts here.
  2. Architecture and technology — Shape and components. The intermediate question starts here — which is why it has to bend the layer above to fit.
  3. Implementation — Code, schema, tests. The junior question starts here, and inherits whatever the two layers above left unstated.

The same words, in two directions

The same words, in two directions
Architecture firstRequirements first
"We will use Kafka.""Two teams need the same event stream independently, and one needs to replay a week — that is a log, not a task queue."
"Microservices.""Billing needs a different deploy cadence and on-call rotation than the rest, so it splits. Nothing else does yet."
"DynamoDB, for scale.""Access is one key, we know every pattern, and the write rate exceeds one primary. Ad-hoc queries would have kept us on Postgres."
"Multi-region active-active.""RPO is 5 minutes and RTO is 30, so a warm standby meets it. Active-active is not required, and it would add conflict resolution."

Three questions that expose an undirected answer

Three questions that expose an undirected answer
AskA derived answer sounds likeAn undirected answer sounds like
Which requirement does this serve?A named non-functional target with a number"It is the standard choice"
What would change your mind?A specific condition — a rate, a pattern, a team boundary"Nothing really, it works for everything"
What is the simpler option you rejected?One named alternative and why it loses hereSilence, or a general criticism of the simpler thing

Remember: A real step up in scope, and the most dangerous of the three, because a technology answer is plausible without being derived — "microservices with Kafka" can be defended at length with no reference to any requirement. The tell is direction: architecture-first bends the requirements to fit the choice, in small accommodations nobody notices, while requirements-first produces the same words in the opposite order. The upgrade is not knowing more technologies; it is answering this question last, and being able to name the specific requirement each choice serves and the condition that would change it.

See also: the junior question · the senior question · start with requirements not technologies · the twelve anti patterns · the recurring trade offs

Advertisement

The senior question

Seven inputs, one output, and the filter that keeps a thorough analysis from producing an elaborate design.

Seven inputs, and the simplest design that satisfies them

coreadvanced

Seven inputs and one output. The inputs: requirements (what it must do), invariants (what must never be false), scale assumptions (the numbers, stated so they can be wrong), failure modes (what happens when each part breaks), consistency needs (per workflow, not globally), operational cost (money and human attention), and long-term trade-offs (what this makes harder later). The output is the last clause, and it is the load-bearing part: the simplest design that satisfies them. Without it, the seven inputs produce a thorough analysis and an elaborate design — which is the characteristic failure of senior engineers, not junior ones, because every input suggests a mechanism and adopting all of them is defensible at every step. Two of the seven deserve special attention. Invariants, because they are the things a system may never violate and they are almost never written down; a design that has not named them cannot be checked against them, and they are what actually decides where transactions and constraints belong. And scale assumptions stated as numbers rather than adjectives, because "high traffic" cannot be wrong and "2,000 requests per second at peak" can — and being wrong is what makes an assumption useful. This question does not replace the other two. It contains them: it ends in an architecture, which ends in an implementation.

Think of it as

A brief and a budget, not a wish list. Seven things that must be true, and then the cheapest structure that makes them all true. The discipline is in the word "simplest" — every one of the seven inputs will suggest adding something, and the answer is only finished when you have taken back out everything that was not required.

text
Seven in, one out

  IN
    requirements        what must it do
    invariants          what must never be false
    scale assumptions   the numbers, disprovable
    failure modes       per dependency
    consistency needs   per workflow
    operational cost    money and attention
    long-term trade-offs  the one-way doors

  OUT
    the SIMPLEST design that satisfies them

The last line is the hard part. Every input
above it argues for adding something.

What we're doing: Run the seven inputs over one feature, then take back out everything they suggested that was not required.

seven-inputs.txttext
THE FEATURE
  "Let customers export their data."

REQUIREMENTS
  A customer downloads everything we hold
  about them, as a file. Legal deadline: 30
  days. Realistic expectation: minutes.

INVARIANTS
  - An export never contains another
    customer's data.
  - An export link is usable only by the
    customer who requested it.
  - A deleted account's export becomes
    unavailable.
  These are the three sentences that must
  never be false. Nothing else on this list
  is absolute.

SCALE ASSUMPTIONS
  ~40 exports/day today. Largest customer:
  ~2 GB, ~14 million rows. Stated so it can
  be wrong -- if it is 400/day the design
  changes; if the largest is 200 GB it
  changes more.

FAILURE MODES
  - Object storage down: exports queue, no
    data lost.
  - Export worker dies mid-run: the job is
    leased, another worker retakes it.
  - Database under load: exports are the
    thing to shed, not checkout.

CONSISTENCY NEEDS
  A point-in-time snapshot is fine. Nobody
  needs an export to include a row written
  during the export. Saying this removes a
  whole class of complexity.

OPERATIONAL COST
  Storage for retained exports; a worker
  pool sized for the largest customer. The
  3am question: does a failed export page
  anybody? No -- it retries and alerts in
  business hours.

LONG-TERM TRADE-OFFS
  The export format is a one-way door. Once
  customers automate against it, changing a
  column is a breaking change. Version it
  from day one; that is the cheap decision
  now and an expensive one later.

WHAT EACH INPUT SUGGESTED

  scale       -> a distributed export system
  failure     -> circuit breakers everywhere
  consistency -> snapshot isolation
  cost        -> a lifecycle-managed bucket
  trade-offs  -> a plugin format registry

THE SIMPLEST DESIGN THAT SATISFIES THEM

  A jobs table. One worker pool, leases,
  streaming writes to object storage, a
  signed URL with a short TTL, a version
  field in the file, and a lifecycle rule
  that deletes exports after 7 days.

  Roughly 300 lines. It satisfies all seven
  inputs, including both invariants -- the
  query is tenant-scoped and the URL is
  scoped to one key.

  It does not satisfy 400 exports/day. That
  is written down, next to the number that
  would trigger the redesign.
8
Writing invariants as sentences that must never be false is what makes them checkable. Two of these three end up enforced by a scoped query and a signed URL, which is where invariants usually land.
21
The assumptions are stated in a form that can be disproved, and the consequence of each being wrong is named. That is the difference between an assumption and a guess.
36
Explicitly deciding that a snapshot is acceptable removes more complexity than any other line here. Consistency needs are usually looser than an unexamined design assumes.
65
This is the whole discipline: five reasonable mechanisms suggested, none adopted, and the reason each was not needed is traceable to a number or a stated tolerance.

Why this works: The seven inputs are a generator of requirements and the last clause is the filter. Running the inputs without the filter produces a design that satisfies everything including things nobody asked for — which is why "simplest" is in the sentence, and why the record of what was rejected, and at what number it would be reconsidered, is part of the answer.

Answering the seven inputs and then building for all of them

Wrong

text
# Analysis: thorough. Seven inputs, all
# answered, all correct.
# Design: sharded store, circuit breakers on
# every call, an event bus, multi-region
# replication, a plugin format registry.
# Each piece traces to a real input. The
# whole thing serves 40 exports a day.

Better

text
# Same analysis. Then, for each mechanism:
#   "what number makes this necessary?"
#   "are we at that number?"
# Sharding at 400/day - we are at 40. Out.
# Circuit breaker when an outage costs
# something - it does not yet. Out.
# Write both numbers down. Ship 300 lines.

What you see: A design review where every component can be justified individually and nobody can explain why the whole system is this large — and an operational burden that grows while the traffic does not.

Why: A thorough analysis raises every possible concern, and each mechanism that addresses one is individually defensible, so nothing gets rejected on the merits of a single argument. Only the filter — what number makes this necessary, and are we at it — rejects things, and skipping it turns a good analysis into the most reliable source of over-engineering there is.

Three questions, three scopes — each contains the next

The senior question does not replace the other two; it ends in an architecture, which ends in an implementation.

  • Three nested rectangles, largest to smallest.
  • The outer rectangle is the senior question: what are the requirements, invariants, scale assumptions, failure modes, consistency needs, operational costs and long-term trade-offs?
  • The middle rectangle is the intermediate question: what architecture and technology should I use?
  • The inner rectangle is the junior question: how do I implement this feature? — and inside it, the code.
  • Below the diagram: each question is correct at its own level and incomplete at the next, and the senior question ends with "what is the simplest design that satisfies them?"

The seven inputs, and what each one is actually asking

The seven inputs, and what each one is actually asking
InputThe questionWhat a good answer contains
RequirementsWhat must it do?Functional behaviour plus non-functional targets with numbers
InvariantsWhat must never be false?Sentences of the form "a seat is never held by two people" — checkable, and usually enforceable in a schema
Scale assumptionsHow much of it is there?Peak RPS, data per year, maximum fan-out — stated so they can be disproved
Failure modesWhat happens when each part breaks?One answer per dependency, plus what the user sees in each case
Consistency needsWho must read their own write?A decision per workflow, not one setting for the whole system
Operational costWhat does running it cost?Money at current load, and how much human attention it needs at 3am
Long-term trade-offsWhat does this make harder later?The one-way doors: a shard key, a public API shape, a schema everyone depends on

The same design, before and after "simplest"

The same design, before and after "simplest"
Input suggestedThe elaborate answerThe simplest answer that still satisfies it
Scale: 2,000 rps peakShard the database nowOne primary with headroom; write down the rate at which sharding starts
Failure: provider outageCircuit breaker, bulkhead, fallback provider, retry queueTimeout and a degraded mode; add the breaker when an outage actually costs something
Consistency: stale reads reportedMove to strong consistency everywhereRoute the two workflows that need it to the primary; leave the rest on replicas
Trade-off: might need multi-regionBuild active-active nowKeep state region-portable and revisit when a requirement names a second region

Remember: Seven inputs — requirements, invariants, scale assumptions, failure modes, consistency needs, operational cost, long-term trade-offs — and one output: the simplest design that satisfies them. The last clause is what makes the question work, because every input argues for adding something and each addition is individually defensible; the filter is "what number makes this necessary, and are we at it". Write the invariants down as sentences that must never be false, state scale assumptions as numbers so they can be wrong, and record what you rejected and at what point it would be reconsidered.

See also: the junior question · the intermediate question · identifying invariants · stating a trade off · the review checklist · estimation categories · right sizing not maximum scale

Advertisement