← The ADLC library
SDLC vs ADLC · 9

Documentation that regenerates itself

We can now generate accurate descriptions of what code does, cheaply and continuously. That solves the smaller half of the documentation problem and makes the larger half more urgent.

Open the internal wiki of any company more than three years old and pick a page at random. There is a decent chance it describes a service that was decommissioned, references an endpoint that moved, or contains a setup guide whose first step is to install a version of something that no longer exists. Somewhere near the top there may be a note saying “this page may be out of date,” added by a well-meaning person who did not have time to fix it and did not want to delete it.

Everybody knows this. It is one of the few genuinely universal experiences in software. And the standard diagnosis has always been a discipline problem: engineers do not update the docs, because updating docs is boring and unrewarded and nobody checks.

I think the diagnosis is mostly wrong, and the reason it matters is that the fix follows from the diagnosis. The problem was never that people are lazy. It was that documentation was a manually maintained copy of something that changed independently of it. Any manually maintained copy of a moving thing drifts. That is not a character flaw, it is a property of copies. We have known this about caches for fifty years and somehow failed to apply it to prose.

Which leads to the interesting question of the moment: if the copy can now regenerate itself continuously and cheaply, is the problem solved?

Partly. And the part that is not solved becomes considerably more important, for reasons that have to do with where the knowledge went.

The two documentations

Almost every argument about documentation goes wrong because two completely different artefacts share the word.

Descriptive documentation answers “what does this do.” API references, module overviews, data models, sequence diagrams of a request path, setup instructions, configuration options. All of it is derivable from the code, in the strict sense that a sufficiently attentive reader with enough time could reconstruct every word of it from the repository. It exists because reading the code is slow, not because the information is absent.

Explanatory documentation answers “why is it like this.” Why we chose eventual consistency for this table. Why there is a hard-coded exception for one customer’s timezone. Why we do not use the framework’s built-in auth. What we tried in 2023 that failed. Which constraint that looks arbitrary is actually a regulatory requirement. None of this is derivable from the code, because it is about the alternatives that were considered and rejected, and rejected alternatives leave no trace in the artefact.

Generation is transformative for the first category and nearly useless for the second. Descriptive documentation was always a compression of the source, so producing it automatically from the source is exactly the right shape of problem. Explanatory documentation is a record of human deliberation, and no amount of reading the code recovers a decision that was made in a conversation.

Once you separate them, the strategy is obvious. Stop hand-maintaining anything descriptive. Redirect all of that effort, plus more, into the explanatory kind.

Descriptive: what does this do

  • API references, module overviews, data models
  • Setup instructions, configuration options
  • Always a compression of the source
  • Generation is transformative here
  • Stop hand-maintaining any of it

Explanatory: why is it like this

  • Why eventual consistency for this table
  • Why one customer has a timezone exception
  • Never derivable from the code
  • Generation is nearly useless here
  • Redirect all the freed effort into it
Two completely different artefacts sharing one word, which is why almost every argument about documentation goes wrong.

Generate the descriptive layer, and treat it as a build artefact

The operational principle: if a document can be generated from source, it must be generated from source, and it must never be edited by hand.

The “never edited by hand” part is what teams get wrong. A generated document that someone has manually improved is worse than either option alone, because the next regeneration either destroys the improvement or the generation gets disabled to preserve it, and now you have a hand-maintained copy again with extra steps. Generated documentation should live where build outputs live, carry a visible marker that it is generated, and be regenerated on every merge so that staleness has a maximum age measured in minutes.

A few consequences follow.

Versioning changesDocumentation for 3.2 is generated from the tag, not maintained as a separate branch of prose.
Coverage becomes measurableCheck every public interface has generated docs and fail the build. Never enforceable when a human had to write it.
Prose duplication becomes a bugAnything explained in both the generated reference and a hand-written guide will drift. The guide should link.
If a document can be generated from source it must be generated from source, and never edited by hand. A generated document someone improved manually is worse than either option alone.

Versioning changes. You do not maintain documentation for version 3.2 as a separate branch of prose. You generate it from the tag. Historical documentation becomes a function of the repository rather than an archive to maintain.

Coverage becomes measurable. You can check that every public interface has generated documentation, and fail the build if not. That was never enforceable when a human had to write it, because the enforcement was unreasonable. It becomes entirely reasonable when the cost is machine time.

Prose duplication becomes a bug. If a concept is explained in the generated reference and also described in a hand-written guide, the hand-written copy will drift. The guide should link, not restate. This is unglamorous editorial discipline and it is where most documentation systems rot.

There is a real question about whether generated prose is good, and the honest answer is that it is competent and characterless. It will accurately describe a function’s parameters and will not tell you that this function is the one everybody gets wrong. For reference material, competent and accurate beats elegant and stale by an enormous margin. For anything a newcomer reads to build a mental model, it is thin, and a human should write it.

The explanatory layer is where the crisis is

Now the part that matters more.

Explanatory knowledge has historically lived in people. Not in documents. In the four engineers who have been here three years, who know that the retry logic in the payment service looks wrong but is correct, and that the customer sync runs at 3am for a reason involving a partner’s batch window. It transmitted through code review, through pairing, through someone leaning over and saying “ah, do not touch that.”

