Documenting Architecture

How to communicate an architecture so it survives contact with a real team — a few living, versioned diagrams and decision records instead of a dead 200-page tome nobody opens twice.

An architecture that lives only in your head is a liability. The moment you go on holiday, change teams, or simply forget why you split that service in two, the design starts to erode. Documentation is how architecture escapes the architect — it turns private intent into a shared, durable artifact the team can reason about, argue with, and extend. Yet most attempts fail in one of two ways: nothing gets written down, or someone produces a heavyweight document that is stale the day it ships and read by no one after.

This post is about the pragmatic middle. The goal is not “complete” documentation — that is a fantasy that rots. The goal is just enough, kept close to the code, aimed at the reader who actually needs it. Three tools get you most of the way: a common diagram notation with a few levels of zoom (the C4 model), diagrams stored as code so they can’t drift, and short decision records that capture the why. Let’s build the case for each.


Why document at all

It helps to name the jobs documentation actually does, because each one implies a different reader and a different artifact.

Notice that none of these jobs requires completeness. They require the right slice, findable and current.

The gotcha: the classic failure isn’t “we wrote too little” or “we wrote too much” — it’s writing the wrong kind. A 200-page document that exhaustively describes every class is simultaneously too much (nobody reads it) and too little (it never explains why anything is the way it is). Optimize for the questions readers actually ask, and most of those questions are about context and rationale, not exhaustive detail.


The two failure modes

Almost every real-world documentation problem collapses into one of two shapes, and they fail for opposite reasons.

  NO DOCS                          THE DEAD TOME
  -------                          -------------
  "It's all in the code"           200 pages, one template
  onboarding by osmosis            written once, at project start
  decisions lost to Slack          instantly stale, trusted anyway
  bus factor = 1                   nobody reads past page 10
       |                                   |
       +---------------> both fail <-------+
                        the same way:
              the team can't get a correct
              answer to a real question fast

The empty case fails because knowledge stays locked in people. The heavyweight case fails because the effort goes into volume rather than freshness and findability — and a document that is confidently wrong is worse than no document, because people trust it. A new hire who reads that the “OrderService calls the WarehouseAPI directly,” builds on that assumption, and only later discovers a queue was inserted last quarter has been actively misled.

The escape from both is the same principle: treat documentation like code. Small, versioned, reviewed, and living next to the thing it describes. Everything below is a way to apply that principle.


The C4 model: a common notation and a few zoom levels

The single most useful idea for architecture diagrams is embarrassingly simple: pick a notation, and offer a few levels of zoom. The C4 model (created by Simon Brown) packages exactly this. Instead of ad-hoc boxes-and-lines where every diagram invents its own visual language, C4 gives you four nested levels, each answering one audience’s questions.

Think of it like a map application. You start at country level, zoom to a city, then to a street. Each zoom shows more detail about less area, and the notation stays consistent as you go.

The value isn’t the specific four levels; it’s the discipline. A common notation means an arrow always means the same thing, a box always means the same category of thing, and a reader who learns to read one diagram can read them all. And because each level targets one audience, you never hand a product manager a class diagram or make a new engineer reverse-engineer the whole system from a single overloaded picture.

The gotcha: ad-hoc diagrams are ambiguous in ways their author never notices. What does this arrow mean — a synchronous HTTP call, an async event, a data dependency, “sort of related”? Is that box a service, a library, a server, a team? Without a convention, every reader fills the gaps differently, and the diagram quietly means something different to each of them. Adopting C4 (or any consistent notation) is less about the four levels and more about making a box and an arrow mean one thing.


A container diagram, rendered

Here is a small C4-style container view of a modest e-commerce system, written in a way this site renders directly. Read it as Level 2: a customer, the deployable pieces, and the technology on each edge.

flowchart TB
    customer["Customer<br/><i>[Person]</i>"]

    subgraph shop["Online Shop [Software System]"]
        web["Web App<br/><i>[Container: React]</i>"]
        api["Storefront API<br/><i>[Container: Go]</i>"]
        worker["Order Worker<br/><i>[Container: Go]</i>"]
        db[("Orders DB<br/><i>[Container: PostgreSQL]</i>")]
        queue["Event Bus<br/><i>[Container: NATS]</i>"]
    end

    payments["Payment Gateway<br/><i>[External System]</i>"]

    customer -->|"HTTPS"| web
    web -->|"JSON / HTTPS"| api
    api -->|"reads & writes"| db
    api -->|"publishes OrderPlaced"| queue
    queue -->|"consumes OrderPlaced"| worker
    worker -->|"charges card / REST"| payments
    worker -->|"updates status"| db

Every box carries its kind and its technology; every arrow carries its purpose and protocol. That is the whole trick — a reader can answer “how does an order get charged?” without opening a single file. Note also what is absent: no class names, no function signatures, no field lists. Those belong to a lower zoom level, or to the code itself.


Diagrams as code: so they can’t rot

A diagram drawn in a slide deck or a whiteboard photo has one fatal property — it is disconnected from the system it describes. When the code changes, nothing forces the picture to change with it. So it drifts, silently, until it is a confident lie.

