Docs Automation Guide

Documenting a legacy system nobody understands: start with what the repo admits

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

The hard part of a legacy system is not that it is old, it is that nobody currently employed can tell you what it consists of. The two people who could have are gone, the wiki entry stops at a login screen, and the deploy is a script whose comments contradict it. Documenting it from scratch feels like an unbounded task, which is why it never gets started. It becomes bounded if you fix the order of operations.

7 min readFor whoever inherited the system everyone is careful around

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

170/20003 credits left
Try:

No account needed · Editable canvas, not a picture

Start with the files that cannot lie

Not all files in a legacy repository are equally honest. Comments lie, READMEs lie, and the docs directory usually describes an intention from 2018. The deployment descriptors do not, because if they were wrong the thing would not be running. Dockerfiles, compose files, Kubernetes and Helm manifests, infrastructure as code, CI workflows, and package manifests are the shortest path from a repository you do not understand to a list of what actually runs.

That is why a repository read should take files in that priority order, infrastructure as code first and prose last, up to a bounded budget of 40 files and 250KB, rather than trying to read everything. On a legacy codebase the bound is a feature: it forces the highest-signal files to the front instead of drowning them in twelve thousand lines of business logic.

The exclusions matter as much as the ordering on an old repository. Vendored directories, node_modules, target, dist, and __pycache__ are never candidates, and neither are lockfiles or binaries. A file above 400KB is skipped outright, and anything above 30KB is truncated with a visible marker so a reader can tell a cut tail from an absent one. Legacy repositories are exactly where those rules earn their keep, because they are where a checked-in dependency tree or a 40MB fixture would otherwise eat the entire budget.

A summary of the top three directory levels rides along with the selection, so you can see the shape of what was not read. On an unfamiliar repository that summary is often the single most useful artifact, because directory names are the last honest naming layer a codebase retains.

Nothing is executed

The read is a read: a read-only token on GitHub, GitLab, or Azure DevOps, no build, no deploy, no credentials into whatever the system connects to. That is what makes it usable on a system everybody is careful around. See how repository sync works.

The first artifact is a map, not a manual

Aim for something to be wrong about in public. A generated diagram plus a written description gets you a concrete draft in minutes, and a draft with three errors in it is a far better instrument than a blank page, because it converts an impossible request into an easy one.

The easy request is this: take the picture to the two people who have been there longest and ask them what is wrong with it. Nobody can write a system description from memory on demand. Everybody can correct a diagram. That asymmetry is the whole technique, and it is why the generated draft matters more than its accuracy.

Run that loop twice and stop. The map does not have to be complete to be useful, it has to be good enough that the next person starts from it rather than from grep.

What the source cannot tell you, and where to put it

Source explains what, never why. Why the retry count is seven. Which single customer the special case in the invoicing path exists for. Which table looks unused but is read by a quarterly job that lives in a different repository. Which failure is known, understood, and deliberately unfixed. That is the knowledge that leaves with people, and it is the reason legacy systems are frightening rather than merely old.

Put it on the map. Annotations placed next to the node they concern keep the context attached to the thing it explains, which is the difference between knowledge that gets found and knowledge that sits in a document nobody opens. A canvas with editable text and labelled edges holds that kind of note naturally.

Those notes survive the regeneration, which is the part that decides whether anyone bothers adding them. A node put on the canvas by hand carries no id from the source, so every later sync is told to keep it as the reader's own work rather than reconcile it away, and anything dragged into position stays there. The map accumulates what people know instead of being flattened each morning by the read.

This is also the part to do while the person who knows is still reachable. Generated documentation makes their time cheap to use: instead of asking them to write, you ask them to correct, and a thirty minute session over a diagram extracts more than a week of good intentions.

Point at the ref that is actually deployed

Legacy systems routinely have a main branch that is not what is running. There is a release branch, a hotfix branch that never merged back, or a tag from three years ago sitting in production. Documenting main in that situation produces an accurate description of something nobody is operating.

A repository connection tracks a specific branch or tag, and one connection can back several diagrams, so you can hold the deployed tag and the current main side by side. Reading the two next to each other is frequently the most informative half hour of the whole exercise, because the divergence is a list of what was changed and never released.

Once the map exists, keep it alive rather than declaring victory. The daily sync regenerates from the tracked ref and skips the work when the commit has not moved, which on a legacy system means it costs almost nothing and quietly captures the day somebody finally touches it.

FAQ

Where do you start documenting a system nobody understands?

With the deployment descriptors rather than the code. Dockerfiles, compose files, Kubernetes and Helm manifests, infrastructure as code, CI workflows, and package manifests describe what actually runs, and unlike comments and READMEs they cannot be silently wrong without the system failing.

Is it safe to point a documentation tool at a legacy production repository?

A source read is a read: read-only repository access on GitHub, GitLab, or Azure DevOps, with nothing built, run, or deployed and no credentials into the systems the code connects to. That is the whole requirement, which is what makes it usable on systems everyone is careful around.

What if the generated map has errors in it?

That is the intended starting state. Asking a long-tenured colleague to write a system description from memory rarely works, while asking them to correct a concrete diagram almost always does. Two correction rounds on a draft produce a better map than months of waiting for someone to write one.

How do we capture knowledge that is not in the code?

Put it on the diagram next to the component it explains, as annotations rather than in a separate document. Reasons behind retry counts, special cases tied to one customer, and known-but-unfixed failures stay findable when they sit beside the node, and disappear when they live in a file nobody opens.

Which branch should a legacy system be documented from?

The one that is actually deployed, which is often a release branch or an old tag rather than main. A connection tracks a specific branch or tag and can back several diagrams, so holding the deployed ref and main side by side shows exactly what was changed and never released.