Overdocumentation

Or: my own codebase no longer takes my word for it

MH

Markus Hav

Lead Researcher, Agents · August 4, 2026

Essay

A few weeks ago I tried to vibe-code a small pet feature into my own repository. Nothing dramatic — the kind of idea you would normally bang out in an evening and merge before anyone noticed. The CI agents bounced it. Politely, and with citations.

The diff contradicted a logged decision without recording a reversal. It added a surface no check claimed. It embodied a choice that appeared nowhere in the decision log. Five rounds later, the implementing agent and the reviewing agents jointly gave up: the review board’s closing comment read “recorded, not fixed”. The finding stands in the review record, the code stands unchanged, and the PR never reached main. If the idea wants in, it comes through the front door: a counter-decision argued into the log before any code.

Two things about that episode are worth the price of this essay. The first: in this repo, a human can no longer even prompt effectively. To change the memory, you need an agent that has read the memory — the working method for a human with an idea is to send a planning agent through the decision log first, let it surface the conflicts, draft the counter-decisions, and only then write code. The second: I used to be the person telling a micromanaging executive to please go through the team’s process. Now my own harness tells me. It is, honestly, refreshing.

My own codebase no longer takes my word for it.

Naming the practice

I call it overdocumentation: deliberately documenting far past the point that classical software-engineering judgment considers sane. Every decision written down, however small, with its rationale and its status. Every durable rule stated in prose and mapped to the thing that enforces it. The log append-only, cross-linked, and machine-validated, so that documentation stops being an artifact about the system and becomes load-bearing infrastructure of the system — the shared, versioned memory that stateless agents and forgetful humans both operate from.

The word is deliberately self-deprecating. It names the thing critics would call it, then defends it. Documentation used to be the thing you skimped on — and skimping was correct: it was a cost calculation, honestly made. The provocation of this essay is that the costs just changed.

Why the economics flipped

Every classic argument for minimal documentation is a cost argument. Writing docs stole hours from code. Nobody read them. They rotted silently, and stale docs are worse than none. All three premises were true for decades, and all three broke at once.

Four Cost Curves Crossed

Every classic argument for minimal documentation is a cost argument. Every one of them just broke.

01

Writing

Was Competed with coding for scarce human hours — and lost.

Now An agent drafts the decision record, the doc update, and the comment in the same pass as the code. The marginal cost of recording a choice rounds to zero.

02

Reading

Was The dirty secret of docs: nobody read them.

Now Agents read them every single time. They grep, they follow cross-references, they quote rules back at you. A convention written down now actually binds.

03

Verifying

Was Doc rot was fate. Prose and code drift apart silently, and stale docs are worse than none.

Now LLM reviewers in CI check semantically on every pull request; deterministic meta-tests pin the mechanical half. Drift stops being a fate and becomes a build failure.

04

Forgetting

Was A team of humans loses knowledge in months, through attrition.

Now Agent sessions are stateless. A fleet of agents loses everything in hours — unless the repo itself is the brain. Forgetting became the expensive thing.

When writing, reading, and verifying are all cheap, and forgetting is the expensive thing, you write everything down — and you arm the archive with a police force.

The fourth curve is the one that changes everything. Whatever an agent learned in its last session is gone unless it was written somewhere the next session reads. Model weights are ROM; a context window is RAM; the repository is the only disk. Overdocumentation is just admitting that the disk is the only place state survives — and organizing it like you mean it.

Documentation is cheap. Amnesia is expensive.

Tour stop one: the decision ledger

The center of the practice is a decision log — and not one reserved for “significant architectural decisions.” Every decision, down to the animation timing and the naming rule, gets a dated file: what was decided, why, and a status that admits it is an experiment. “Decided” is defined in the log’s own header as decided to try, pending validation in code. Anything can be revisited; the rule is that you record the reversal rather than edit history. A revising decision names its target; the revised file points back and flips its status; CI validates both directions. A choice can be reversed in a day — that is healthy — but it can never be reversed silently. The failure mode being engineered away is not “we chose wrong.” It is “we un-chose without noticing, and re-litigated the same question every six weeks.”

How small do the decisions go? In one repo I work in, the log contains a six-hundred-word decision about CSS opacity — entrance animations may only add to an already-finished page, so a failed animation can never leave the page blank, and a unit test literally brace-matches the media query that keeps it true. There is a decision about the sign of an SVG dash offset, because engines disagree on how a negative one resolves. There is a decision about which letters a random ID suffix may use (“so it survives being read off a screen”) — with an explicit disclaimer that unguessability is not a property anyone may rely on. There is an 88-line decision on the difference between an empty list and an absent one, because that difference once silently disabled a feature.

