DOMAIN:CONTENT — TECHNICAL WRITING¶
OWNER: benjamin, joost
ALSO_USED_BY: jouke, dinand, anna (Spec Writer), antje (TDD Writer)
UPDATED: 2026-03-24
TECHNICAL_WRITING:API_DOCUMENTATION¶
STANDARD: OpenAPI 3.1 (latest) for REST APIs
STANDARD: AsyncAPI 3.0 for event-driven APIs
RULE: design-first — write OpenAPI spec BEFORE implementation code
RULE: spec file IS the single source of truth — generate reference docs from it
RULE: never hand-write API reference that can be auto-generated from spec
Structure of API Reference¶
CHECK: every endpoint has summary, description, parameters, request body, responses, examples
CHECK: every parameter has type, required/optional, default value, constraints (min/max/pattern)
CHECK: every response code documented — 200, 201, 400, 401, 403, 404, 409, 422, 429, 500
CHECK: error response schema is consistent across ALL endpoints
REQUIRED SECTIONS (per API docs site):
1. Overview — what the API does, base URL, authentication
2. Authentication — full auth flow with code examples
3. Quick Start — working request in <5 minutes
4. Endpoints Reference — auto-generated from OpenAPI spec
5. Error Reference — all error codes, meanings, fixes
6. Rate Limits — limits per tier, headers, retry strategy
7. Webhooks — event types, payload schemas, verification
8. Changelog — version history with breaking change flags
9. SDKs / Libraries — official clients per language
Error Response Schema¶
RULE: use consistent error envelope across all endpoints
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable explanation",
"details": [
{
"field": "email",
"issue": "Must be a valid email address",
"value": "not-an-email"
}
],
"request_id": "req_abc123",
"doc_url": "https://docs.example.com/errors/VALIDATION_ERROR"
}
}
ANTI_PATTERN: generic "Something went wrong" with no error code
FIX: every error has a unique code, human message, and doc_url
ANTI_PATTERN: different error shapes per endpoint
FIX: single ErrorResponse schema in OpenAPI components, $ref everywhere
OpenAPI File Organization¶
RULE: split large specs by resource — one file per tag/resource group
RULE: use $ref to shared components (schemas, parameters, responses)
RULE: directory structure mirrors URL hierarchy
api/
├── openapi.yaml # root spec, references below
├── paths/
│ ├── users.yaml
│ ├── projects.yaml
│ └── billing.yaml
├── components/
│ ├── schemas/
│ ├── parameters/
│ └── responses/
└── examples/
TOOL: Spectral — lint OpenAPI specs
RUN: npx @stoplight/spectral-cli lint openapi.yaml
TOOL: Redocly CLI — bundle, lint, preview
RUN: npx @redocly/cli lint openapi.yaml
RUN: npx @redocly/cli preview-docs openapi.yaml
TECHNICAL_WRITING:INTEGRATION_GUIDES¶
RULE: every integration guide follows this structure
INTEGRATION GUIDE TEMPLATE:
1. Prerequisites — what you need before starting
2. Installation — package manager commands (npm, pip, etc.)
3. Authentication — get API key, configure credentials
4. Quick Start — minimal working example (copy-paste-run)
5. Core Concepts — explain the mental model
6. Common Use Cases — 3-5 recipes with full code
7. Error Handling — how to catch and handle errors
8. Testing — how to test the integration (sandbox, mocks)
9. Going to Production — checklist, environment variables
10. Troubleshooting — FAQ, common errors, debug steps
Code Sample Quality¶
RULE: every code sample MUST compile/run without modification
RULE: every code sample MUST include error handling
RULE: every code sample MUST show imports/requires
RULE: every code sample MUST use realistic (not foo/bar) variable names
RULE: provide samples in ALL languages the SDK supports
ANTI_PATTERN: code snippet that starts mid-function with no context
FIX: show complete, runnable file — from imports to execution
ANTI_PATTERN: happy-path-only examples
FIX: every example shows try/catch or error callback
ANTI_PATTERN: outdated code samples that no longer compile
FIX: CI pipeline runs code samples against latest SDK version
TOOL: Docusaurus code block testing or custom test harness
CHECK: sample uses environment variables for secrets, never hardcoded
CHECK: sample includes comments explaining non-obvious lines
CHECK: sample matches the API version documented on the page
TECHNICAL_WRITING:DEVELOPER_ONBOARDING¶
README Structure¶
RULE: every project README follows this structure
README TEMPLATE:
1. Project Name + One-line description
2. Status badges (build, coverage, version)
3. Quick Start (3-5 steps to run locally)
4. Prerequisites (runtime versions, tools)
5. Installation
6. Configuration (environment variables table)
7. Usage (basic examples)
8. Architecture Overview (link to detailed doc)
9. Contributing (link to CONTRIBUTING.md)
10. License
ANTI_PATTERN: README with only "npm install && npm start"
FIX: include prerequisites, configuration, and what to expect
ANTI_PATTERN: README that duplicates the full docs
FIX: README is entry point — link to detailed docs for depth
Architecture Overview Document¶
RULE: every project has an architecture overview at docs/architecture.md
CHECK: includes system diagram (Mermaid or similar)
CHECK: lists all services/components and their responsibilities
CHECK: documents data flow for primary use cases
CHECK: lists external dependencies and why they were chosen
CHECK: links to relevant ADRs
TECHNICAL_WRITING:DOCS_AS_CODE_TOOLING¶
Tool Selection Matrix¶
| Criterion | Docusaurus | Mintlify | MkDocs |
|---|---|---|---|
| Cost | Free (MIT) | $300+/mo | Free (BSD) |
| Framework | React/MDX | MDX (hosted) | Python/Markdown |
| Versioning | Built-in | Manual | mike plugin |
| i18n | Built-in | Limited | i18n plugin |
| Search | Meilisearch (FR)/local preferred. Algolia (US) secondary — sovereignty risk. | Built-in AI | search plugin |
| Self-hosted | Yes | No | Yes |
| Maintenance | Self-managed | Managed | Self-managed |
| AI features | None | llms.txt, AI search | None |
NOTE: GE uses MkDocs for wiki brain — all internal docs stay MkDocs
RULE: client projects — recommend Docusaurus (free, full control, React ecosystem)
RULE: if client wants managed docs — Mintlify (best DX, API playground)
RULE: never recommend Nextra — single maintainer, uncertain future
Docs-as-Code Workflow¶
RULE: docs live in same repo as code
RULE: docs PRs require review just like code PRs
RULE: docs deploy on merge to main (CI/CD)
RULE: broken links = failed build
TOOL: Docusaurus
RUN: npx create-docusaurus@latest docs classic --typescript
TOOL: vale — prose linter
RUN: vale docs/
TOOL: markdownlint
RUN: npx markdownlint-cli docs/**/*.md
TECHNICAL_WRITING:VERSIONING_DOCUMENTATION¶
RULE: multi-version docs required when API has breaking changes
RULE: latest version is default — older versions clearly labeled
RULE: deprecated versions show banner with migration guide link
Changelog Format¶
RULE: follow Keep a Changelog format (keepachangelog.com)
## [2.1.0] - 2026-03-24
### Added
- New `/webhooks` endpoint for event subscriptions
### Changed
- `GET /users` now returns pagination metadata in headers
### Deprecated
- `GET /users?page=N` query param — use cursor-based pagination
### Removed
- `DELETE /legacy/endpoint` (deprecated since 1.8.0)
### Fixed
- Rate limit headers now return correct reset timestamp
### Security
- Patched CVE-2026-XXXXX in auth middleware
ANTI_PATTERN: changelog that says "various improvements"
FIX: every entry is specific, actionable, links to docs
ANTI_PATTERN: no migration guide for breaking changes
FIX: every REMOVED/CHANGED entry links to migration instructions
TECHNICAL_WRITING:ARCHITECTURE_DECISION_RECORDS¶
STANDARD: Michael Nygard ADR format (original, lightweight)
ADR Template¶
# ADR-NNN: [Short Noun Phrase Title]
STATUS: proposed | accepted | deprecated | superseded by ADR-NNN
DATE: YYYY-MM-DD
DECIDERS: [who participated]
## Context
[Forces at play — technical, business, organizational. Value-neutral facts.]
## Decision
[The decision, in active voice: "We will..."]
## Consequences
[All consequences — positive, negative, neutral. Be honest.]
When to Write an ADR¶
WHEN_REQUIRED: choosing a framework, language, or major library
WHEN_REQUIRED: changing authentication/authorization approach
WHEN_REQUIRED: choosing between build vs. buy
WHEN_REQUIRED: changing data storage strategy
WHEN_REQUIRED: introducing a new external dependency
WHEN_REQUIRED: changing deployment strategy
WHEN_REQUIRED: any decision that would be hard to reverse
RULE: ADR is 1-2 pages max — link to supplemental docs for depth
RULE: ADRs are immutable once accepted — supersede, don't edit
RULE: store ADRs in docs/adr/ in the project repo
RULE: number sequentially: ADR-001, ADR-002, etc.
RULE: review ADRs monthly — compare decision vs. actual outcome
ANTI_PATTERN: ADR written after implementation as paperwork
FIX: write ADR BEFORE implementation — it's a decision tool, not documentation
ANTI_PATTERN: ADR with no alternatives considered
FIX: list at least 2 alternatives with pros/cons
TOOL: adr-tools CLI
RUN: adr new "Use PostgreSQL for primary data store"
TOOL: Log4brains — static site generator for ADR browsing