How to maintain documentation over time
Ownership, change-driven reviews, checks on every pull request, and a plan for retiring pages.
Documentation starts drifting the day it ships. Features change, flags get renamed, an example stops compiling. Nobody decides to let the docs rot; it happens by default, one unreviewed release at a time. Readers follow a stale step, hit an error, and stop trusting the rest of the page.
Maintenance is not a heroic quarterly cleanup. It is a small number of habits wired into
the workflow you already have, so that accuracy is the path of least resistance. On Reed
that workflow is git: your docs are MDX files and a docs.json in a repository, and every
change goes through the same branch-and-review loop as code. That makes most of what
follows easier to enforce than it would be anywhere else.
Assign ownership
Section titled “Assign ownership”Every section of your docs needs an owner. An owner is not necessarily the person who writes every word. It is the person who is accountable when the page is wrong.
Three models work:
- Centralized. A docs team or a technical writer owns everything. This fits small products, where one team can hold enough context to keep every page correct.
- Distributed. Each product or engineering team owns the docs for its own area. This fits large products with specialized domains. It needs a coordinator, or the sections drift apart in tone and structure and nobody notices the gaps between them.
- Hybrid. A docs team owns the shared spine — the quickstart, navigation, style — and domain teams own the reference and guides for their features.
Pick one and make it visible. Because your docs live in a repository, the obvious place
is a CODEOWNERS file: map content/api/ to the API team and content/guides/ to the docs
team, and your git host requests the right reviewer on every pull request automatically.
Ownership that lives only in someone’s head does not survive a reorg.
Review on change, not on schedule
Section titled “Review on change, not on schedule”The review that catches the most drift is the one triggered by the product change itself.
- Update docs in the same pull request as the code. The engineer who changed the behavior knows exactly what changed, today. In a month they will not.
- Put docs on the launch checklist. A feature is not shipped until its page is.
- Gate user-facing tickets on a docs check. If the behavior a user sees changed, the ticket does not close until someone has confirmed the docs match.
This beats a calendar because the context is fresh and the cost is small. Doing it in the same pull request also means a reviewer sees the code and the prose side by side, which is where contradictions are easiest to spot.
Scheduled reviews still have a job
Section titled “Scheduled reviews still have a job”Some content is not tied to any single feature — overviews, concepts, troubleshooting — and it drifts anyway. A light schedule catches that:
- Monthly: re-read your highest-traffic pages. They affect the most readers, so they earn the most attention.
- Quarterly: audit the pages that generate support tickets or complaints.
- Annually: a full pass for outdated examples, superseded approaches, and pages that no longer describe how the product actually works.
“Highest-traffic” assumes you measure traffic. Reed does not, on its own; it emits the script for an analytics provider you configure. See Improving docs.
Let checks catch what they can
Section titled “Let checks catch what they can”Automation cannot tell you whether a paragraph is true. It can tell you, on every pull request and without anyone remembering to ask, about the mistakes that are mechanical.
Run reed check on every pull request
Section titled “Run reed check on every pull request”reed check validates docs.json against the schema and
runs static checks over your content: navigation entries resolve to real pages, components
exist and carry their required props, referenced assets are present. It never touches the
network and needs no setup, so it is safe to run in any CI job. Among the diagnostics it
reports:
| Code | Catches |
|---|---|
DS-S06 |
An unknown component — usually a typo, or one pasted from another platform. |
DS-A01 |
An image with no alt text. |
DS-A02 |
An H1 in the page body, or a skipped heading level. |
DS-D01 |
More than 40 components on a single page. |
The full list is in Diagnostics. The CI recipe, including the two caches you need to restore for an offline build, is in Offline builds in CI.
Check prose links yourself
Section titled “Check prose links yourself”reed check validates docs.json and your navigation. It does not check
links you write inside page prose, and it does not add your base prefix to them. A link
to /quickstart on a site mounted at /docs compiles cleanly and 404s in production.
So add a link check of your own. This site runs a short Node script before every build
that scans each .mdx file for markdown link destinations and href attributes, fails on
any root-relative link that is missing the base prefix, and fails on any link whose target
page file does not exist. It skips inline code, so a page can still show a wrong link as a
counter-example. Under eighty lines of script, and a whole class of broken links cannot reach
production. See Linking for the linking rules it
enforces.
Lint the prose
Section titled “Lint the prose”A prose linter such as Vale checks terminology, passive voice and house style on every pull request. It will not make writing good, but it keeps a hundred small inconsistencies from accumulating. Style and tone covers what to encode.
Surface stale pages from git history
Section titled “Surface stale pages from git history”Reed has no “last reviewed” tracker. It does not need one: git already knows when each file last changed. List your oldest pages first:
for f in $(git ls-files 'content/*.mdx'); do echo "$(git log -1 --format=%cs -- "$f") $f"done | sort | head -20An old date is not proof a page is wrong. It is a list of pages nobody has looked at in a while, which is exactly the review queue you want. Run it in a scheduled CI job if you want it to arrive without anyone asking.
Keep generated reference generated
Section titled “Keep generated reference generated”If your API reference comes from an OpenAPI spec, regenerate the spec as part of your release pipeline and commit it with the release. A reference rendered from the real spec cannot fall behind the API; one maintained by hand always does.
Know when to rewrite
Section titled “Know when to rewrite”Incremental edits stop working past a certain point. A page that has absorbed years of caveats and workarounds becomes harder to fix than to replace.
Rewrite, rather than edit, when:
- readers keep reporting confusion after several rounds of fixes;
- the page has grown to cover several distinct topics that should be separate pages;
- the product changed so much that the page’s structure no longer maps to how the feature works;
- more than half of it is edge cases, exceptions and “unless” clauses.
Start a rewrite with an audit, not a blank file. Write down what is missing, what is misleading, and what is redundant. Then rewrite in a focused sprint, on a branch, and merge it in one pull request so reviewers see the new page as a whole.
Remove what no longer belongs
Section titled “Remove what no longer belongs”A wrong page is worse than a missing one. A missing page sends readers to search or support. A wrong page sends them down a path that fails, and costs you their trust in every other page. If content is inaccurate and you cannot fix it now, take it down.
When you remove or move a page:
- Add a redirect. Map the old path to the most relevant current page in
redirectsindocs.json. Reed writes each into a_redirectsfile, which Cloudflare Pages and Workers serve as a real 301 or 302. It also writes a page at the old path that meta-refreshes to the new one and carries a canonical link to it, the fallback on hosts that do not apply_redirects, including Netlify unless you add a forced rule. See Redirects. - Fix inbound links. Search your content for the old path and update every link. The link check above fails the build on the ones you miss.
- Update navigation. Remove the page from
navigation.reed checkfails on an entry that no longer resolves, so you cannot forget this step. - Say so in the changelog if the page was widely used. On Reed, a changelog is a page
with
type: changelogin its frontmatter and one dated##heading per entry. There is no RSS feed or subscriber notification, so the entry is only as visible as the page.
Frequently asked questions
Section titled “Frequently asked questions”How do I get engineers to update documentation when they ship?
Make it a requirement, not a request. Put documentation in your definition of done, and put the docs change in the same pull request as the code change. With a CODEOWNERS entry, the right docs reviewer is added to that pull request automatically. Engineers who see that stale docs turn into support tickets routed back to them rarely need a second reminder.
How do I handle documentation for a deprecated feature?
Keep the page, and mark it. Put a warning callout at the top that says the feature is deprecated, when it will be removed, and what to migrate to. Do not delete the page until the feature is actually gone, because readers on older versions still rely on it. Once it is removed, delete the page and redirect its path to the migration guide or the replacement.
What is the minimum viable process for a small team?
Two habits cover most of the value. Update docs in the same pull request as the code, and run reed check plus a prose link check on every pull request. Together they stop the two most common kinds of drift, stale instructions and dead links, without any dedicated tooling. Add scheduled reviews as the product grows.
How do I prioritize documentation debt?
By impact on readers, not by how messy a page feels to the team. A stale page with thousands of monthly views and a steady stream of support tickets outranks an ugly page nobody reads. Rank pages by traffic from your analytics provider, then cross-reference with support ticket topics, and fix the pages readers actually hit.