Docs-as-Code

Documentation decays for a structural reason: it lives somewhere the code doesn't. When docs are in a wiki, a shared drive, or a separate CMS, updating them is a separate task with a separate workflow, done by someone who has to remember. Nobody remembers. Six months later the getting-started guide references a flag that no longer exists, and the only signal is a confused user.

Docs-as-code removes the distance. Documentation lives in the repository next to the code it describes, is written in Markdown, is reviewed in the same pull request as the change, and is built and validated by CI. A change and its documentation land together or neither lands. The workflow is the one engineers already have, which means the cost of updating docs drops to nearly zero — and that cost is the entire reason documentation goes stale.

The other half is automation. Anything mechanically derivable — API schemas, CLI help, config options, type signatures — should be generated rather than transcribed, because transcribed reference material is guaranteed to drift.

TL;DR

Quick Example

A documentation pipeline that fails the build on stale content:

The fourth CI step — verifying generated reference is current — is the one that most reliably catches drift, because it fails the build the moment someone changes an API without regenerating the docs.

Core Concepts

The workflow

Compare with the wiki model: change code, ship, remember to update the wiki, don't, discover six months later. The difference isn't discipline — it's that the docs-as-code workflow makes the correct behavior the path of least resistance.

The four modes

This is the Diátaxis framework, and it's the most useful structural idea in technical documentation. Its value is diagnostic: when a page feels wrong, it's usually because it's trying to be two modes at once — a tutorial interrupted by exhaustive option tables, or a reference page that keeps digressing into rationale.

Organize the site by these modes and the "where does this go?" question mostly answers itself.

Generated reference

Anything a machine can derive, a machine should derive:

Commit the generated output and verify it's current in CI, or generate at build time and never commit it. Either works; the failure mode is generating sometimes, which produces a file that's authoritative-looking and months old.

Prose linting

Vale catches passive voice, weasel words, and inconsistent terminology. The terminology rule is the one that earns its keep — a codebase where the same concept is called three things across the docs is genuinely confusing, and no human reviewer catches it consistently. See Documentation Style Guides.

Versioning

Users on v2 need v2's documentation. Two approaches:

Directory-based is simpler for most teams. Whichever you choose, always publish a latest alias, show a prominent banner on outdated versions, and set canonical URLs so search engines send people to the current page rather than a three-year-old one.

Publishing

Deploy on merge to a static host — Vercel, Netlify, Cloudflare Pages, or GitHub Pages. Preview deploys per pull request are worth setting up early: reviewing rendered documentation catches formatting and navigation problems that a Markdown diff hides.

Best Practices

Require docs in the same pull request

Make it a review checklist item, or automate it: if a PR touches a public API and no docs/ file changed, flag it. Splitting docs into a follow-up ticket means the follow-up is deprioritized, every time.

Keep documentation next to what it describes

Component docs beside the component, API docs beside the handler, architecture docs in the service's directory. The physical distance between code and documentation predicts how often they diverge.

Fail the build on broken links

Link rot is constant and invisible. A link checker in CI is a few minutes of setup and permanently prevents the most common documentation defect. Include anchor fragments — a link to a heading that was renamed is just as broken as a 404.

Test your code samples

A sample that doesn't compile is worse than no sample, because the reader assumes the fault is theirs. Tag runnable blocks and execute them in CI. This is more work than the other checks and catches the errors that most damage trust.

Write for a specific reader in a specific mode

"A backend engineer who has our SDK installed and needs to add webhook handling" produces a much better page than "documentation about webhooks." Naming the reader also makes it obvious which of the four modes you're in.

Prune and archive

Documentation for removed features, superseded approaches, and abandoned experiments actively misleads. Delete it or mark it clearly archived with a pointer to the current approach. A smaller, accurate set of docs is more valuable than a comprehensive one where some pages are wrong.

Instrument the docs site

Search queries with no results tell you exactly what's missing. Page views tell you what matters. A thumbs-up/down widget with an optional comment costs nothing and produces a steady stream of specific, actionable reports.

Include diagrams as text

Mermaid, D2, or PlantUML render in most doc tooling and diff properly in review. A PNG exported from a whiteboard cannot be updated by anyone but its author, which means it never gets updated. See Technical Diagrams.

Common Mistakes

Documentation in a separate repository or wiki

Hand-writing generated reference

Mixing documentation modes

No link checking

Untested code samples

Publishing only one version

FAQ

Doesn't this put too much burden on engineers?

It reduces the burden. The alternative — a separate documentation workflow in a separate tool — is more work per change, not less, which is precisely why it doesn't get done. Engineers writing a first draft in the PR they're already opening is the cheapest possible moment. A technical writer's highest-value contribution is then structure, consistency, and the reader-facing surfaces, not being the sole author. See Technical Writing.

Markdown or something more powerful?

Markdown, with MDX or a directives extension where you need components, tabs, or callouts. It's readable as plain text, diffs well, and every engineer already knows it. reStructuredText is more capable and much less widely known — reasonable in the Python ecosystem where Sphinx is standard, and a barrier elsewhere. AsciiDoc sits in between and has a small but committed following.

How do we handle documentation for multiple products?

Either a monorepo with a docs site per product sharing components and style config, or a single site with clear product sections. What matters is one style guide, one terminology list, and one search index — users don't care about your repository structure and will search across everything.

Should the generated output be committed?

Either commit it and verify currency in CI, or generate at build time and gitignore it. Committing makes the docs readable in the repository and reviewable in diffs, at the cost of merge conflicts in generated files. Build-time generation is cleaner and means the source isn't browsable on GitHub. Both work; generating inconsistently does not.

How do we know if the documentation is any good?

Instrument it: zero-result search queries (what's missing), page views (what matters), time on page and bounce (whether it's answering the question), and a feedback widget (specific complaints). The strongest qualitative signal is support tickets — a question asked repeatedly is a documentation gap, and tracking which docs pages reduce ticket volume tells you where to invest.

Does AI-generated documentation fit this workflow?

It fits well for drafts and for the mechanical layer — expanding terse notes, generating reference from code, keeping tone consistent, spotting gaps. It fits badly for intent and tradeoffs, which aren't recoverable from source. The workflow protects you here: AI-drafted content goes through the same PR review, the same link checking, and the same sample testing as anything else, so the verification step is already in place.

Related Topics

References