Filter concepts by levelShowing all levels.

System Design · High-Level vs Low-Level Design

High-Level Design vs Low-Level Design

What each level contains, the test that tells you which one you are on, and how to move between them without producing a document that fails at both

  • High levelcomponents, owned stores, queues, boundaries, major flows
  • Low levelone component: modules, schema, state machines, algorithms
  • Move on purposeannounce the descent, then carry the findings back up

1.What belongs in a high-level design

A high-level design answers "what are the moving parts, and how does data get between them". Its vocabulary is components and services, the data stores each one owns, the queues and streams that connect them asynchronously, the network and trust boundaries they sit inside, and the major flows — the two or three paths that carry most of the system's traffic and value. The boundaries deserve particular attention, because a boundary is where the properties change: a call that crosses a process boundary can fail in ways an in-process call cannot, a call that crosses a trust boundary needs authentication and authorization, and a call that crosses a network boundary needs a timeout. Marking them is what makes the diagram say something rather than merely arrange things. What does not belong is anything about the inside of a box: class names, method signatures, schema columns, algorithms. Not because those are unimportant, but because a reader at this level is asking a different question, and mixing the two produces a diagram that is too detailed to see the shape and too incomplete to implement from. A good test is whether the design still reads correctly if one component's internals were replaced entirely — if it does, the level is right.

Key features

  • The vocabulary is components and services, the data stores each owns, the queues and streams between them, boundaries, and the major flows
  • Two or three major flows are enough — a high-level design that traces every path has stopped being high-level
  • Mark network, process and trust boundaries explicitly, because that is where failure modes and security requirements change
  • Distinguish synchronous calls from asynchronous ones, because that difference determines the system's failure behaviour
  • Class names, method signatures, schema columns and algorithms belong one level down
  • Test: if a component's internals were replaced entirely, the high-level design should still be correct

In a high-level design, and not

In a high-level design, and not
BelongsDoes not belong
Services and components, with responsibilitiesClass and module names
Data stores, and which component owns eachTable columns, indexes, constraints
Queues and streams, and what flows through themMessage field-level schemas
Network, process and trust boundariesFunction signatures
Two or three major data flowsEvery path through the system
Synchronous versus asynchronous edgesRetry counts and backoff constants
The level a high-level design operates at
syncasync

Client

Edge / CDN

trust boundary crossed here

API gateway

Orders service

owns the orders database

Orders database

Event stream

asynchronous edge

Search indexer

owns the search index

  • Client
    • leads to Edge / CDN
  • Edge / CDN — trust boundary crossed here
    • leads to API gateway
  • API gateway
    • leads to Orders service (sync)
  • Orders service — owns the orders database
    • leads to Orders database
    • leads to Event stream (async)
  • Orders database
  • Event stream — asynchronous edge
    • leads to Search indexer
  • Search indexer — owns the search index

Command

text
A high-level design, written out

  [browser] --https--> (edge/CDN)
                          |
                    == trust boundary ==
                          v
                     (api gateway) --sync--> (orders svc)
                                                 |  owns
                                                 v
                                             [orders db]
                                                 |
                          (orders svc) --async--> {events}
                                                    |
                                             (search indexer)
                                                    |  owns
                                                    v
                                              [search index]

  Major flows: 1) place an order  2) search orders

A high-level design names the components and services, the stores each one owns, the queues between them, the network and trust boundaries, and the two or three major flows — and marks which edges are synchronous. Boundaries matter most, because they are where failure modes and security requirements change. Nothing about the inside of a box belongs here: if a component's internals could be replaced entirely and the design still read correctly, the level is right.

2.What belongs in a low-level design

A low-level design answers "how is one component actually built". Its vocabulary is modules and their boundaries, the classes and interfaces inside them, the database schema with its columns, keys, indexes and constraints, the state machines that govern how an entity changes, the algorithms chosen for the parts where the choice matters, and the internal APIs one module offers another. Its scope is deliberately one component — a low-level design that spans three services has drifted into being a poor high-level design. Two elements carry more weight than the rest. The schema, because it outlives the code around it and because constraints written there are enforced against every future writer, which is the difference between a rule and a hope. And the state machine, because most real-world defects are states nobody enumerated: a listed set of states and permitted transitions turns "what happens if the payment succeeds after the hold expired" from an open question into either a transition on the diagram or a gap you can see. The test for the right level here is implementability — a reader who knows the language and the codebase should be able to write the component from it without asking a design question, and if they cannot, the missing piece belongs in the document.

Key features

  • The vocabulary is modules, classes and interfaces, database schema, state machines, algorithms, and the internal APIs between modules
  • Scope is one component — a low-level design spanning several services has become a poor high-level design instead
  • The schema carries the most weight because it outlives the code and its constraints bind every future writer
  • A state machine with enumerated states and permitted transitions turns unhandled cases into visible gaps
  • Name an algorithm only where the choice matters — where it changes complexity, memory, or observable behaviour
  • Test: someone who knows the codebase should be able to implement from it without asking a design question

In a low-level design, and not

In a low-level design, and not
BelongsDoes not belong
Module boundaries and responsibilitiesWhich services exist in the system
Classes, interfaces, and their contractsNetwork topology
Schema: columns, keys, indexes, constraintsWhich component owns which store
State machines and permitted transitionsCross-service flows
Algorithms where the choice mattersDeployment topology
Internal API signatures and error shapesPublic API versioning strategy
A state machine is the highest-value part of a low-level design
paymentstartedexpiry orcancellationpayment succeeded,hold still validpayment failed, orhold expired first

held

