Every architecture diagram I’ve seen in a wiki was wrong. Not dramatically wrong. Just quietly, incrementally wrong. The service labeled “Auth” was split into three microservices six months ago. The arrow marked “sync call” is now async via a queue. The database labeled “PostgreSQL” was migrated to something else during a fire drill and nobody updated the box.

This is not negligence. It is physics. Code changes hundreds of times per week. Documentation changes when someone remembers. The half-life of an architecture diagram is roughly one sprint. After that, it becomes fiction.

The goal is not to write docs that never drift. The goal is to detect the drift before it misleads the next engineer.

Why docs rot faster than code

Code has a built-in correction mechanism: it either compiles and passes tests, or it doesn’t. Documentation has no compiler, no test suite, no CI gate. A wrong diagram renders perfectly. A stale ADR reads just as convincingly as a fresh one.

The divergence is also social. Engineers update code because the build breaks. They update docs because they feel guilty. Guilt is an unreliable scheduler.

When a new engineer joins and reads the wiki, they build a mental model of the system. If that model is six months stale, they will make decisions that compound the existing mess. The documentation that was supposed to reduce onboarding friction becomes a source of subtle, expensive misdirection.

The three fiction traps

There are three places where architecture documentation becomes fiction fastest.

The wiki graveyard. Architecture docs in Confluence, Notion, or SharePoint live outside the repo. They have no commit history correlated with code changes. When a refactor deletes a service, the wiki page survives like a digital ghost town. No build fails. No alert fires.

The diagram fiction. A beautiful diagram exported from Lucidchart or draw.io and pasted into a README looks authoritative. It is also a static image. The next developer who changes the architecture has to open a design tool, find the source file, update it, re-export it, and paste it back. That workflow has a 5% survival rate.

The omniscient README. The top-level README that attempts to document every service, every dependency, and every data flow in one file. It becomes a mega-document that everyone is afraid to touch. Eventually, a brave soul adds a note: “This section may be outdated.” Then another. Then the whole document is wrapped in scare quotes and abandoned.

Living documentation that stays honest

The fix is not to write more docs. It is to make the docs unavoidable.

Move docs into the repository. If the documentation is not in the same pull request as the code change, it will not get updated. ADRs, runbooks, and decision logs should live in docs/architecture/ or docs/adr/ next to the code they describe. When a reviewer sees a refactor without a doc update, they can block the merge.

Generate diagrams from code. Mermaid, PlantUML, and Structurizr let you define diagrams in text files that live in the repo. A CI job renders them on every commit. When the architecture changes, the diagram source changes with it. No design tool required.

<!-- docs/architecture/data-flow.md -->
## Ingestion Pipeline

```mermaid
graph LR
    A[Client SDK] -->|HTTP POST| B[Ingest API]
    B -->|Pub/Sub| C[Event Consumer]
    C -->|Write| D[(ClickHouse)]

This diagram is reviewable in a diff. When the pipeline gains a new Kafka topic, the change shows up in the same PR that adds the consumer.

**Separate "what" from "why."** The codebase already describes what the system does. Comments, function names, and types encode the current structure. Documentation should focus on why the system looks the way it does. That is what drifts silently: the reasoning behind a decision, not the decision itself.

Architecture Decision Records capture this. An ADR is not a spec. It is a timestamped note that says: "On this date, for these reasons, we chose this. Here are the constraints we accepted." When the constraints change, the ADR is superseded by a new one. The old record stays as history.

## Automate the "what," write the "why"

The most honest architecture docs are the ones you do not write by hand.

OpenAPI specs generated from controller code describe your API surface accurately. TypeDoc, RustDoc, or Javadoc describe your internal types. Dependency graphs generated by `cargo tree`, `go mod graph`, or `webpack-bundle-analyzer` show the actual module structure.

For higher-level architecture constraints, automated architecture tests act as a safety net. In Java, ArchUnit can enforce rules like "no package in `domain` may depend on `infrastructure`." In Python, `import-linter` does the same. In Go, you can write a small test that walks the AST and fails the build if a forbidden import appears.

```java
// This test fails the build if architecture rules break.
@ArchTest
static final ArchRule domain_independence =
    noClasses()
        .that().resideInAPackage("..domain..")
        .should().dependOnClassesThat()
        .resideInAPackage("..infrastructure..");

When a developer accidentally imports the wrong package, the build breaks immediately. The architecture doc does not need to warn them. The compiler does.

The trade-offs nobody admits

Living documentation is not free. It costs review time, CI minutes, and the occasional argument about whether a change warrants a new ADR.

Not every system needs a diagram. A single monolith with three layers does not need a C4 model. The effort to maintain generated docs should be proportional to the pain of misunderstanding the system. If the wrong mental model costs you a day of debugging, invest an hour in a diagram. If it costs you five minutes, write a comment.

Generated docs can lie too. An OpenAPI spec generated from code describes every endpoint, including the internal ones you forgot to document. A dependency graph shows every import, including the ones that violate your architecture. Generated truth is accurate but not always useful. You still need human curation to highlight what matters.

ADRs accumulate. After two years, you may have thirty ADRs in docs/adr/. Most will be irrelevant. The solution is not to delete them. It is to mark them clearly: status: superseded with a link to the replacement. The history has value. The confusion does not.

A minimal setup that works

You do not need a documentation platform. You need three things in your repo.

  1. docs/adr/YYYY-MM-DD-title.md for decisions. Use a lightweight template:
# ADR 012: Event Storage Backend

- Status: accepted
- Date: 2026-03-15

## Context
We need durable storage for ingestion events with sub-second query latency.

## Decision
Use ClickHouse for hot storage, S3 for cold archival.

## Consequences
- Fast analytical queries on recent data.
- Complex operational footprint compared to PostgreSQL.
- Migration path defined in ADR 013.
  1. docs/architecture/*.md with Mermaid diagrams for system boundaries and data flow. Require a doc update in the same PR as structural code changes.

  2. One architecture test that enforces your most expensive invariant. If you only have one rule, make it the one that prevents the last painful refactor. Add more as the system grows.

FAQ

Do I need to diagram every microservice?

No. Diagram the boundaries where teams hand off, where data crosses trust zones, and where failures cascade. A diagram of your internal CRUD service is usually waste. A diagram of which services call the payment gateway is usually valuable.

What if my team already uses Confluence?

Mirror the critical docs in the repo and link out. The source of truth stays in git. The wiki becomes a convenience layer. If the wiki drifts, engineers know where to find the real version.

How many ADRs is too many?

There is no upper limit, but there is a readability limit. If a new engineer cannot scan docs/adr/ and understand the system’s evolution in ten minutes, add an index. Group by subsystem. Mark superseded records clearly.

What about wikis for non-technical stakeholders?

Product managers and executives need high-level summaries. Generate those from the ADR index. Do not maintain a separate narrative. The same facts should flow from the source code, through ADRs, into human summaries. One source of truth, multiple views.

Start with one ADR and one test

You do not need to overhaul your documentation culture this week. Pick the last architectural decision that caused confusion. Write one ADR explaining why you chose what you chose. Pick the one invariant you wish was enforced automatically. Write one architecture test that fails the build when someone violates it.

Those two artifacts will stay honest because they live in the repo, they are reviewed in PRs, and they either pass CI or they do not ship. Everything else, the wiki pages, the slide decks, the conference posters, is secondary. Nice to have. Possibly useful. Probably wrong.