All posts
Engineering8 min read

Why Documentation Drift Happens (And How to Fix It)

Docs rarely go stale because engineers are lazy. They drift because of how documentation is structured, owned, and checked. Here is what causes drift, how to detect it, and how to stop it for good.

By the Benlon team

Every engineering team has a page that was correct once. The endpoint it describes still exists, but a field was renamed. The runbook still has the right steps, but the dashboard it links to moved. The architecture diagram is beautiful and is missing the three services added since it was drawn.

That gap between what the documentation says and what the system actually does is documentation drift. It is not a discipline problem. Drift is the predictable result of how most teams structure, own, and verify their docs, which also means it can be fixed structurally.

What drift actually looks like

Drift is rarely a page that is entirely wrong. It is usually a small mismatch that costs someone an hour:

  • A request field is renamed or becomes required, and the API reference still shows the old shape.
  • A default value changes in code, but the configuration guide still documents the previous one.
  • A deprecated flow is still the first result when someone searches the wiki.
  • A runbook references a feature flag, queue, or dashboard that no longer exists.
  • An architecture diagram omits a service that now sits in the critical path.

Each one is minor on its own. Together they teach engineers that the docs cannot be trusted, and once that happens people stop reading them, stop updating them, and go back to asking in Slack.

Why it happens

  1. Docs live somewhere other than the code. When documentation sits in a wiki and the change sits in a pull request, reviewers see the code diff and never the doc diff. Nothing in the workflow connects the two.
  2. Nothing fails when a doc becomes wrong. Code has compilers, type checkers, and tests. A stale doc produces no red build, no alert, and no error. It just quietly becomes incorrect.
  3. Ownership is diffuse. "Everyone owns the docs" usually means nobody does. Without a named owner, updates happen only when someone is personally inconvenienced enough to fix them.
  4. Docs are written for a moment. Most documentation is written for a launch, a migration, or an onboarding push. Writing gets recognized; maintaining rarely does.
  5. Behavior changes outside the repository. Feature flags, infrastructure, and configuration change without a code diff at all, so even a careful reviewer would not know a doc is affected.

How to detect drift

You cannot fix what you cannot see. The goal is to turn "this doc might be wrong" from a vague feeling into a concrete signal.

Tie docs to code identifiers. A section that documents POST /v1/invoices or the InvoiceCreated event should be linked to that endpoint or type, not just mention it in prose. When the identifier changes, the linked sections become suspects automatically.

Generate reference material from the source of truth. Endpoints, request and response schemas, event payloads, and configuration options already exist in machine-readable form in OpenAPI specs, protobuf definitions, and type declarations. Reference docs built from those cannot drift, because they are regenerated on every change.

Put a check in the pull request. Even a crude CI rule is better than none. This one fails a pull request that changes the public API contract without touching anything under docs/:

.github/workflows/docs-drift.yml
name: docs-drift
on: pull_request

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Require a docs change when the API contract changes
        run: |
          changed=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
          if echo "$changed" | grep -q '^api/openapi.yaml
#x27; \ && ! echo "$changed" | grep -q '^docs/'; then echo "::error::api/openapi.yaml changed but nothing under docs/ did." exit 1 fi

It is blunt, and it will occasionally ask for a docs change that is not needed. That is fine: the point is to make the reviewer ask the question.

Track freshness explicitly. Add a "last verified" date and an owner to runbooks and guides, and review anything that has not been verified within a set window. A visible date alone changes how much readers trust a page, in both directions.

How to fix it for good

Detection buys time. The durable fix is to change what humans are responsible for.

  • Split reference from narrative. Reference material (endpoints, schemas, events, dependencies, configuration) should be generated and regenerated by machines. Narrative material (why a system is designed this way, trade-offs, how-to guides) should be written by people.
  • Keep the human part small and owned. Once reference docs maintain themselves, the remaining narrative pages are few enough to have real owners, and owners can be listed right on the page.
  • Delete aggressively. A wrong doc is worse than a missing one, because it costs time and erodes trust in every other page. If nobody will own a page, archive it.
  • Make drift visible after every merge. A short list of "pages that reference something that just changed" is far more actionable than a quarterly documentation cleanup.
Drift is a systems problem, not a people problem. Teams that stop drift do not write more documentation. They write less, and connect what they do write to the code it describes.

Where Benlon fits

This is the problem we built Benlon to solve. Benlon builds a typed model of your system from your repositories and API contracts, generates reference documentation from it, and links every page to the code it describes. When a merge changes an endpoint, type, or event, generated sections update automatically and human-written sections that mention what changed are flagged for review, so drift shows up in the pull request instead of in an incident.

If you want to see it on your own codebase, the getting started guide walks through connecting a first repository.

See living documentation on your own codebase

Connect a repository in minutes. Read-only by default.