Document Every Shipped Surface
Document every shipped surface
If it ships, it has a doc page. No exceptions, no "we'll document it later."
What counts as a surface, for a CLI-shaped product:
- Every command and subcommand
- Every flag of every command
- Every config key, with default, type, and what it controls
- Every IPC request and response variant
- Every search operator (every
is:,has:, every field) - Every keybinding
For an app: every settings panel, every notification type, every keyboard shortcut, every URL scheme, every webhook event.
Media can be a surface too. A batch of demo recordings becomes a shipped
gallery only when it has a stable URL, navigation, context, and an owner in the
docs tree. Files sitting in a private folder or release scratch space are raw
assets, not a discoverable product surface.
Also count callable backend surfaces. A public function, resolver, server action, or callback can be just as much of an API as an HTTP route. See Public backend functions are API surfaces.
The Sports DB audit added the public data version of the same rule. A public directory page is not just UI. It implies a data contract. If it reads from a broad internal API while a separate passport API carefully filters sensitive fields, the undocumented surface is the real product risk.
The discipline only holds if it's mechanical
A policy that depends on remembering to update docs decays. The only version that survives contact with reality is the one CI enforces. In mxr, clap --help snapshots are captured in tests, the CLI reference is regenerated from them, and the build fails on snapshot drift. Adding a flag without re-snapshotting breaks the build — that's the point.
See Generated Docs as Drift Defense for the mechanism.
Source of truth maps to doc page
Every surface has one source of truth and one doc page. When they're far apart, drift creeps in.
| Surface | Source of truth | Doc page |
|---|---|---|
| CLI commands & flags | crates/daemon/src/cli/ |
Auto-generated reference/cli/<cmd>.md |
| Config keys | crates/config/src/types.rs |
Hand-written reference/config.md |
| IPC | crates/protocol/src/types.rs |
Hand-written + OpenAPI dump |
| Search operators | mail-query crate + mxr custom filters in crates/search/src/lib.rs |
Hand-written guides/search.md |
| Keybindings | crates/tui/src/keys.rs + keys.toml |
Hand-written reference/keybindings.md |
Hand-written pages are weaker than generated ones. For anything regenerable, generate it.
Media pages have a second contract: loading and accessibility. A gallery should
not download every recording on page load when posters are enough. Repeated
links and controls need labels that identify the clip, not fifteen copies of
"Open video." The Mxr Docs Site gallery uses posters, preload="none", and
clip-specific fallback links for that reason.
What this catches
Without this discipline, a flag ships and is documented six weeks later when a user files a bug. The user has already lost trust. The fix is mechanical: every flag has a generated page, every config key has a row in the reference table, every search operator has an entry. The author can't get the PR through without it.
See also
- Generated Docs as Drift Defense — the CI mechanism that makes this enforceable
- How I Write Software Docs — the full synthesis
- Mxr — the project where this discipline lives
- Client contract drift is product drift
- Public backend functions are API surfaces
- Sports DB