# Architecture diagrams that stay true

> The diagram on the wall was accurate the day it was drawn. Here is why that is the last day it was, and what to do differently.

**Source:** https://graphlit.co/blog/diagrams-that-stay-true

**Published:** 2026-08-09

Every engineering organisation has the same artefact: a diagram, made for a presentation eighteen months ago, that everyone knows is out of date and nobody updates. It still gets shown to new starters, because it is better than nothing, and it teaches them a system that no longer exists.

## Why diagrams die

Not laziness. Three structural reasons, and none of them is fixed by resolving to try harder.

### They are disconnected from the thing they describe

The diagram lives in a design tool. The system lives in a repository. Nothing links them, so changing one has no mechanical relationship to the other. Two artefacts, one intention, no connection.

### Updating them is unrewarded work

Shipping a feature is visible. Updating a diagram afterwards is invisible, and skipping it has no immediate consequence. Any process that depends on people doing invisible work with deferred payoff will lose.

### They are pictures, not data

A box in a design tool is a rectangle with a label on it. It does not *know* it is a service. Nothing can ask it what it connects to, so nothing can check it, which means nothing can tell you it has gone stale.

> **A wrong diagram is worse than no diagram**
>
> With no diagram, people read the code. With a wrong one, they act on it: planning against a service that was decommissioned, assuming a boundary that was crossed a year ago. The confidence is the damage.

## The three approaches

| Approach | How it works | Stays true? | Trade-off |
| --- | --- | --- | --- |
| Draw it by hand | A design tool, updated manually | No, it decays from day one | Total expressive freedom; zero enforcement |
| Diagrams as code | Text definition rendered to an image, in the repo | Partly: versioned, still manual | Reviewable in a diff, but nothing checks it against reality |
| Generated from code | Derived by static analysis | Yes, regenerated each time | Shows what *is*, never what was *intended* |

The third is the biggest step forward and it has a real limitation worth naming. A generated diagram cannot be wrong, but it also cannot disagree with you, and disagreement is the useful part. It will happily draw the direct database call the frontend should never have made, as a normal edge, because it is describing reality with no opinion about it.

## What a diagram needs to be trustworthy

Four properties. The first three are common; the fourth is the one that changes what a diagram is for.

1. **Typed.** A box is a *service* or a *table* or a *route*, not a rectangle. Type is what makes a connection meaningful and checkable.
2. **Versioned with the code.** In the repository, changing in the same commits, reviewable in the same diff.
3. **Comparable to reality.** Something can mechanically ask whether the code still matches it.
4. **Opinionated.** It states what *should* be true, so reality can contradict it, and that contradiction is the alert.

Properties three and four together are what separate a picture from a contract. A generated diagram has three. A hand-drawn one has four and nothing else. You want both.

## Intent and reality, side by side

This is the design Graphlit follows. You draw the intended architecture. That is the opinionated half, and drawing is a good interface for it because architecture is genuinely a picture. It becomes a **typed graph**: services, routes, tables, jobs, and the edges between them, each with a type.

Then the code is read and mapped back onto that graph, file by file. Now there are two structures of the same kind, and comparing them is mechanical: what exists in the code with no counterpart in the drawing, what is drawn but unbuilt, and which files changed underneath the node that owns them.

*[Interactive demo: A typed graph: boxes with kinds, edges with meaning. Not a picture of a system, a description of one.]*

The diagram stops being documentation and becomes an assertion. Nothing has to remember to update it, because the moment it stops being true, something says so. That is the mechanism behind [catching architecture drift](https://graphlit.co/blog/architecture-drift).

## If you are not going to adopt a tool

Most teams will not change their tooling this quarter. Three things that help regardless:

1. **Move the diagram into the repository**: Even as a text-defined diagram rendered to an image. Being in the same pull request as the change is most of the battle. It puts the update in front of a reviewer at the moment it is wrong.
2. **Draw fewer, smaller diagrams**: One diagram of everything is never accurate and never read. One per subsystem, each fitting on a screen, gets updated because updating it is a two-minute job.
3. **Automate the boundaries you care about**: You will not enforce a whole diagram by hand. You can enforce three dependency rules with existing tooling, and those three are usually the ones that matter.

## Frequently asked

### What is the best tool for architecture diagrams?

For communication, whatever your team already uses. For accuracy, the deciding property is not the tool but whether the diagram is connected to the code: versioned alongside it and checkable against it. A beautiful diagram in a disconnected design tool decays exactly as fast as an ugly one.

### Should architecture diagrams be generated or hand-drawn?

Both, for different jobs. Generated diagrams show what the system actually is and cannot go stale. Hand-drawn ones state what it should be, which is the part that can be violated. The useful setup is a stated intent plus a generated reality, compared automatically.

### How often should architecture documentation be updated?

Any answer in units of time is the wrong shape. It means the update is a scheduled chore that will be skipped. It should be updated in the same change that alters the system, which only happens realistically if something fails when it is not.

### Is the C4 model still worth using?

Yes, as a way of thinking about levels of detail: separating context, containers and components stops the single unreadable everything-diagram. It is a notation, not a mechanism, so it does not solve staleness on its own; pair it with something that checks the diagram against the code.

