Skip to content
Why AI code rots · 5 min read

Architecture drift: what it is and how to catch it

Every codebase has a design in someone's head and a design in the files. Drift is the distance between them, and it only ever grows on its own.

Ask two engineers on the same team to draw the system on a whiteboard. You will get two different pictures, and neither will match the code. That gap has a name: architecture drift. It is one of the few problems in software that gets worse when nobody does anything at all.

A precise definition

Drift is the divergence between the intended architecture and the implemented one.

It is worth being strict about the word. Not every change is drift. Deliberately deciding the frontend may now query the read replica directly, writing that down, and updating the design is architectural evolution, healthy and normal. Drift is when the code changes and the intent does not follow, so the two silently disagree.

EvolutionDrift
The design changedYes, deliberatelyNo, only the code did
Someone decidedYesIt happened as a side effect
Written downYesNo
DiscoveredWhen it happensDuring an incident

The four sources

1 · Expedience

A deadline, a direct call that skips a layer, a note to clean it up later. The note is never actioned. This is the classic source and it long predates AI.

2 · Ignorance

Someone new does not know the rule, because the rule lives in a conversation they were not in. They write something reasonable that violates a constraint nobody told them about, and it passes review because the reviewer did not spot it either.

3 · Generated code

The modern accelerant. An AI agent is permanently in the position of the new starter: every session, no memory of the reasoning, inferring the rules from the files. It is fast, so it drifts fast. See why AI-generated apps fall apart.

4 · Deletion

The quiet one. A service is decommissioned, a table is dropped, a module is folded into another. The code is correct; the diagram now describes a system that no longer exists. Drift by subtraction still misleads everyone who reads the diagram.

Why it stays invisible

Drift has no symptoms until it has expensive ones. Nothing in the ordinary toolchain is looking for it:

  • The compiler checks types, not topology. A frontend importing the payments module directly is perfectly well-typed.
  • Tests check behaviour. Every drift example preserves behaviour. That is what makes it drift and not a bug.
  • Linters check style and local patterns, not which module may depend on which.
  • Code review checks the diff. Drift is a property of the whole system, and it is nearly impossible to see a hundred-line diff and notice it has quietly created the fourth path into the database.

How to detect it

All the working approaches share one shape: a machine-readable description of the intended architecture, plus something that compares it to reality automatically. The manual alternative (periodic architecture reviews) finds drift months after it happened, which is better than nothing and much worse than a check.

01

Dependency rules

Declare which modules may import which, and fail the build on a violation. The cheapest useful step, and there are mature libraries for most language ecosystems. Catches boundary erosion specifically.

02

A generated map

Derive the architecture diagram from the code rather than drawing it. It cannot go stale, because it is regenerated. It tells you what is, though not what was intended.

03

Intent, compared to code

Keep a description of the intended system, and diff it against the generated map on every change. This is the one that catches all four sources, because it has both halves: a stated intent, and an observed reality to check it against.

Graphlit does the third. The drawing is the intent: typed, versioned, and stored with the code. Every file it builds is hash-mapped back to the node that owns it, so after any change the code can be re-read and compared: what moved, what vanished, what was never built, and what appeared that nothing accounts for.

checkout-flow · sync
GraphFlowsTasksHealthSync

Five nodes claim a file in this repo.

Press Check sync to hash every one of them against the code that is actually on disk right now.

A drift report: each node hash-checked against the file on disk.

Making drift a decision

The goal is not zero drift. Zero drift means nothing is changing.

The goal is that every divergence becomes a decision rather than an accident. When the check reports that the frontend now calls the database directly, exactly one of two things is true, and either is fine as long as somebody chooses:

  1. 01That was a mistake. Revert it.
  2. 02That was deliberate. Update the design, and now the drawing is true again.

What is not fine is the third case, which is what happens by default: nobody knows it happened, and the diagram on the wall quietly becomes fiction.

Common questions

What is architecture drift?

The divergence between a system's intended architecture and its implemented one. It happens whenever code changes without the design being updated to match, and unlike most software problems it grows without anyone doing anything.

What is the difference between architecture drift and technical debt?

Technical debt is usually a known shortcut you intend to repay. Drift is unknown: the gap between belief and reality. Debt is a decision with a cost; drift is a decision nobody made, which is why it is harder to plan around.

How do you measure architecture drift?

You need a machine-readable statement of the intended architecture and a map derived from the actual code, then you compare them. Useful measures are the count of dependency-rule violations, components in the code with no counterpart in the design, and components in the design with nothing implementing them.

Can architecture drift be prevented entirely?

No, and preventing it is the wrong goal, because some divergence is legitimate evolution. The achievable goal is to make every divergence visible quickly, so it becomes a deliberate decision to accept or revert rather than something discovered during an incident.

Draw it. Then keep it true.

Graphlit turns an architecture drawing into a typed graph, builds against it, and proves the code still matches. Free to start.

Keep reading