Absurd? The template for a decision file ends with the sentence that justifies all of it: state what forced the choice, what was rejected, and the costs you are accepting on purpose, so their absence later reads as intent, not oversight. The log exists so that silence, later, is information.

Even the ledger’s own history is a lesson in why enforcement matters. The log began as a single numbered table — and sequential IDs turned out to be an implied merge queue: every pair of decision-logging PRs collided on “who gets the next number.” The PR that retired the numbering was drafted against freeze point #113, which became #115 while it was being drafted and #116 while it sat in review. The system got the last word on its own way out. And the frozen table now carries a conservation law: a unit test asserts every historical ID appears exactly once across the old table and the new files — because a merge conflict once silently deleted two rows from the middle of the table, and the existing “nobody appended” check watched only the highest number. The fix was not “be more careful.” It was an accounting identity, in a test, with the war story in a comment above it.

Tour stop two: prose with a police force

Prose that nothing checks is a wish. So every durable statement gets wired to an enforcement mechanism, and the wiring runs in layers.

The deterministic layer treats citations as foreign keys. Decisions are cited by ID from markdown, TypeScript, Terraform, SQL, YAML, commit messages, and PR bodies — about 1,300 citations in that young repo, and code citations outnumber prose ones. The log is load-bearing comments, not an archive. A CI test scans every tracked file and requires every citation to resolve: cite a decision that does not exist, or delete a decision file someone still cites, and the build fails. The ledger and the code hold references into each other, ref-checked like a database. Even the blind spots are documented in place — the plans directory is exempt, “because a plan legitimately cites decisions that do not exist yet.” Plans are the one place where the future tense is legal, and the tooling encodes that epistemology explicitly.

The semantic layer is LLM checks in CI. One reads each code diff against the full rendered decision log and asks a single question: does this change contradict a logged decision without recording a reversal? Its precision rules are strict — “a finding that cannot name a specific decision entry is not a finding” — and its philosophy matches the ledger’s: reversals are legitimate and expected; the only violation is the silent one. A diff that adds a decision recording its own reversal is the system working, not a finding. Another check is a staleness tripwire: every automated check declares which docs its rules are lifted from, and when a PR edits the doc but not the check, a focused review fires and asks whether the two still agree. Docs cannot silently strand their enforcement, and enforcement cannot silently outlive its docs.

My favorite of the family: the internal slide deck has a freshness check. The deck makes checkable claims — what is live, what is building, what is planned — and a CI review flags any merge that makes a slide false, naming the slide and the one-line correction. “Live” is defined on the slide itself as deployed and in daily use, and the check holds the marketing copy to the definition. Our slide deck fails CI when it lies.

This is Chesterton’s fence, solved structurally: every fence in the field has its construction permit stapled to it, and removing the fence without reading the permit is a build failure. It is also case law with a clerk — decisions as precedents, cited by docket number, overruled by later decisions but never expunged, with a court that checks new work against them.

Tour stop three: the checks check themselves

A registry of checks rots like anything else, so the system polices its own coverage — and this is the part I think is genuinely new.

The critical invariants are not scattered through review checklists; they are a data structure. Each is one file exporting a spec: a tier by blast radius, whether it gates the build, the trigger paths, the rules as prose, and the docs and decisions the rules are lifted from. Top-tier checks gate from day one and fail closed — a model outage must not silently pass a security review — and a meta-test asserts the posture itself, so downgrading a gate is a loud two-place diff while promoting one stays a one-line change. Loosening is loud; tightening is cheap.

Gating CI on an LLM reviewer sounds reckless, and undisciplined it would be. Two protocols make it sane. First, confirm-or-withdraw: before any gating finding may fail the build, a second focused model call must confirm it; findings it cannot confirm are withdrawn — still rendered on the review board, never counted toward the exit code. The rule has a name: downgrade to visible, never hide. Second, teach the check, don’t fight it: when a reviewer repeatedly flags a sound pattern, the protocol is to verify the code is right, make the guarantee deterministic in a meta-test, then name the pattern in the check’s own rules as known-good by construction, citing the deterministic guard. Applying that protocol once, the new deterministic test found a real gap on its first run. Teaching the check hardened the guarantee instead of muting the alarm.

Coverage itself is data, not vibes. An inventory maps every stated commitment to the deterministic tests that would fail if it broke and the semantic checks that defend it — and where both lists are empty, a mandatory written rationale for why that is consciously accepted. Tier-zero entries are not allowed to be unguarded, by test. A sibling surface map lists every top-level directory with the checks that claim it, claims proven by probe paths, so a PR adding a new directory fails CI until the same PR records a coverage decision. And a sentinel asks four questions of every diff: does it add an invariant nothing covers, make a check’s prose stale, delete what a check defends, or embody a decision that is not in the log? Its trigger paths include the checks package itself, so changes to the assurance system get asked the same four questions about themselves.

