Skip to main content

Documentation Standards

Documentation standards define the required content, format, and maintenance expectations for all documentation artifacts that accompany code. This reference covers project documentation (READMEs, changelogs), technical documentation (API specs, architecture records), operational documentation (runbooks), and inline documentation (comments). Each section specifies mandatory elements, format requirements, and quality criteria.

README requirements

Every repository requires a README.md file in the root directory. The README serves as the entry point for anyone encountering the codebase, whether they are new team members, future maintainers, or integration partners. README content follows a fixed structure that ensures consistency across projects while accommodating variation in project complexity.

Mandatory sections

SectionPurposeRequired content
Title and descriptionIdentify the projectProject name as H1, single paragraph explaining what the software does and who uses it
StatusCommunicate maintenance stateBadge or text indicating: active, maintenance-only, deprecated, or archived
PrerequisitesEnable environment setupRuntime versions, system dependencies, required accounts or access
InstallationEnable local setupStep-by-step commands to install dependencies and configure the application
ConfigurationEnable customisationEnvironment variables with descriptions, example values, and defaults
UsageDemonstrate basic operationCommands or code examples showing primary use cases
ContributingGuide contributorsLink to CONTRIBUTING.md or inline contribution process
LicenceDeclare termsLicence name with link to LICENCE file

Conditional sections

Include these sections when the specified conditions apply:

SectionInclude whenContent
ArchitectureCodebase exceeds 5,000 lines or has 3+ major componentsComponent diagram, data flow description, key design decisions
API referenceProject exposes an APILink to generated documentation or inline endpoint summary
DeploymentProject runs as a serviceEnvironment requirements, deployment commands, health check endpoints
TestingTest suite existsCommands to run tests, coverage requirements, test data setup
TroubleshootingSupport requests reveal common issuesProblem/solution pairs for 5+ frequent issues
ChangelogProject has external consumersLink to CHANGELOG.md
SecurityProject handles sensitive dataSecurity contact, vulnerability reporting process, link to SECURITY.md

README template

# Project Name
Brief description of what this project does and who it serves.
![Build Status](link-to-ci-badge)
![Licence](link-to-licence-badge)
## Prerequisites
- Node.js 18.0 or higher
- PostgreSQL 14 or higher
- Access to organisation's npm registry
## Installation
Clone the repository and install dependencies:
git clone https://github.com/org/project.git
cd project
npm install
Copy the example environment file and configure:
cp .env.example .env
## Configuration
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `DATABASE_URL` | PostgreSQL connection string | None | Yes |
| `API_KEY` | External service API key | None | Yes |
| `LOG_LEVEL` | Logging verbosity | `info` | No |
| `PORT` | HTTP server port | `3000` | No |
## Usage
Start the development server:
npm run dev
Run database migrations:
npm run migrate
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup
and contribution guidelines.
## Licence
[MIT](LICENCE)

API documentation

API documentation uses the OpenAPI Specification (formerly Swagger) version 3.0 or higher. Every HTTP API requires an OpenAPI document maintained alongside the code. The specification file lives in the repository at docs/openapi.yaml or openapi.yaml in the root directory.

OpenAPI requirements

The OpenAPI document includes these mandatory elements:

ElementPathRequirement
Infoinfo.titleHuman-readable API name
Infoinfo.versionSemantic version matching release
Infoinfo.descriptionPurpose, authentication overview, rate limits
ServersserversAt least production URL; staging if applicable
Pathspaths.{path}.{method}.summaryOne-line operation description
Pathspaths.{path}.{method}.descriptionDetailed behaviour explanation
Pathspaths.{path}.{method}.operationIdUnique identifier in camelCase
Pathspaths.{path}.{method}.responsesAll possible response codes with schemas
Componentscomponents.schemasReusable schema definitions for all request/response bodies
Componentscomponents.securitySchemesAuthentication mechanisms
SecuritysecurityDefault security requirements

Response documentation

Document all response codes the endpoint can return. For each response, include a schema definition and at least one example.

paths:
/beneficiaries/{id}:
get:
summary: Retrieve a beneficiary record
description: |
Returns the complete beneficiary record including demographic
data and programme enrolments. Requires read access to the
beneficiary's programme.
operationId: getBeneficiary
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
description: Beneficiary unique identifier
responses:
'200':
description: Beneficiary record retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Beneficiary'
example:
id: "550e8400-e29b-41d4-a716-446655440000"
givenName: "Maria"
familyName: "Santos"
dateOfBirth: "1985-03-15"
programmes:
- id: "prog-001"
name: "Cash Assistance"
enrolledAt: "2024-01-15T10:30:00Z"
'401':
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: Insufficient permissions for this beneficiary
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Beneficiary not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'

