How I Write Software Docs
How I write software docs
The principles I keep reaching for whenever I write docs for a dev tool or an app. Written down so I don't have to rediscover them on every project.
The full canonical version lives in docs/guides/writing-docs.md in the Mxr repo. This page is the vault hub — the principles distilled, with links to atomic concept notes for each one.
The anti-goal
No reader should finish a doc and think "I get what this is, but I don't know how to make it useful."
That's it. Every other rule I keep is a tactic in service of this. See Operationally Useful Docs for the deletion test that catches violations.
The seven principles
1. Every section ends with something runnable
For a CLI: a real mxr invocation that produces real output. For an app: a screenshot of the actual UI flow, with the relevant button labelled. Description without action is the failure mode.
If a section still teaches something useful after deleting every code block, the section is mislabeled. See Operationally Useful Docs.
2. Document every shipped surface
If it ships, it has a doc page. Every command, every flag, every config key, every event type, every keybinding. The discipline only holds if it's mechanical — make CI enforce it. See Document Every Shipped Surface.
3. Recipes solve goals, not restate reference
Anything composable gets a recipe section. A real recipe = problem statement + smallest pipeline + output shape. Filler recipes restate --help; real ones solve a job. See A Recipe Solves a Goal.
4. One Diátaxis quadrant per page
Tutorial, how-to, reference, or explanation — pick one. Mode-mixing is a review-blocker. A quickstart that detours into architecture is broken; a reference page that opens with rationale is broken. See Diátaxis Quadrants.
5. Voice: command first, explanation second
Second person, imperative. Show before tell. No "click here," no "see above." Placeholders paired with real values. Inline code for code things. Tighter than article voice. See Command First, Explanation Second.
6. Generate reference from the source of truth
Hand-edited reference pages rot the moment a flag changes. Generate them from the code, snapshot them in CI, fail the build on drift. Authors keep the parts that take judgment (the "when to use it" line, the three to four examples); the generator handles the boring mechanical part. See Generated Docs as Drift Defense.
7. Mutations are documented dry-run first
Every mutating example shows --dry-run before --yes. Copy-paste habits absorb the discipline. The doc convention mirrors the runtime contract. See Mutations Documented Dry-Run First.
8. Lock the decisions that get relitigated
For docs aimed at maintainers and contributors, an explicit "locked decisions (do not relitigate)" section saves every reviewer the same conversation. Pair it with an "out of scope (do not implement)" list. Both are short, both are honest about why, both are revisitable when constraints actually change. See Locked Decisions in Docs.
How these apply to apps, not just CLIs
The principles port:
- "Every section ends with something runnable" → for an app, every section ends with the exact UI step (screenshot + button label), not a paraphrase
- "Document every surface" → every settings panel, every notification, every URL scheme, every webhook event
- "Recipes solve goals" → end-to-end task flows that compose features, not one-screen tutorials
- "Diátaxis quadrants" → unchanged
- "Voice" → unchanged
- "Generate from source of truth" → settings schemas, route tables, event types all become reference pages automatically
- "Mutations dry-run first" → destructive in-app actions get a confirmation flow in the doc, never lead with the irreversible step
The principles aren't CLI-specific. They're a discipline about respecting the reader's time and the doc's accuracy. CLIs are just where I had to write them down first because the surface is so wide.
Reviewer checklist
When reviewing a doc PR — or my own draft before opening one — I scan for these:
A PR that fails any of these isn't ready. Send it back, don't merge it with "we'll fix it later."
Anti-patterns I keep watching for
- Lorem-ipsum reference. Generated stubs like "The foo method does foo things." Worse than no docs.
- Explainer-only pages. A concept doc with zero invocations is a smell. Either add "Use it like this," or fold the concept into a guide.
- Stale screenshots and outputs. When a command's output format changes, every doc showing the old output becomes a lie. Regenerate as part of the change.
- Mode-mixing. A quickstart that detours into architecture. Pick the quadrant; link to the others.
- Patching stale pages instead of deleting. "Note: this no longer works" is worse than 404. Use the build's link-check to find orphans; fix or delete.
- Empty "See also." Every guide ends with cross-links. If you can't think of any, the guide is probably orphaned.
How this connects to my broader thinking
- Documenting Domain-Specific Knowledge — Calderone's principles. Where this overlaps: pick an audience, no assumptions, be prescriptive. Where this differs: that note is about what to document; this hub is about how the page reads.
- My Writing Style Guide — my article voice. Docs are deliberately not that voice — see Command First, Explanation Second for the contrast.
- Mxr — the concrete project where these principles live as enforced rules.
- Writing is thinking — the meta-principle that makes any of this worth doing.
See also
- Diátaxis — the four-quadrant framework I anchor on
- Rust API Guidelines: Documentation — every public item has an example; applies equally to CLI verbs and config keys
- ripgrep GUIDE.md — exemplar of CLI narrative docs alongside a man page