Drawing architecture diagrams that read well
Open in the interactive app: practice, quiz, mock interview →In one minute
- The diagram is your shared memory with the interviewer. A clean one lets them follow your reasoning and ask sharper questions; a messy one hides good ideas and costs you minutes of re-explaining.
- Draw left to right in the direction a request travels: clients, edge, services, async pipeline, storage. Start with a skeleton of five to seven boxes and add detail only where you go deep.
- Every arrow should mean one thing: a call with a direction. Mark which arrows are synchronous and which are asynchronous, and label the important ones with what flows (protocol, payload, rate).
- Narrate while drawing, using numbered flows: "1 to 4 is the write path, 5 to 7 the read path". Then walk a single request end to end.
- Staff-level signal: the diagram shows failure domains, replicas and data ownership, so you can point at it and say what happens when any box dies.
How it works
Time budget
In a 45-minute interview, the high-level diagram should exist by around minute 15 and be mostly complete by minute 20. Spend the first pass on structure, not polish. Boxes are cheap to add later; a layout that runs out of room is expensive to fix, so leave space on the right and bottom.
The skeleton first
Draw the minimum that satisfies the functional requirements: client, an entry point (load balancer or API gateway), one or two services, the primary datastore, and a cache or queue if the requirements obviously need one. Walk one write and one read through it before adding anything. This forces gaps to the surface early, such as "where does the feed get built?", while changes are still cheap.
Conventions that make diagrams readable
- Direction. Requests flow left to right, data stores sit on the right or bottom, async pipelines below the synchronous path.
- Shapes. Rectangles for services, cylinders (or a labelled box) for databases, a distinct shape for queues and topics, a cloud or edge box for CDN. Keep the same shape for the same kind of thing.
- Arrows. Solid for synchronous request and response, dashed for asynchronous messages and events. One arrow per interaction; avoid double-headed arrows, which hide who initiates. The C4 notation guide makes the same rule: every line is unidirectional and labelled with its intent, never just "uses" (C4 notation).
- Labels. Name boxes by responsibility ("Feed service"), not by technology alone. Add the technology in small text when you have chosen it ("Postgres, sharded by user_id"), and the protocol on arrows between processes ("gRPC", "Kafka") (C4 notation).
- Numbers. Write key rates on arrows or boxes ("60k QPS reads", "2 TB/day") and replica counts on stateful components.
- Numbered flows. Number the steps of the main write and read paths so you can refer to them.
Narration
Talk as you draw, but in short sentences: "The client uploads to object storage through a pre-signed URL, then calls the post service with the object key." After the skeleton, do one complete pass of each path. When you go deep, zoom: draw a second, smaller diagram for the deep dive (for example a sequence diagram for a payment flow) rather than cramming detail into the overview.
What interviewers look for
- A diagram that matches the requirements and the API you defined.
- Clear separation of the read path and the write path, and of synchronous and asynchronous work.
- Stateful components clearly marked, with how they scale and replicate.
- The ability to point at any box and answer "what if this fails or gets 10 times the load?"
Common mistakes
- Drawing every component you know (service mesh, three caches, two queues) before establishing the core flow.
- Unlabelled arrows that leave the interviewer guessing what flows and who calls whom.
- A single box called "Database" with no indication of partitioning or replication at the scale estimated earlier.
- Cycles of synchronous calls between services, which become latency chains and deadlock risks.
- Erasing and redrawing large parts instead of evolving the diagram; keep the old version if you change direction, and say why.
Down the stack
Layer 1The contract
A system diagram makes testable claims. Each box is a deployable unit with an owner and a failure domain. Each arrow is a dependency: if the target is down, the source is affected, synchronously (fails or slows now) or asynchronously (backlog grows). A stateful box claims a data ownership boundary: no other box writes its data directly. From those claims you can answer the questions interviewers ask: which component is a single point of failure, which calls are on the latency-critical path, and what a failure cascades into.
Layer 2The mechanism
Treat the diagram as a directed graph. The request path is every node reachable from the entry point. A single point of failure is a node with no redundancy whose removal disconnects the entry from some required data store, which is a cut vertex (articulation point) in graph terms, findable in linear time with a depth-first search (Wikipedia). A cycle of synchronous edges is a call loop: retries within a loop multiply load, and timeouts nested inside each other can exceed the caller's deadline. Retries multiply along any chain, not just loops: if three layers each retry three times, one user action can become 4 × 4 × 4 = 64 attempts on the database, which is why each arrow should carry a propagated deadline rather than its own timeout (SRE book, ch. 22). Fan-out edges multiply requests: one arrow into a service that calls 20 shards means 20 backend calls per request, and tail latency is set by the slowest; if each shard is slow 1 percent of the time, about 18 percent of requests wait on at least one slow shard. For deep dives, sequence diagrams show ordering and failure points that a box diagram cannot, such as "the response is lost after the charge succeeded". Edges also differ in strength: a strong dependency takes the flow down when it fails, while a weak one only removes a feature, so mark which is which (Azure Well-Architected). The C4 model formalises the zoom levels: context, containers, components and code, plus supplementary dynamic and deployment diagrams (C4 model). Interviews mostly live at the container level, and the AZ boundaries you draw are a small deployment diagram.
Layer 3The machine
Each arrow costs something physical, and annotating it shows depth. A synchronous call inside one AZ is about 0.2 to 0.5 ms of network round trip plus serialisation; a cross-AZ call adds about 1 ms, since AWS keeps a region's AZs within 100 km of each other (AWS), and is billed per GB in most clouds; a cross-region call adds tens to a hundred milliseconds. Behind each arrow are TCP connections held in pools: 200 app instances times a pool of 20 connections is 4,000 connections into one database primary. Postgres runs one backend process per connection (Postgres docs), so that many connections exhausts memory without a pooler such as PgBouncer, which needs about 2 kB per client connection and in transaction mode lends a server connection only for the length of a transaction (PgBouncer). A load balancer box is either L4 (forwarding TCP connections, very cheap per packet) or L7 (terminating TLS and parsing HTTP, costing CPU per request). A queue box is a disk-backed log: its throughput is bounded by sequential disk writes and replication, and its buffer gives you hours of absorption only if you size the disk for it.
Why it's built this way
Interviewers can hold only a few things in working memory while listening, so a consistent left-to-right layout with few boxes reduces their cognitive load. Failures propagate along dependencies, so arrows must show direction and synchrony. Detail is needed only where the risk is, so you zoom into a separate diagram for deep dives. And because the diagram is the evidence for your claims about scale and failure, the numbers and replica counts belong on it.
Diagram
flowchart LR
C["Client"] -->|"1 POST /posts"| LB["Load balancer x2"]
LB --> API["Post service x6"]
API -->|"2 insert"| DB["Posts DB, sharded by user_id"]
API -.->|"3 PostCreated"| Q["Kafka topic"]
Q -.->|"4 fan-out"| FW["Feed workers"]
FW --> FC["Feed cache (Redis)"]
C -->|"5 GET /feed"| LB
LB --> FS["Feed service x8"]
FS -->|"6 read"| FCWorked example
You are asked to design a URL shortener and have 10 minutes for the high-level design.
Minute 0 to 2: draw client, load balancer, "Shortener service", "URL store" and a cache, left to right. Label the write arrow POST /urls and the read arrow GET /{code} -> 301.
Minute 2 to 4: number the flows. Write path: 1 client to service, 2 service gets an ID from an ID allocator (draw a small box), 3 service writes code -> long_url to the store. Read path: 4 client to service, 5 cache lookup, 6 on miss read store and populate cache. Say each step aloud once.
Minute 4 to 6: add numbers from your estimate. "100 million new URLs a month is about 40 writes per second; reads at 100 to 1 are about 4,000 per second average, 20,000 at peak." Write them on the arrows. Mark the store "KV, partitioned by code, RF=3" and the cache "Redis, about 20 GB hot set".
Minute 6 to 8: mark failure domains. Draw a dashed boundary for each of two AZs and show two service instances and store replicas in each. Point at the ID allocator: "This is a single point of failure, so it hands out ID ranges of 1,000 to each service instance and runs as a three-node group."
Minute 8 to 10: add the async analytics path under the main flow: dashed arrow from service to a click-event topic, then to an aggregation job and an analytics store. You now have a diagram that answers most follow-ups by pointing.
In the wild
- Roblox, 2021: one box that everything, including monitoring, depended on. Every backend service shared a single Consul cluster for service discovery, and the telemetry systems depended on Consul too. Contention in a newly enabled Consul streaming feature, plus a bloated BoltDB freelist that slowed Raft writes, took the platform down for 73 hours for about 50 million daily players, while engineers lacked the dashboards that ran through the same cluster. Fixes included dedicated Consul clusters for critical services and removed the circular dependency so telemetry no longer relies on Consul (Roblox).
- Slack, 2021: the arrow nobody drew. On the first workday of 2021, traffic grew faster than AWS Transit Gateways could scale. Packet loss failed health checks, autoscaling removed instances and tried to add about 1,200 more, most never fully provisioned, and the dashboards failed too because they reached their databases across the same gateways. Slack planned to run the dashboards in the same VPC as their databases and to request gateway pre-scaling ahead of busy periods (Slack Engineering).
Trade-offs
| Option | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| One overview diagram | Fast, keeps context | Gets crowded | First 20 minutes |
| Overview plus zoomed deep dive | Detail where it matters | Takes space and time | Deep dives on one flow |
| Sequence diagram | Shows ordering and failure timing | Poor at structure | Payments, consensus, retries |
| Box-and-arrow with numbers | Shows scale and bottlenecks | Needs estimates first | Capacity discussion |
| Drawing tool templates | Neat, reusable shapes | Fiddly under time pressure | Remote interviews with practice |
Pitfalls in production
These apply equally to design reviews at work:
- Diagrams that drift from reality. Architecture diagrams go stale within months. Generate them from service catalogs or keep them next to the code with an owner.
- Missing async edges. Event consumers are often left off, so nobody sees that a "harmless" schema change breaks five downstream jobs. Draw the topics and their consumers.
- Hidden shared dependencies. Two "independent" services both call the same auth service or database, a shared failure domain. Draw shared dependencies once and connect all callers to it.
- No legend. Readers interpret dashed lines and colours differently. Add a three-line legend, and give the diagram a title that states its type and scope (C4 notation).
- Single diagram for every audience. Executives, on-call engineers and new hires need different zoom levels. Use C4-style levels.
Leadership lens
- Ask for a diagram with numbers and failure domains in every design doc; it surfaces questions before review meetings rather than during them.
- In review, point at each stateful box and ask who owns it, how it is backed up, and what depends on it synchronously.
- Run a quick graph check on the dependency map: single points of failure on the critical path and synchronous cycles are cheap to find and expensive to discover in an incident.
Check yourself
Why draw async flows with a different line style from sync calls?
Show answer
They fail differently: a failed sync dependency fails or slows the caller immediately, while an async one builds a backlog, so readers need to tell them apart at a glance.
When is a box with one replica not a single point of failure?
Show answer
When it is not needed on any path from the entry point to a required store, or when an alternative path bypasses it.
Why are cycles of synchronous calls between services a red flag?
Show answer
They create long latency chains, retry amplification around the loop and potential deadlock under load, and they make independent deployment hard.
What should you do when you want to show the details of a payment flow?
Show answer
Draw a separate sequence diagram for it, keeping the overview uncluttered, and refer back to the numbered step in the overview.
Sources and further study
Primary
- C4 model: notation: titles, legends, element types, and the rule that every relationship is a single labelled direction.
Systems docs
- Azure Well-Architected: identify flows: mapping user and system flows onto components before you analyse their failure modes.
- Google SRE book, ch. 22: Addressing Cascading Failures: why retries and timeouts along the arrows decide how a failure spreads.
- PgBouncer features: the pooling modes behind the "connections per database" annotation.
Books
- Fundamentals of Software Architecture (2nd ed.), Richards and Ford, chapter 23 "Diagramming Architecture": tools, standards and guidelines for architecture diagrams.
- Release It! (2nd ed.), Michael Nygard, chapter 4 "Stability Antipatterns": integration points, chain reactions and cascading failures, the failure paths a good diagram makes visible.
Other curricula
- donnemartin/system-design-primer: frames the diagram as step 2, a sketch of main components to justify before going deep.
- Hello Interview: Delivery Framework: frames the high-level design as about 10 to 15 minutes of drawing that comes after the requirements, core entities and API it must satisfy.
Watch
- Visualising software architecture with the C4 model (Simon Brown): zoom levels and notation that make diagrams readable.
- System Design Interview: A Step-By-Step Guide (ByteByteGo): where the high-level diagram fits in the interview and how to evolve it.
Free resources
- The C4 model: a lightweight, widely used approach to diagram levels and notation.
- Flowchart syntax (Mermaid docs): text-based diagrams you can keep next to code.
- Sequence diagram syntax (Mermaid docs): for drawing deep-dive flows and failure timing.
- Failure mode analysis (Azure Architecture Center): walking each component of a diagram for failure modes.
- Single point of failure (Wikipedia): the concept and common mitigations.