Technical Diagrams

Most architecture diagrams are wrong. Not wrong when drawn — wrong six months later, because they were exported as a PNG from a tool only one person has access to, and updating them means reopening a file, rearranging boxes, re-exporting, and remembering to commit the result. Nobody does that, so the diagram drifts until it's actively misleading, and new engineers form a mental model of a system that no longer exists.

Diagrams-as-code solves it the same way docs-as-code solves stale prose. The diagram is a text file, lives next to the code, is reviewed in the pull request, and renders automatically. Anyone can edit it. Changes show up as a readable diff. And because Mermaid renders natively in GitHub, GitLab, Notion, and most documentation tooling, there's no build step to forget.

The other half of the problem is choosing what to draw. A single diagram trying to show services, data flow, deployment topology, and request sequencing shows none of them. The C4 model is the standard answer: separate diagrams at separate zoom levels, each with one audience and one question.

TL;DR

Quick Example

A sequence diagram in Mermaid — the type you'll write most often:

That diagram answers a question prose handles badly: what is synchronous, what is asynchronous, and where does the request return to the client? The -) arrows (async) versus ->> (sync) carry information that three paragraphs would struggle to convey.

Core Concepts

The C4 model

Four zoom levels, each for a different audience:

Levels 1 and 2 are the ones worth maintaining. Level 3 is useful for a genuinely complex service, and level 4 goes stale within a sprint and duplicates what the code already says.

The value of a context diagram is that it fits on one slide and answers "what is this system and what does it touch?" for a new engineer, an auditor, or an executive — three audiences that otherwise get three different explanations.

Choosing a diagram type

Sequence diagrams are underused relative to their value. Most confusion about a distributed system is about ordering, synchrony, and failure paths — exactly what a sequence diagram shows and what a box-and-arrow architecture diagram cannot.

Mermaid

The pragmatic default, because it renders in GitHub, GitLab, Notion, Obsidian, Docusaurus, and most other places without any tooling.

Mermaid's weakness is layout: it uses automatic layout with limited control, and diagrams past roughly fifteen nodes tend to produce crossing edges and awkward placement. When you're fighting the layout, that's usually a signal to split the diagram rather than to switch tools.

D2 and PlantUML

When Mermaid's layout isn't enough:

D2 has a better layout engine (ELK), cleaner syntax, and genuinely attractive output, at the cost of a rendering step in your build. PlantUML is older, extremely capable, and has the broadest diagram-type coverage including proper C4 support via a standard library — its syntax is less pleasant and its ecosystem is mature.

Both require a build step to produce images, which means one more thing that can be forgotten. For most teams, Mermaid's zero-setup rendering outweighs its layout limitations.

Generated diagrams

Anything derivable from a source of truth should be:

A generated ER diagram is always correct. A hand-drawn one is correct on the day it's drawn.

Best Practices

Store diagrams as text, next to the code

The whole argument in one line. Text diffs in review, anyone can edit, no proprietary tool, no export step, and the diagram changes in the same PR as the system it describes.

One diagram, one question

An architecture diagram that also tries to show request sequencing, deployment topology, and data ownership becomes a wall of boxes. Draw four diagrams, each answering one question, and link between them.

Label every arrow

An unlabeled arrow means "these are related somehow," which the reader already assumed. The protocol, direction, and synchrony are the information.

Distinguish synchronous from asynchronous

Solid lines for synchronous calls, dashed for asynchronous or event-driven. In a system with a queue in it, this is the single most important distinction on the diagram and the one most often omitted.

Keep architecture diagrams under about fifteen boxes

Past that, nobody can hold it in their head and the layout becomes unreadable regardless of tool. Split by bounded context, by team ownership, or by C4 level.

Include a legend when notation isn't obvious

Shapes, colors, and line styles that carry meaning need explaining. A diagram whose conventions are only in the author's head is a diagram that gets misread.

Date it, or better, make it unnecessary

A rendered diagram in a wiki should carry a "last updated" date so readers can calibrate their trust. A diagram in the repo, updated in the same PR as the code, doesn't need one — which is a good argument for the latter.

Render in the docs pipeline

Mermaid renders natively in most tooling. For D2 or PlantUML, render during the docs build and commit or publish the output, so a reader never sees raw diagram source. Check that rendering succeeds in CI — a syntax error that produces a blank diagram is easy to miss.

Common Mistakes

Screenshots of a whiteboard

Diagrams in a proprietary tool one person can open

The everything diagram

Unlabeled relationships

Diagrams that contradict the code

Hand-drawing what could be generated

FAQ

Mermaid, D2, or PlantUML?

Mermaid unless you have a specific reason otherwise — it renders natively in GitHub, GitLab, and most documentation platforms with zero setup, which removes the most common reason diagrams don't get made. D2 when layout quality matters and you're willing to add a build step; its output is noticeably more attractive for complex diagrams. PlantUML when you need diagram types the others lack or you're in an ecosystem that already uses it.

Are drawing tools like Excalidraw or Figma ever right?

For thinking and for one-off explanations, absolutely — sketching on a shared canvas during a design discussion is faster than writing syntax, and Excalidraw in particular exports an editable .excalidraw file that can live in the repo. What they're bad at is being the long-term source of truth for a diagram that must stay accurate, because updating requires opening the tool. A reasonable split: sketch in Excalidraw, formalize the ones worth keeping in Mermaid.

How do I keep diagrams up to date?

The same mechanisms as documentation: keep them in the repo so they change in the same PR, add "does this change a diagram?" to the review checklist, generate whatever can be generated, and delete diagrams that have stopped being maintained rather than leaving them to mislead. A stale diagram is worse than no diagram because readers trust it.

How much detail belongs in an architecture diagram?

Enough to answer the diagram's one question, and no more. A container diagram doesn't need internal class structure. A sequence diagram for the happy path doesn't need every error branch — draw a second one for failure handling if it matters. When you're adding a box because it exists rather than because it's relevant to the question, stop.

Should diagrams be in the code repo or a wiki?

The repository, with the docs site rendering them. That's what makes them reviewable alongside the code and keeps them in sync. A wiki is acceptable for diagrams that don't correspond to code — organizational structure, process flows, deployment environments — where there's no PR to attach the update to.

Can AI generate diagrams from a codebase?

It's reasonably good at producing a first-pass Mermaid diagram from source or from a description, and that's a genuine time saving for the initial draft. What it doesn't know is which relationships matter, what the system is supposed to look like versus what accreted, or where the boundaries a team actually cares about lie. Treat the output as a starting point to be edited down — the editing is where the diagram becomes useful.

Related Topics

References