Diagrams-as-code fixes this by making the diagram a text file in the repository. You describe the diagram in a plain-text notation and a tool renders it. Three common choices:

The payoff is mechanical but decisive. Because the diagram is text:

   whiteboard / slide            diagrams-as-code
   ------------------            ----------------
   image on a laptop     -->     .mmd / .puml in the repo
   drifts silently       -->     changes in the same PR as code
   binary, no diff       -->     line-by-line diff in review
   "who has the source?" -->     git log is the source of truth

The gotcha: a beautiful architecture diagram drawn once, in a slide deck, is stale the day after it’s presented — and it lies to every new hire who trusts it, because it looks authoritative and nobody remembers when it was last true. The fix is not “draw it more carefully.” The fix is to put the diagram source in version control next to the code, so the only way it goes stale is if a pull request changes the system and leaves the diagram wrong — something a reviewer can see and block.


“Just enough”: README + C4 + ADRs

Put the pieces together and a lightweight, durable documentation set for most systems looks like this:

  1. A README that states what the system is, how to run it locally, and where the other docs live. The front door.
  2. A couple of C4 diagrams — almost always a System Context and a Container diagram, stored as diagram-as-code. Component diagrams only for the containers complex enough to earn one.
  3. A set of ADRs — Architecture Decision Records — capturing the why behind significant choices.

ADRs deserve emphasis because diagrams show what the architecture is but rarely why. An ADR is a short, dated, immutable note in the repo — typically context, the decision, and its consequences — written when a significant call is made. (We covered ADRs in depth in the earlier post on architecture decisions; here they are simply the “why” layer of your living docs.) When someone later questions a design, they read the ADR instead of guessing. When a decision is genuinely reversed, you don’t edit the old record — you write a new ADR that supersedes it, preserving the trail.

This trio is deliberately small. It fits in the repository, it’s reviewed like code, and a new engineer can consume all of it in an afternoon and come away with an accurate map plus the reasoning behind the tricky parts.

The gotcha: the instinct to “document everything” backfires — the more you write, the more there is to keep current, so it rots faster, and its very volume ensures nobody reads it. Document just enough: the context and container views for what, and ADRs for why. If a section of documentation isn’t answering a question someone actually asks, it is not an asset — it’s maintenance debt wearing a useful costume.


Multiple views for multiple audiences

A single diagram cannot serve everyone, because different stakeholders ask different questions. The 4+1 view model (introduced by Philippe Kruchten) captures this cleanly: describe an architecture through several concurrent views, each aimed at a set of concerns, all tied together by scenarios.

In plain terms, the views are:

You do not need all five for every project — that would violate “just enough.” The point is a diagnostic: when a diagram feels cluttered or an argument goes in circles, it’s often because two audiences with different concerns are being served by one picture. Split it by view.

There’s a neat correspondence worth noticing: C4 is largely a structural (logical/development) decomposition, while the deployment view maps onto C4 with a separate deployment diagram. The two ideas complement rather than compete — C4 gives you the zoom levels, 4+1 reminds you which concern each diagram addresses.

The gotcha: trying to make one diagram serve every audience produces a picture that serves none — the operations engineer wants regions and failover, the developer wants modules and dependencies, and cramming both into one canvas leaves an unreadable mess that satisfies neither. Pick the view and the zoom level for the specific reader in front of you, and make a second diagram rather than overloading the first.


arc42: a template if you want one

Some teams want more scaffolding than “README + diagrams + ADRs,” especially for larger or more regulated systems. arc42 is a well-known open template for architecture documentation. It offers roughly a dozen numbered sections — things like goals and constraints, the solution strategy, building-block view, runtime view, deployment view, crosscutting concepts, architecture decisions, quality requirements, and known risks.

Two things make arc42 worth knowing. First, it is a checklist of concerns, not a mandate to fill every box — you keep the sections that carry weight for your system and drop the rest. Second, it composes naturally with everything above: the building-block sections are a good home for C4 diagrams, the decisions section is where ADRs go (or are referenced), and the whole thing can live as Markdown or AsciiDoc in the repo so it stays diffable and reviewable.

Treat arc42 as a menu, not a form. The failure mode is filling in all twelve sections dutifully at project start and never touching them again — which is just the dead tome with a nicer table of contents. Keep the sections that answer real questions, and let the rest stay empty.


Keeping documentation current

None of this matters if the docs drift. Freshness is the property that separates a useful document from a dangerous one, and you buy it with process and automation, not willpower.

Keeping documentation current
Practice What it prevents
Diagrams-as-code in the repo Whiteboard photos and slide decks that drift silently
C4 (or any) consistent notation Ambiguous boxes and arrows that mean different things to different readers
README + C4 + ADRs The 200-page tome nobody reads and can’t keep current
ADRs for the why Superstitious preservation or blind removal of past decisions
One view/zoom per audience Overloaded diagrams that serve no one
Review docs in the PR Documentation quietly going stale after every change

Key takeaways


Further reading