Schema documentation

Every schema includes a description at the schema level and for each property. Mark required fields explicitly. Provide format hints for strings that follow patterns.

components:
schemas:
Beneficiary:
type: object
description: |
A beneficiary represents an individual registered to receive
assistance through one or more programmes.
required:
- id
- givenName
- familyName
properties:
id:
type: string
format: uuid
description: System-generated unique identifier
readOnly: true
givenName:
type: string
minLength: 1
maxLength: 100
description: First or given name as provided by the beneficiary
familyName:
type: string
minLength: 1
maxLength: 100
description: Family name or surname
dateOfBirth:
type: string
format: date
description: Birth date in ISO 8601 format (YYYY-MM-DD)
programmes:
type: array
description: Programmes in which the beneficiary is enrolled
items:
$ref: '#/components/schemas/ProgrammeEnrolment'

Code comments

Comments explain intent, constraints, and non-obvious behaviour. They do not restate what code does when the code is self-explanatory. The purpose of a comment is to provide information that cannot be expressed in the code itself.

Comment categories

Intent comments explain why code exists or why a particular approach was chosen. These comments survive refactoring because they document decisions rather than implementation.

# Use insertion sort for small arrays because the overhead of
# quicksort exceeds its benefits below 10 elements. Benchmarked
# on target hardware: insertion wins below n=12.
if len(items) < 10:
return insertion_sort(items)

Constraint comments document requirements imposed by external systems, regulations, or integration partners that are not evident from the code.

# USAID requires beneficiary exports to exclude PII fields when
# destination is outside the implementing country. See compliance
# requirement DR-2024-0042.
if export_destination != programme.country:
exclude_fields.extend(PII_FIELDS)

Warning comments alert future developers to non-obvious risks or dependencies.

# WARNING: This timeout must exceed the payment provider's retry
# window (currently 180 seconds) to prevent duplicate transactions.
# Do not reduce without coordinating with finance team.
TRANSACTION_TIMEOUT = 200

TODO comments mark incomplete work with enough context to action later. Include the author identifier and date.

# TODO(jsmith 2024-11-15): Replace with batch query once
# PostgreSQL 16 upgrade completes. Current approach makes
# N+1 queries. Issue #1234.

Language-specific standards

LanguageSingle-lineMulti-lineDocumentation
Python#"""..."""Docstrings per PEP 257
JavaScript/TypeScript///* ... */JSDoc for exports
Java///* ... */Javadoc for public APIs
Go///* ... */Package and exported identifiers
SQL--/* ... */Complex queries only
Shell#NoneScript header and complex logic

Function and method documentation

Public functions, methods, and classes require documentation comments. Documentation specifies parameters, return values, exceptions, and usage examples.