Two things are happening to that transmission mechanism at once, and they compound.

The first is that the volume of code is going up while the number of people who understand it is not. A codebase where a large fraction of changes were composed by agents has proportionally less human-held rationale per line. The knowledge was never in the code. It was in whoever wrote it. If nobody wrote it, in the sense of holding the intent while composing, then there is less of it in existence at all.

The second is that the review channel is thinning. As argued in the piece on sampling, human review of every change is not sustainable at volume, and human review was one of the main channels by which tacit knowledge propagated. Fewer people read fewer diffs, so fewer people absorb the reasoning.

So the supply of explanatory knowledge falls at the same time the amount of system to explain grows. That is the actual documentation crisis of the ADLC, and it is not addressed by anything generated, because the thing that is scarce is precisely the part that cannot be derived.

The response has to be to capture rationale at the moment of decision, which means it has to be captured during specification rather than after implementation. When someone writes acceptance criteria that say internal tokens are exempt from rate limiting, the reason for that exemption is in their head at that moment and nowhere else, and it costs one sentence to record. A week later it costs a conversation. A year later it costs an incident.

Architectural decision records are the obvious vehicle and they are a genuinely good practice that most teams adopt for two months. What makes them stick, in the teams where they do stick, is that they are short (a page, not a document), they are stored with the code rather than in a wiki, and they are written as part of the work rather than as a separate deliverable. The ones that die are the ones that became a template with nine required sections.

Decision provenance is the underrated version

There is a stronger version of the same idea that fewer teams do, and it is worth naming.

If every change links to its work item, and every work item states its criteria, and criteria record the reasoning behind non-obvious constraints, then the codebase acquires a queryable decision history. Not a wiki page about the payment service, but the ability to take a specific line of code, find the change that introduced it, find the work item, and read why that constraint existed. That is a much more useful artefact than any document, because it is indexed by the thing you are actually looking at when you have the question.

This is one of the quieter benefits of the linkage that a control layer like GroundTruth maintains: the chain from line to change to work item to intent stays intact automatically rather than depending on someone following a branch naming convention. The documentation value of that chain is usually discovered later than the compliance value, and it is arguably larger.

Where this breaks down

The limits, and the first one is significant enough that it undercuts a chunk of the enthusiasm above.

Generated descriptions can be confidently wrong. A generated description of a function is an inference about intent from an implementation, and inference can be mistaken. A function named validateEmail that does not actually validate will be documented as validating. Generated documentation is accurate about structure and merely plausible about purpose, and readers will not distinguish. In some ways this is worse than stale documentation, because stale documentation is often visibly stale (it mentions things that no longer exist) whereas confidently wrong generated documentation looks perfectly current.

Regeneration removes the drift signal. There was information in staleness. A wiki page that had not been touched in two years told you something real about the system’s neglect, and a document that suddenly disagreed with the code told you a change had unexpected reach. Continuous regeneration eliminates the disagreement, and with it a genuine, if crude, detector.

Nobody reads generated documentation. Or rather, people read it the way they read a dictionary, in targeted lookups. It does not build a mental model. A newcomer who reads every generated page will know the parts and not the system. The hand-written narrative overview remains the single most valuable document a team can produce and is exactly the one that generation does not touch, so the effort saved on reference material should be redirected there rather than banked.

Capturing rationale at decision time is a discipline problem, and I opened this piece by dismissing discipline problems. That is a fair charge. Everything I have said about explanatory documentation asks people to do a small unrewarded thing consistently, which is precisely the ask that failed for descriptive documentation for thirty years. The reasons to think it might work better are weak ones: it is smaller, it is attached to work people are already doing, and the pain of not doing it arrives faster now. I would not describe those as strong reasons. If you asked me to predict, I would say most teams will not do this and will absorb the cost as a slow rise in the price of understanding their own systems.

Written rationale goes stale too. A decision record explaining why we chose eventual consistency remains on file long after the constraint that forced it disappeared. The record becomes an argument against revisiting a decision that should be revisited, which is a real and observed failure mode. Decisions need dates and, ideally, the conditions under which they should be reconsidered, and almost nobody writes that part.

And the whole model assumes the code is the source of truth. For a system whose real behaviour depends on configuration in a vendor console, on data in a warehouse, on models trained elsewhere, generation from source captures a fraction of the system. The gap between what is documented and what is running is exactly the part nobody generates.

The takeaway

Documentation was two things sharing a word. The descriptive half was always a manually maintained copy of the source, and copies drift; generating it continuously is straightforwardly correct, provided you treat it as a build output that no one edits.

The explanatory half is not derivable from anything, it lived in people, and both of its transmission channels (composing the code and reviewing each other’s) are getting thinner at the same time as the system to explain gets bigger. That is the actual problem, it is not solved by generation, and the only real answer is capturing rationale at the moment of decision, which is a discipline the industry has a poor track record with.

This series has spent nine pieces arguing that large parts of the SDLC were assumptions rather than laws, and that agents invalidated them. That is a one-sided case, made deliberately. The last piece makes the other one: what the SDLC understood correctly, which of its practices are load-bearing for reasons that have nothing to do with who writes the code, and why the biggest risk right now is over-correction rather than resistance.