System Design Diagrams That Don't Suck
Why most architecture diagrams fail, the four types every engineer should know, and a pragmatic guide to drawing one that actually helps the reader understand what you built.
Most architecture diagrams are useless. They’re either a wall of boxes with no hierarchy, a Visio export from 2013 with clipart servers, or a hand-drawn sketch that only makes sense to the person who drew it. A good diagram, by contrast, is a tool for thinking — it lets you and your reader reason about a system without loading the full codebase into your head.
Here’s what separates the two.
The four types of diagram
You almost certainly need more than one diagram per system. The useful ones fall into four categories, each answering a different question.
1. Context diagram — “where does this fit?”
The system is a single box. The surrounding boxes are the external actors and systems it talks to. No internal detail. This is the diagram you show to someone who’s never heard of your system before.
[Users] ──► [our API] ──► [Stripe]
│
└─────────► [Postgres (managed)]
If you can’t draw this cleanly, you don’t yet know what your system is.
2. Container diagram — “what runs where?”
One level deeper. You break the system into deployable units — web app, mobile app, API, workers, databases — and show how they talk. Still no internal code structure.
This is the diagram engineers use to orient new hires. “We have a Next.js app, a Go API, a Postgres cluster, and a Redis queue — here’s how they connect.”
3. Component diagram — “what’s inside a container?”
One level deeper again. For one container (say, the Go API), show the internal modules or services and their dependencies. Most people skip this level because it rots as the code changes. When it’s worth drawing, it’s because the module boundaries are load-bearing for a decision you’re making.
4. Sequence / flow diagram — “what happens in time?”
For a specific user journey or request path, show the ordered calls between components. This is the diagram that exposes hidden coupling, fanout problems, and latency stackups.
User ──► CDN ──► API ──► AuthService ──► DB
└────► FeatureFlags
└────► Stripe
If you can’t fit the sequence diagram on one page, your user journey has too many hops.
These four are the C4 model in miniature. You don’t need to adopt C4 formally — just internalize that “one diagram” is almost always wrong. A system needs at least a context diagram plus whichever one or two of the others answer the question you’re trying to answer.
The five things that kill a diagram
1. Every box looks identical
When every box is the same color, same size, same shape, the reader has no hierarchy to grab onto. Use visual distinction — database cylinder vs. service rectangle vs. external-actor stickperson. The moment you can’t tell a third-party service from a service you own at a glance, the diagram has failed.
2. Arrows with no labels
A bare arrow is a statement that “these two things are related.” Which is almost useless. Label every arrow with what flows over it — “HTTP/JSON,” “gRPC,” “S3 events,” “Stripe webhook.” The label does more work than the arrow itself.
3. Mixing levels
The biggest single cause of confusing diagrams. You have a box labeled “Payments” (a container), another labeled “AuthHandler.verify()” (a function), and another labeled “AWS” (a cloud provider). These are three different levels of abstraction on one page, and the reader has to re-calibrate at every arrow.
Pick a level. Stay on it. If you need to show two levels, make a second diagram.
4. Drawing the code, not the runtime
The code’s module structure is often different from the runtime deployment. Your pkg/payments Go package might live in the same binary as pkg/users. On a deployment diagram, those are one box, not two. Decide whether you’re drawing “what the code looks like” or “what runs in prod,” and stick to it.
5. Decorative clipart
Stock-photo servers, cartoon clouds, 3D cylinders with drop shadows. Visual noise that makes the actual information harder to see. Good architecture diagrams are almost always near-monochrome with two or three accent colors used sparingly.
The pragmatic tool choice
There are a lot of options. In rough order of “how often do I see it in real codebases”:
- Mermaid — text-based, renders natively in GitHub/GitLab README, version-controlled, zero UI fiddling. My default for anything that lives next to code.
- Excalidraw — fast for whiteboard-style sketches. Good for RFC early drafts. Not ideal for long-lived docs because the file format is harder to diff.
- draw.io / diagrams.net — heavier but fine for polished diagrams in a wiki. Supports export to SVG.
- Whimsical, Miro, Figma — sharp-looking diagrams but locked into a tool, not version-controlled with the code.
For engineering READMEs and architecture docs, Mermaid wins because it diffs, reviews, and renders in GitHub without anyone installing anything. A simple example:
graph LR
U[Users] --> CDN[Cloudflare]
CDN --> API[API / Astro SSR]
API --> KV[(Cloudflare KV)]
API --> OR[OpenRouter]
API -.fallback.-> WAI[Workers AI]
That renders inline on GitHub and updates in the PR when the architecture changes.
The checklist before you publish one
Before you paste a diagram into a README, RFC, or slide, run through this:
- One level of abstraction. No mix of classes, containers, and companies.
- Labeled arrows. Every line has a protocol, format, or event on it.
- Clear system boundary. What’s yours vs. someone else’s is visually obvious.
- Answers a specific question. You can say “this diagram exists to show X” in one sentence.
- Legible at the size it’ll be viewed. Slack previews, GitHub mobile, phone screenshots.
If any of those fail, iterate.
The actual hard part
The hardest thing about architecture diagrams isn’t the drawing — it’s figuring out what to leave out. A diagram that shows everything shows nothing. A good one ruthlessly omits anything that doesn’t serve the question the reader is asking.
When in doubt, draft two versions of the same diagram and ask a colleague which one is easier to understand on first read. The answer is almost always the simpler one.
Getting to a first draft faster
If you know roughly what the system looks like but the blank Mermaid file is intimidating, our Architecture Diagram tool takes a plain-English description (“a Next.js app on Vercel hitting a Postgres db, with Stripe for billing and a Resend email worker on a queue”) and returns a Mermaid diagram you can edit from there. Free, no sign-up, 5 runs per day.
You’ll want to edit the output. But editing a draft is faster than drawing from scratch, and the draft at least forces you to decide what level of abstraction you’re working at.
The goal of every diagram is the same: when a stranger reads it, they should understand the system well enough to ask a good question. If they can’t, redraw.