start

converting (payment in flight)

booked

end

released

end

  • held (start)
    • → converting (payment in flight) when payment started
    • → released when expiry or cancellation
  • converting (payment in flight)
    • → booked when payment succeeded, hold still valid
    • → released when payment failed, or hold expired first
  • booked (end)
  • released (end)

Command

sql
-- schema: the part that outlives the code
CREATE TABLE holds (
  seat_id    uuid PRIMARY KEY,
  buyer_id   uuid        NOT NULL REFERENCES buyers,
  state      text        NOT NULL,
  expires_at timestamptz NOT NULL,
  CHECK (state IN ('held', 'converting', 'released'))
);
CREATE INDEX holds_expiry ON holds (expires_at)
  WHERE state = 'held';   -- partial: only live rows

-- states: held -> converting -> (booked | released)
--         held -> released (expiry or cancel)

A low-level design covers one component: its modules, classes and interfaces, its schema with keys, indexes and constraints, its state machines, the algorithms where the choice matters, and the internal APIs between modules. The schema and the state machine carry the most weight — one outlives the code and binds every future writer, the other turns unenumerated cases into visible gaps. The test is implementability: a reader who knows the codebase should not have to ask a design question.

3.Moving between levels without mixing them

The skill is not knowing what belongs at each level — it is descending deliberately and announcing the move. A design conversation naturally slides downward, because concrete details are easier to discuss than abstract ones and everyone has an opinion about a column name. The result is a mixed document: three services sketched at cartoon depth alongside one table's exact indexes, which is too vague to evaluate as an architecture and too incomplete to build from. The discipline has three parts. Descend on purpose: finish the level you are on, say "let me go a level down into the fetch path", and come back up when that path is done. Keep one level per artefact, so a high-level diagram never grows a schema and a component design never grows a network topology — if a detail is needed for the higher level to make sense, it is usually a boundary or a data-ownership fact, both of which are legitimately high-level. And carry consistency between levels: every component named at the high level should have a low-level design or an explicit note that it does not need one, and every store or queue in a low-level design should appear on the high-level diagram. That last check is the one that catches real problems, because a store appearing at the low level and not the high level usually means a component quietly took a dependency nobody reviewed.

Key features

  • Design conversations slide downward on their own, because concrete details are easier to discuss and easier to have opinions about
  • A mixed-level document is too vague to evaluate as an architecture and too incomplete to implement from — it fails at both jobs, not one
  • Descend deliberately: finish the current level, announce the move, work the detail, then come back up
  • One level per artefact — a high-level diagram that grows a schema has stopped being one
  • Consistency check downward: every high-level component has a low-level design, or a note saying it does not need one
  • Consistency check upward: every store or queue in a low-level design appears on the high-level diagram — a missing one is usually an unreviewed dependency

Three kinds of level mixing, and what each hides

Three kinds of level mixing, and what each hides
MixingLooks likeWhat it hides
Detail in a high-level designA diagram with one component's table columns on itThat the other components were never designed to the same depth
Architecture in a low-level designA component design that also decides service boundariesThat a boundary decision was made without an architecture review
Uneven depth across componentsThree services sketched, one specifiedThat the unspecified ones carry unexamined risk

The two consistency checks

The two consistency checks
DirectionCheckWhat a failure usually means
High → lowEvery component has a low-level design, or a note saying why notA component nobody has thought through yet
Low → highEvery store, queue and external call appears on the high-level diagramA dependency was taken without architectural review
Three levels, and what changes between them

System context

the system, its users and the external systems it talks to

High-level design

components, owned stores, queues, boundaries, major flows

Low-level design

one component: modules, classes, schema, state machines, algorithms

Code

where the low-level design is realised, and where it stops being a document

  1. System context — the system, its users and the external systems it talks to
  2. High-level design — components, owned stores, queues, boundaries, major flows
  3. Low-level design — one component: modules, classes, schema, state machines, algorithms
  4. Code — where the low-level design is realised, and where it stops being a document

Command

text
Announcing the move, in a review or an interview

  "That is the high-level shape: gateway, orders
   service, event stream, search indexer. The
   risky part is the indexer, so let me go a
   level down into it."

   ... module boundaries, schema, state machine,
       backfill algorithm ...

  "Coming back up: that changes the high-level
   picture in one way -- the indexer needs its own
   store for checkpoints, so it belongs on the
   diagram."

Descend on purpose and say you are doing it: finish the level, name the component you are going into, work the detail, then come back up. One level per artefact — a high-level diagram that has grown a schema is no longer one. Then run both consistency checks: every high-level component has a low-level design or a note saying why not, and every store, queue and external call in a low-level design appears on the high-level diagram, because a missing one is usually a dependency nobody reviewed.

Remember

  • High level: if a component's internals could be replaced entirely and the design still read correctly, the level is right.
  • Low level: if a reader who knows the codebase has to ask a design question, the document is not finished.
  • A mixed-level document is too vague to review as architecture and too incomplete to build from — it fails at both jobs.
  • Every store, queue or external call in a low-level design belongs on the high-level diagram; a missing one is an unreviewed dependency.

Pro tip

  • Level every component before descending into one, so uneven depth is a stated choice rather than a record of what was interesting to write.
  • Answer a question at the level it was asked; the detail is still available a level down, once the shape is agreed.

Next up

  • High-Level Design Checklist
  • Fifteen items, grouped by the question each answers
  • Using it as a gap-finder rather than a script

Sources

  • The C4 model for visualising software architecture
  • Checked 2026-08-28

Spotted a mistake on this page? Tell us.