---
title: How to maintain documentation over time
description: 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

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

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

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](/docs/guides/improving-docs).

## 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

[`reed check`](/docs/reference/cli/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](/docs/reference/diagnostics). The CI recipe, including
the two caches you need to restore for an offline build, is in
[Offline builds in CI](/docs/ship/ci-offline).

### 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](/docs/guides/linking) for the linking rules it
enforces.

### 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](/docs/guides/style-and-tone) covers what to encode.

### 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:

```sh
for f in $(git ls-files 'content/*.mdx'); do
  echo "$(git log -1 --format=%cs -- "$f") $f"
done | sort | head -20
```

An 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

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

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

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:

1. **Add a redirect.** Map the old path to the most relevant current page in `redirects`
   in `docs.json`. Reed writes each into a `_redirects` file, 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](/docs/site/redirects).
2. **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.
3. **Update navigation.** Remove the page from `navigation`. `reed check` fails on an entry
   that no longer resolves, so you cannot forget this step.
4. **Say so in the changelog** if the page was widely used. On Reed, a changelog is a page
   with `type: changelog` in 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

<AccordionGroup>
  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>