Deletion, finally, is a ceremony. A check leaves the registry only by moving to a tombstone list naming the decision that killed it; an append-only roster of every check that ever shipped asserts each appears in exactly one of active or retired; and an erosion sentinel reviews any diff to the guard layer with one question — after this change, is any guard weaker than before? Deliberate deletion stays a one-PR change. Accidental deletion becomes structurally impossible. And the honest limit is written in the docs rather than papered over: a diff that weakens this sentinel is reviewed by the weakened sentinel — which is exactly why the last line of defense stays outside the loop, in model-free structural tests, branch protection, and humans.

What it feels like to live in

The counterintuitive part: speed and confidence went up. Every guardrail that fires is one less thing a human had to catch in review; every decision written down is one less argument re-fought from memory. The comment culture changes too — the best comments stop describing the next line and become letters to the next agent: constraints, precedents, war stories in place. A fallback operator annotated “deliberately left literal: this is a ceiling, so an empty list that ever reached it fails closed, never open.” A workflow file documenting the deliberate absence of a trigger key, with a citation, so no helpful agent ever “fixes” the gap. One gating check even flags a comment that overstates what its guard guarantees — a guardrail that misstates its own guarantee is worse than none.

What Refusing to Economize on Prose Looks Like

A production TypeScript monorepo, young enough that its age is still counted in weeks. Single-digit human contributors, plus agent fleets.

27.6%

of ~52,000 non-empty source lines are comment lines

0.9 : 1

test files to source files — 23 of them structural meta-tests

156

logged decisions in a matter of weeks; ~56,000 words of decision log

~1,300

decision citations across the tree — code citations outnumber prose

~500 pages

of design docs (~133,000 words) for a repo this young

20 + 62

LLM-reviewed invariant sets, and inventoried commitments each mapped to its guards

The numbers describe discipline, not architecture. Thirteen of the twenty invariant sets gate CI, fail-closed: a model outage does not silently pass a security review.

The costs are real, and conceding them is part of the practice. Spontaneity dies: vibe-coding a random idea into a repo like this is genuinely hard, and the mitigations — draft modes, a plans directory exempt from the citation police, advisory-first checks — soften but do not remove the tension. LLM reviewers cry wolf until you tame them with the protocols above. A dozen model calls per PR is real money and real minutes. I report all of this and keep the regime anyway, because for production systems maintained by fleets of amnesiac contributors, maybe spontaneity at the merge boundary was never the thing to optimize for.

How you would start

You do not need the whole apparatus on day one, and you do not need a greenfield repo. The working hypothesis — not yet fully tested — is that this retrofits better than you would expect: mine the existing docs for the inventory, let the coverage sentinels nag the registry into existence, and begin with three moves.

The Starting Moves

Greenfield is easy: every PR under the regime from day one. For an existing repo, start here and let the sentinels nag the rest into existence.

01

A decision log with teeth

One file per decision, dated, append-only in the accounting sense: reversal happens by counter-decision, never by edit. Add a citation checker in CI — cite a decision that does not exist, or delete one that is still cited, and the build fails.

02

One structural meta-test

Pick a single completeness rule and enforce it deterministically: every new top-level directory needs a coverage claim or a written exemption, in the same PR. New code cannot appear without a coverage decision.

03

One LLM check, advisory first

Start with the consistency question: does this diff contradict a logged decision without recording a reversal? Keep it advisory until it earns trust; promote it to gating only when its precision does the persuading.

The anti-goal is documentation theater: prose that accumulates without enforcement. The litmus test — can a doc failure fail the build? If nothing red happens when the prose lies, you have built a diary, not a memory.

The provocation

Overdocumentation is what “self-documenting code” grows up into when the readers never sleep and never remember. We did not add process to slow the agents down; we added memory so they could go faster — and the memory turned out to have opinions about my commits too. I expected to resent that. Instead it is the most trustworthy collaborator I have: a codebase that remembers every choice we made, insists I either honor them or overrule them in writing, and extends exactly the same courtesy to me as to any agent passing through.

Write everything down. Then arm it.

About the Author

MH

Markus Hav

Markus Hav is Lead Researcher for Agents at Benque Max AI Lab in Finland, where he focuses on advancing autonomous AI systems and agent architectures. His work explores the boundaries between programmed behavior and emergent intelligence in AI agents. He also serves as Head of AI Automation at PostScriptum, applying cutting-edge agent research to real-world automation challenges.