def calculate_distribution_amount(
beneficiary_id: str,
programme_id: str,
distribution_date: date
) -> Decimal:
"""Calculate the distribution amount for a beneficiary.
Determines the amount based on household size, programme rules,
and any adjustments from previous distributions. The calculation
accounts for partial periods when beneficiaries enrolled mid-cycle.
Args:
beneficiary_id: UUID of the registered beneficiary.
programme_id: UUID of the distribution programme.
distribution_date: Date of the planned distribution.
Returns:
Distribution amount in programme currency. Returns Decimal('0')
if the beneficiary is ineligible for this distribution.
Raises:
BeneficiaryNotFoundError: If beneficiary_id does not exist.
ProgrammeNotFoundError: If programme_id does not exist.
IneligibleError: If beneficiary is suspended or exited.
Example:
>>> amount = calculate_distribution_amount(
... beneficiary_id="550e8400-e29b-41d4-a716-446655440000",
... programme_id="prog-001",
... distribution_date=date(2024, 12, 1)
... )
>>> print(amount)
Decimal('150.00')
"""
/
* Validates beneficiary registration data against programme requirements.
*
* Checks required fields, data formats, and programme-specific eligibility
* rules. Returns validation errors grouped by field for display in forms.
*
* @param data - Registration form data from client
* @param programmeId - Target programme for enrolment
* @returns Validation result with any errors grouped by field name
*
* @example
* const result = validateRegistration(formData, 'prog-001');
* if (!result.valid) {
* console.log(result.errors);
* // { dateOfBirth: ['Must be 18 or older'], phone: ['Invalid format'] }
* }
*/
export function validateRegistration(
data: RegistrationData,
programmeId: string
): ValidationResult {

Architecture decision records

An architecture decision record (ADR) documents a significant technical decision along with its context and consequences. ADRs create a decision log that explains why the system has its current shape, which prevents relitigating settled decisions and helps new team members understand constraints.

When to write an ADR

Write an ADR when the decision:

  • Affects multiple components or services
  • Is difficult or expensive to reverse
  • Involves significant trade-offs
  • Departs from established patterns
  • Has security or compliance implications
  • Results from evaluating multiple alternatives

Do not write an ADR for routine choices like variable naming, minor refactoring, or bug fixes.

ADR format

ADRs use a fixed format stored as Markdown files in docs/adr/ with filenames following the pattern NNNN-title-with-dashes.md where NNNN is a zero-padded sequence number.

docs/adr/
├── 0001-use-postgresql-for-primary-database.md
├── 0002-adopt-event-sourcing-for-audit-log.md
├── 0003-select-keycloak-for-identity-provider.md
└── README.md

ADR template

# NNNN. Title of Decision
Date: YYYY-MM-DD
## Status
Proposed | Accepted | Deprecated | Superseded by [NNNN](link)
## Context
Describe the situation that requires a decision. Include:
- The problem or need driving the decision
- Constraints (technical, organisational, regulatory)
- Forces in tension that make this decision non-trivial
## Decision
State the decision in active voice. "We will use X" not
"X was chosen". Be specific about scope and boundaries.
## Consequences
Describe the results of the decision:
- Positive outcomes and benefits
- Negative outcomes and risks accepted
- New constraints this creates
- Follow-up actions required
## Alternatives Considered
For each rejected alternative:
- Brief description
- Why it was not selected

ADR example

# 0003. Select Keycloak for Identity Provider
Date: 2024-09-15
## Status
Accepted
## Context
The organisation requires centralised identity management to
support single sign-on across 12 applications, multi-factor
authentication, and federation with partner organisations.
Current state has each application managing its own user
database, creating credential sprawl and inconsistent
security controls.
Requirements:
- Support SAML 2.0 and OIDC protocols
- Self-hosted option for data sovereignty (field offices
operate in countries with data localisation requirements)
- Integration with existing Active Directory for HQ staff
- Support for social login for external stakeholders
- Budget constraint: no per-user licensing fees
## Decision
We will deploy Keycloak as the organisation's identity
provider. Keycloak will run on the existing Kubernetes
cluster in the EU region with a replica deployment in
the East Africa regional data centre.
## Consequences
Positive:
- No licensing costs regardless of user count
- Full control over data location
- Active community and extensive documentation
- Native Kubernetes operator simplifies deployment
Negative:
- Requires internal expertise for maintenance
- Upgrades require manual intervention and testing
- No vendor support SLA (mitigated by commercial support
option if needed)
New constraints:
- All new applications must integrate via OIDC
- Identity team must maintain Keycloak expertise
- Quarterly security patching required
## Alternatives Considered
Microsoft Entra ID: Strong Active Directory integration
but per-user pricing exceeds budget at scale. Data residency
options do not cover all required countries.
Okta: Excellent developer experience but licensing costs
prohibitive. Nonprofit discount still exceeded budget by 3x.
Authentik: Promising open-source alternative but smaller
community and less mature federation support. Considered too
risky for production identity infrastructure.

Changelog

A changelog documents all notable changes to a project in reverse chronological order. Every project with external consumers (other teams, partners, end users) maintains a CHANGELOG.md file in the repository root.

Changelog format

Changelogs follow the Keep a Changelog format (keepachangelog.com) with modifications for semantic versioning.

# Changelog
All notable changes to this project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
### Added
- Beneficiary export to CSV format
### Changed
- Increased API rate limit from 100 to 500 requests per minute
## [2.1.0] - 2024-11-01
### Added
- Multi-factor authentication support for API access
- Bulk beneficiary import from Excel files
- Arabic language support in beneficiary portal
### Changed
- Upgraded PostgreSQL client to version 3.4.0
- Distribution calculations now round to 2 decimal places
### Fixed
- Corrected timezone handling in distribution scheduling
- Resolved memory leak in long-running export jobs
### Security
- Patched CVE-2024-1234 in authentication library
## [2.0.0] - 2024-08-15
### Changed
- BREAKING: Renamed `participant` to `beneficiary` in all APIs
- BREAKING: Date fields now use ISO 8601 format exclusively
### Removed
- BREAKING: Deprecated v1 API endpoints removed
### Migration
- Run `npm run migrate:v2` before deploying
- Update API clients to use new field names
- See [Migration Guide](docs/migration-v2.md) for details

Change categories

CategoryUse for
AddedNew features and capabilities
ChangedChanges to existing functionality
DeprecatedFeatures marked for future removal
RemovedFeatures removed in this release
FixedBug fixes
SecurityVulnerability patches and security improvements
MigrationRequired steps when upgrading (for breaking changes)

Writing changelog entries

Each entry describes the change from the user’s perspective. Focus on what changed and why it matters, not how it was implemented.

PoorBetter
”Refactored database queries""Reduced report generation time from 45 seconds to 8 seconds"
"Fixed bug in validation""Fixed: Phone numbers with country codes now validate correctly"
"Updated dependencies""Upgraded authentication library to patch CVE-2024-1234"
"Added new endpoint""Added: Export beneficiary data to CSV for offline analysis”

Runbook documentation

A runbook documents procedures for operating, maintaining, and troubleshooting a system in production. Runbooks enable operations staff to handle routine tasks and common incidents without requiring deep system knowledge. Every service deployed to production requires a runbook in docs/runbook.md or docs/runbook/ directory for complex services.

Runbook sections

SectionContent
Service overviewPurpose, dependencies, SLA targets, owning team
ArchitectureComponent diagram, data flows, external integrations
Health checksEndpoints, expected responses, check frequency
Metrics and alertsKey metrics, threshold values, alert configurations
Common proceduresStart, stop, restart, scale, backup, restore
TroubleshootingSymptoms, diagnosis steps, resolution procedures
EscalationContact information, escalation criteria, on-call rotation
Disaster recoveryRPO/RTO targets, recovery procedures, test schedule

Health check documentation

## Health Checks
### Application Health
Endpoint: `GET /health`
Expected response:
```json
{
"status": "healthy",
"version": "2.1.0",
"checks": {
"database": "ok",
"cache": "ok",
"queue": "ok"
}
}
```
Check frequency: Every 30 seconds
Alert threshold: 3 consecutive failures
### Database Connectivity
Command:
```bash
psql $DATABASE_URL -c "SELECT 1"
```
Expected: Returns `1` within 5 seconds
Alert threshold: Response time exceeds 5 seconds or connection fails

Troubleshooting format

Troubleshooting sections follow a consistent structure: symptom, possible causes with likelihood, diagnosis steps, and resolution.

## Troubleshooting
### High Memory Usage
Symptom: Container memory exceeds 80% of limit; OOMKilled events
in logs.
Possible causes:
1. Memory leak in export processing (common)
2. Excessive concurrent connections (occasional)
3. Large payload processing (rare)
Diagnosis:
Check current memory consumers:
```bash
kubectl top pods -n production
kubectl exec -it $POD -- ps aux --sort=-%mem | head -20
```
Check for export jobs:
```bash
kubectl logs $POD | grep "export" | tail -50
```
Resolution:
For export-related memory growth:
```bash
# Cancel running exports
curl -X POST http://$POD:8080/admin/exports/cancel-all
# Restart pod to reclaim memory
kubectl rollout restart deployment/beneficiary-service -n production
```
For connection pool exhaustion:
```bash
# Check connection count
kubectl exec -it $POD -- psql $DATABASE_URL -c \
"SELECT count(*) FROM pg_stat_activity WHERE application_name = 'beneficiary-service'"
# If above 80, restart to reset connections
kubectl rollout restart deployment/beneficiary-service -n production
```
Prevention: Configure export jobs to process in batches of 1,000
records. Set connection pool maximum to 20 (see config `DB_POOL_MAX`).

Documentation review

Documentation undergoes review before merge, following the same process as code. Reviews verify accuracy, completeness, and adherence to standards.

Review checklist

CategoryVerification
AccuracyTechnical content is correct; commands and examples work
CompletenessAll required sections present; no placeholder text
CurrencyVersion numbers and dependencies are current
ClarityExplanations are understandable to target audience
ConsistencyTerminology matches project conventions
LinksInternal and external links resolve correctly
FormattingMarkdown renders correctly; code blocks have language tags
AccessibilityImages have alt text; content structure uses headings correctly

Review assignment

Documentation typeRequired reviewer
READMEAny team member
API documentationDeveloper familiar with the API
Architecture decision recordTechnical lead or architect
RunbookOperations team member
Security documentationSecurity team member
User documentationSomeone unfamiliar with the feature

Documentation tooling

Documentation tooling follows docs-as-code principles: documentation source lives in version control alongside code, undergoes the same review process, and builds through automated pipelines.

Tooling stack

+--------------------------------------------------------------------+
| DOCUMENTATION PIPELINE |
+--------------------------------------------------------------------+
| |
| SOURCE BUILD PUBLISH |
| +-------------+ +----------------+ +-------------+ |
| | | | | | | |
| | Markdown +------>| Static Site +------>| Web Server | |
| | in Git | | Generator | | or CDN | |
| | | | | | | |
| +------+------+ +-------+--------+ +-------------+ |
| | | |
| | +-------+--------+ |
| | | | |
| +------------->| Linting and | |
| | Validation | |
| | | |
| +----------------+ |
| |
+--------------------------------------------------------------------+
PurposeToolNotes
Markdown lintingmarkdownlintEnforces consistent formatting
Link checkingmarkdown-link-checkValidates internal and external links
Spell checkingcspellConfigurable with technical dictionaries
API documentationRedoc, Swagger UIRenders OpenAPI specifications
Static siteAstro, MkDocs, DocusaurusGenerates documentation websites
Diagram generationMermaid, PlantUMLCreates diagrams from text

CI pipeline integration

Documentation builds and validates in the CI pipeline alongside code. Failed documentation checks block merge.

.github/workflows/docs.yml
name: Documentation
on:
pull_request:
paths:
- 'docs/'
- '*.md'
- 'openapi.yaml'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint Markdown
uses: DavidAnson/markdownlint-cli2-action@v14
with:
globs: '/*.md'
- name: Check links
uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
use-quiet-mode: 'yes'
config-file: '.markdown-link-check.json'
- name: Validate OpenAPI
run: npx @redocly/cli lint openapi.yaml
- name: Spell check
run: npx cspell "/*.md" --no-progress

Accessibility in documentation

Documentation accessibility ensures content is usable by people with disabilities, including those using screen readers, keyboard navigation, or other assistive technologies.

Requirements

ElementRequirement
HeadingsUse hierarchical structure (H1 → H2 → H3); do not skip levels
ImagesProvide alt text describing content and purpose
LinksUse descriptive text; avoid “click here” or “read more”
Code blocksInclude language identifier; provide text description for complex examples
TablesInclude header row; avoid merged cells; keep tables simple
ColourDo not use colour alone to convey meaning
LanguageSpecify document language; mark language changes in multilingual content

Alt text guidelines

Alt text describes what an image shows and why it matters in context. For diagrams, describe the relationships and key information rather than visual appearance.

Image typeAlt text approach
ScreenshotDescribe the relevant UI state and highlighted elements
DiagramDescribe the components and their relationships
ChartState the trend or key data points
DecorativeUse empty alt (alt="") or CSS background
<!-- Poor -->
![Diagram](architecture.png)
<!-- Better -->
![System architecture showing web server connecting to API
gateway, which routes requests to three microservices, each
with its own PostgreSQL database](architecture.png)

Versioning documentation

Documentation versions track with the software it describes. When code changes affect behaviour, documentation updates in the same commit or pull request.

Version synchronisation

Change typeDocumentation action
New featureAdd documentation before or with feature merge
API changeUpdate OpenAPI spec in same pull request
Configuration changeUpdate README and runbook
Breaking changeUpdate changelog, migration guide, and affected docs
Bug fixUpdate if fix changes documented behaviour
DeprecationAdd deprecation notice with removal timeline

Multi-version documentation

Projects supporting multiple versions simultaneously maintain documentation branches or version selectors.

docs/
├── v1/ # Legacy version (maintenance only)
│ ├── api.md
│ └── guide.md
├── v2/ # Current stable version
│ ├── api.md
│ ├── guide.md
│ └── migration-from-v1.md
└── next/ # Upcoming version (pre-release)
├── api.md
└── guide.md

Documentation for deprecated versions includes a notice directing users to current documentation:

<Aside variant="caution" title="Deprecated version">
This documentation covers API v1, which is deprecated and will
be removed on 2025-06-01. See the [v2 documentation](/docs/v2/)
for the current version and [migration guide](/docs/v2/migration-from-v1/)
for upgrade instructions.
</Aside>

See also