Docs & Code Guide

Documentation SLAs: a review-age table by doc type and the CI gate that enforces it

A documentation SLA is a maximum age since last verification, set per document type, with a named owner and an enforcement mechanism. It is one clause of a broader freshness policy, the clause with teeth. Uniform SLAs fail because doc types decay at different speeds and their wrongness costs different amounts, so the useful artifact is a table: which type, what age, verified how, and what happens on breach.

7 min readFor docs owners who want freshness enforced, not requested

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

222/20003 credits left
Try:

No account needed · Editable canvas, not a picture

SLA versus policy

A freshness policy answers the broad questions: what documentation must exist, who owns each part, how ownership transfers, what verified means. The SLA is the enforcement clause inside it: a number, per doc type, that a script can check. Keep the two separate in your head because they fail differently: policies fail by being unread, SLAs fail by being unenforced. This page covers the table and the enforcement; the full policy template has its own guide.

The single most important design decision is that the SLA counts verification, not modification. A page edited yesterday to fix a typo is not thereby correct, and a page untouched for a year may be flawless. The clock resets only when the owner confirms the content against reality, which for a runbook means executing it, not reading it.

The review-age table, with the reasoning

Incident runbooks take the tightest SLA, 30 to 90 days plus a drill requirement, because their wrongness is priced in outage minutes and discovered under the worst possible conditions. The runbook maintenance guide covers what verification means there: execution against staging, a read-through does not reset the clock.

Onboarding docs come next: verify before each cohort starts, with a 90-day floor between cohorts, because new hires are the readers least able to detect wrongness and most damaged by it. Architecture overviews split by how they are produced: hand-written ones get 90 days, while generated views, diagrams drawn from the repository, carry no review age at all. Their SLA moves to the pipeline: regenerate on merge, and verify quarterly that the regeneration loop itself still runs. Decision records, ADRs, are exempt in the other direction: they are dated records of a decision, immutable by design, and putting them under a review SLA would corrupt what they are.

Reference material that readers consult rarely, style guides, vendor evaluations, gets 180 days or an explicit expiry date, after which the page is archived rather than trusted. An expiry that defaults to removal keeps the corpus honest without demanding review effort the content does not merit.

The CI gate

Enforcement that depends on people remembering is not enforcement. The mechanical version needs two pieces: a last_verified date in each doc's front matter, and a job that fails when any date exceeds its SLA. Run it two ways: on pull requests touching the docs tree, so contributors see breaches in context, and on a weekly schedule, so breaches surface even when nobody is editing.

The snippet below emits GitHub Actions error annotations, one per breach, and fails the job if any exist. Point the path and the day limit at each doc type from the table; the CI checks guide extends this pattern to link checking and generated-content verification.

#!/usr/bin/env bash
# Fail CI when a runbook's last_verified date exceeds the 90-day SLA.
set -euo pipefail
breaches=0
while IFS= read -r f; do
  d=$(awk -F': ' '/^last_verified:/{print $2; exit}' "$f")
  if [ -z "$d" ]; then
    echo "::error file=$f::missing last_verified front matter"
    breaches=$((breaches + 1))
    continue
  fi
  age_days=$(( ( $(date +%s) - $(date -d "$d" +%s) ) / 86400 ))
  if (( age_days > 90 )); then
    echo "::error file=$f::last verified $age_days days ago, SLA is 90"
    breaches=$((breaches + 1))
  fi
done < <(find docs/runbooks -name '*.md')
(( breaches == 0 ))

Sign-off and breach handling

Every SLA needs a name attached, and the cleanest mapping is directory-level ownership mirrored in CODEOWNERS, so the person who signs off on the docs for a service is the person reviewing its code. Sign-off is an action, not a state: the owner re-verifies the content and commits the new last_verified date, which makes the verification itself reviewable history.

A breach should open a ticket assigned to the owner, dated, and visible on whatever dashboard tracks the team's health metrics; the freshness metrics guide covers turning breach counts into a trend worth watching. At twice the SLA age, escalate to a harsher default: the page is archived or marked untrusted rather than left standing. An SLA whose only consequence is a red number becomes decoration within a quarter.

Generated views change the table

The expensive rows in the table are the ones where verification means human hours, and the way to cheapen them is to change how the document is produced. A hand-drawn architecture diagram under a 90-day SLA costs a review eight times a year per diagram. The same view generated from the repository inverts the cost: an agent connected to the Datadef MCP server redraws it in one command after infra merges, the wiki shows it through a live embed URL that follows edits within minutes, and the quarterly human check shrinks to confirming the loop ran.

Two honest boundaries. The live embed exists only for projects shared public, so confidential architectures keep the human SLA and an export discipline instead. And nothing watches the repository on its own: the on-merge regeneration is a CI job or an agent habit you wire once, which is exactly the kind of enforcement this page is about. Where a review cadence still applies, reserve it for the prose the table cannot automate.

The table in one breath

Runbooks: 30-90 days, verified by execution. Onboarding docs: before each cohort, 90-day floor. Hand-written architecture pages: 90 days. Generated views: on merge, pipeline verified quarterly. ADRs: exempt, immutable. Rarely-read reference: 180 days, then archive by default.

FAQ

What is a documentation SLA?

A documentation SLA is a maximum allowed age since a document was last verified against reality, set per document type, with a named owner and an enforcement mechanism such as a CI check. It differs from an edit date: the clock resets on verification, meaning the owner confirmed the content, not on any modification.

What is a reasonable review SLA for each documentation type?

Tightest for incident runbooks, 30 to 90 days verified by execution, because wrongness there is priced in outage minutes. Onboarding docs before each cohort with a 90-day floor. Hand-written architecture pages 90 days. Generated views regenerate on merge, with a quarterly check that the pipeline works. Decision records are exempt as immutable dated records, and rarely-read reference gets 180 days with archive as the default outcome.

How do you enforce a documentation SLA in CI?

Stamp each doc with a last_verified date in front matter, then run a job that computes the age of each stamp and fails with an annotation per breach. Run it on pull requests touching the docs tree and on a weekly schedule so breaches surface without an edit. Breaches should open tickets assigned to the doc owner rather than just failing silently.

What counts as verifying a document?

Confirming the content against reality, at whatever strength the doc type demands: executing a runbook step by step in staging, walking an onboarding doc as if new, comparing an architecture page to what the repository deploys. Reading a page and finding it plausible does not count, because plausibility is exactly what stale docs are good at.

Do generated docs need a review SLA?

Not on their content. A view generated from source, like a diagram drawn from the repository and embedded by URL, follows its source, so reviewing the artifact adds nothing. The SLA moves to the pipeline: confirm on a quarterly floor that regeneration actually runs on merges and that the embed points at the right project. If the pipeline breaks silently, a generated view rots like any other page.