Insight

Never Hand-Maintain What the System Can Derive

The architecture doc says the service owns pricing. The service says something else now. One of them was hand-maintained, and it is not the one that runs in production.

Alaa Almallah 8 min read

The architecture doc is proud of its diagram. It lists every service and, beside each one, the facts a reader might want: what it owns, what it calls, how many endpoints it exposes. The diagram was drawn by hand, carefully, eight months ago.

The service now called pricing-core was renamed twice since. The endpoint count drifted from eleven to nineteen. Two services in the diagram were merged and one was never built. None of this is malice; it is just that the code changes every day, in a hurry, and the document changes on a schedule that does not exist.

Every reader now faces two authorities. One of them runs in production. The rule is simple and unforgiving: never hand-maintain what the system can derive.

The rule, stated precisely

If a fact about the system is computable from the system, storing a hand-typed copy of that fact creates a second source that will disagree with the first, silently, at the speed of the busier side.

Usually the busier side is the code. Code changes under deadline; documents change under guilt. So the copy decays monotonically, and the disagreement it eventually carries is invisible, because a hand-typed fact looks exactly as authoritative on the day it is wrong as on the day it was written. There is no freshness signal on a paragraph. That is the mechanism worth internalizing: derived facts carry their own truth (they are regenerated), and hand-maintained facts carry a costume of truth that never fades.

The test for any fact in a tracker, doc, or config is one question: can this be computed from something the system already is? If yes, computing it is not an optimization. It is the only version that survives. Endpoint counts, service lists, module dependencies, coverage numbers, schema fields, environment URLs: all computable, all routinely re-typed by hand somewhere.

The disagreement is silent because both sides look fine

This is where the failure differs from ordinary bugs. When the doc and the code disagree, nothing crashes. Both artifacts render beautifully. The cost is paid by whoever trusts the wrong one, later, in a meeting, confidently.

A new engineer reads the architecture doc and spends a day routing a request through a service that no longer exists. A team plans capacity from a hand-updated endpoint inventory that stopped being true at the third rename. A tracker carries a hand-maintained module field, and over a year the field and the repository drift apart until one of them is fiction. Nobody gets an alert, because the system that could alert, the repository, does not know the copy exists.

The drift compounds fastest where the copy is convenient to update early and inconvenient later. Week one, everyone updates it; week nine, deadlines win; month six, the updates are ceremonial; month twelve, the artifact is a museum that still says welcome. At that point, "the docs are wrong" becomes team folklore, and the organization quietly reroutes all its trust into heads and Slack threads, which is where truth goes when its official homes rot. That rerouting is the real cost: not the stale fact, but the learned distrust of every artifact, including the ones still true.

This is why the failure belongs beside Most Technical Debt Is Deferred Judgment, Not Bad Code. The hand-maintained copy is deferred work wearing a document's clothes: the team deferred writing the generator, or deferring was never even decided, it was just easier to type the list. And the diagnosis echoes The Stack Is Not the System: the diagram is not the architecture. Confusing the drawn copy with the running thing is the original error.

Derivation, projection, and the honest residue

The fix has three levels, in ascending effort.

Generate. The fact is computed and refreshed on a schedule, into a build artifact, a generated doc section, or a nightly export. Endpoint counts from the route table. Service lists from the repository layout, via whatever semantic tooling already indexes it. The artifact carries a generation timestamp, which is the freshness signal hand-typed facts can never wear. This is Date-Stamp the Numbers for internal structure: the timestamp is what makes the fact checkable.

Project. Where two views of one truth are needed, both are derived from one store, never synchronized from each other. The treemap and the story map over one work tree, as in The Roadmap and the Codebase Are Different Trees: two lenses, zero copies. Projection is derivation applied to views rather than facts.

Label. Some facts genuinely cannot be derived: intent, rationale, why the system chose this shape and rejected that one. Those belong in documents, hand-written, permanently. The rule does not say documents are obsolete. It says documents should carry the non-derivable layer, decisions and reasons, while the derivable layer regenerates underneath them. The runbook that explains why the migration runs before the check is precious. The runbook that re-types the current list of checks is a time bomb with a table of contents.

What this is not

Not an argument for generating everything. Generated documents have their own failure mode: comprehensive, current, and unread, because generation optimizes for coverage and readers need judgment. The division of labor is the point: derived facts for what the system is, human prose for why it is and what to do about it.

Not a claim that hand-maintained means worthless. Early in a system's life, a hand-drawn diagram is the right tool; there is not yet enough system for derivation to read. The rule activates when the copy outlives the convenience: the moment the fact becomes checkable against the system, checking by hand stops being care and starts being debt.

And not a tooling manifesto. The generator can be a forty-line script. The expensive part is not the tooling; it is admitting that a document the team liked writing was always going to rot.

The move

Run the copy audit on one artifact this week:

  1. Take the most-trusted internal document or tracker view. List every fact it states that the system could compute: names, counts, lists, paths, owners-by-repository.
  2. Mark each fact: generated, derivable-but-hand-typed, or genuinely non-derivable. Expect the middle column to be the majority. That column is the rot schedule.
  3. Replace the middle column, one fact at a time, oldest and most-drifted first. A generated section inside a hand-written doc is a legitimate intermediate step.
  4. Leave the non-derivable residue alone and make it better: that layer, reasons and decisions, is what documents are for.

Take-home

  • Find the most-copied fact in your organization, the one re-typed in three docs and a tracker. Derive it once, and let the copies become projections.
  • Every hand-maintained fact should either become generated or declare itself non-derivable, intent, rationale. The middle category is silent decay.
  • Timestamp generated artifacts. A freshness signal is the difference between a fact and a costume.

Related reading:

If your team has stopped trusting its own documentation and reroutes truth through heads and chat, the docs are not the problem; the hand-maintained facts inside them are. If you want help splitting the derivable from the non-derivable before the next rename season, book a discovery call.

FAQ

Why should you never hand-maintain derivable facts?

Because a hand-typed copy of a computable fact is a second source of truth that disagrees with the first, silently, at the speed of the busier side, which is usually the code. A wrong paragraph looks exactly as authoritative as a right one; there is no freshness signal. Derived facts regenerate, so they carry their own truth and a timestamp.

What documentation should stay hand-written?

The non-derivable layer: intent, rationale, rejected alternatives, operating philosophy, why the system chose its shape. Documents should carry decisions and reasons while the derivable layer, names, counts, lists, dependencies, regenerates underneath them. Re-typing what the repository already knows is what rots.

How do you audit for hand-maintained copies?

Take your most-trusted artifact and list every fact it states that the system could compute. Mark each one generated, derivable-but-hand-typed, or genuinely non-derivable. The middle column is your decay schedule. Replace it one fact at a time, most-drifted first, starting with a generated section inside the existing document.

What is the difference between derivation and projection?

Derivation computes a fact from the system on a schedule: an endpoint count regenerated nightly from the route table. Projection applies the same principle to views: two dashboards reading one store, so there is nothing to synchronize. Both eliminate copies; derived facts under documents, projected views under lenses.

Part 4 of 6 · The Internal Systems Series

The shape of the systems inside a team: syncs that become third systems, dashboards nobody opens, taxonomies without death rules, hand-maintained copies, tool limits posing as design goals, and maps that show attention.

Related