- Published on
Documentation as Code — Keeping Your API Docs Accurate and Always Up to Date
- Authors

- Name
- Sanjeev Sharma
- @webcoderspeed1
Introduction
Documentation rots. You know this. You update the API, forget to update the docs, and three weeks later someone is reading stale information. The answer isn't "writers need to be more diligent." The answer is architecture: keep docs close to code so they can't drift apart.
Documentation-as-code means docs live in your repo, are reviewed like code, and are validated in CI. When code changes, docs change with it or the build fails.
- Why Docs Rot
- Inline JSDoc → OpenAPI Generation
- TypeDoc for TypeScript API Docs
- ADRs (Architecture Decision Records) in Code Repo
- Runbooks as Code
- Auto-Generating Changelogs from Conventional Commits
- Mintlify, Scalar, and Redocly for Beautiful API Docs
- Testing That Docs Examples Actually Work
- Docs CI Pipeline
- Docs-as-Code with Docusaurus
- Measuring Doc Quality
- Checklist
- Conclusion
Why Docs Rot
Docs rot because:
- They're written separately from code
- Nobody validates them in CI
- Code changes, docs don't
- Docs are treated as optional
The fix: treat docs like code. Commit them with changes. Review them in PRs. Validate them in CI. Then they can't fall behind.
Inline JSDoc → OpenAPI Generation
Start with JSDoc in your code. Generate OpenAPI from it.
Example:
/**
* Get a user by ID
* @param id - User ID (UUID format)
* @returns User object with profile and stats
* @throws 404 if user not found
*/
export async function getUser(id: string): Promise<User> {
return db.users.findById(id);
}
Tool like tsdoc-openapi can extract this and generate an OpenAPI endpoint definition.
Advantages:
- Documentation lives in code
- Changes together with implementation
- Can't forget to update docs (they're part of the function)
- IDE shows docs on hover
Workflow:
- Write JSDoc comments
- Generate OpenAPI on build
- Generate client SDKs
- Docs stay in sync
TypeDoc for TypeScript API Docs
TypeDoc extracts documentation from TypeScript code and generates an interactive HTML site.
Usage:
npm install --save-dev typedoc
typedoc --out docs src/
Output: A browsable documentation site showing:
- All exported functions, classes, interfaces
- Parameter descriptions (from JSDoc)
- Return types
- Examples (from code)
- Inheritance hierarchy
Update code + JSDoc, regenerate docs. No separate docs to maintain.
ADRs (Architecture Decision Records) in Code Repo
ADRs are lightweight architecture documents. Store them in your repo.
Location: /docs/adr/ in your repo
Format (example):
# ADR-001: Use PostgreSQL for primary data store
Date: 2026-03-01
Status: Accepted
## Context
We needed a primary data store for user data.
## Decision
We chose PostgreSQL because:
- ACID transactions
- JSON support
- Strong community
- Mature tooling
## Consequences
- Must manage backups
- Requires DBA knowledge for optimization
Benefits:
- Decision rationale is documented
- Future engineers understand why
- Changes to decisions are version-controlled
- Can review old decisions to understand evolution
Runbooks as Code
Operational runbooks should be executable where possible.
Example (Markdown with code blocks):
# Incident: High Error Rate in Auth Service
## Detection
- Alert: error_rate < 5% for 5 minutes
- Check dashboard: /dashboards/auth-service
## Investigation
1. Check logs:
\`\`\`bash
kubectl logs -l app=auth-service --tail=100
\`\`\`
2. Check metrics:
\`\`\`bash
curl http://prometheus:9090/api/v1/query?query=auth_error_rate
\`\`\`
## Resolution
If database connection pool exhausted:
1. Restart service:
\`\`\`bash
kubectl rollout restart deployment/auth-service
\`\`\`
2. Monitor recovery
3. Post-mortem
Store in /docs/runbooks/. Copy-paste commands directly into terminal. No mismatches between documented procedure and actual commands.
Auto-Generating Changelogs from Conventional Commits
Conventional commits enable automatic changelog generation.
Conventional commit format:
feat(auth): add two-factor authentication
fix(api): handle null user ID in getUser
docs(api): update endpoint descriptions
Tool: standard-version or conventional-changelog
conventional-changelog -p angular -i CHANGELOG.md -s
Output: CHANGELOG.md with sections:
- Features
- Bug Fixes
- Breaking Changes
Regenerate on each release. Changelog is always accurate and never out of sync.
Mintlify, Scalar, and Redocly for Beautiful API Docs
These tools generate beautiful API documentation from OpenAPI specs.
Mintlify:
- Beautiful UI
- Customizable branding
- Docs + API ref combined
- Self-hosted or cloud
Scalar:
- Modern, interactive
- Dark mode
- Request/response examples
- Try-it functionality
Redocly:
- Enterprise-grade
- Versioning
- Analytics
- Custom portals
All are generated from your OpenAPI spec. Update spec, regenerate. Docs always match.
Testing That Docs Examples Actually Work
Documentation examples should be tested.
Approach 1: Doctests
/**
* Get a user by ID
* @example
* const user = await getUser("550e8400-e29b-41d4-a716-446655440000");
* console.assert(user.email === "alice@example.com");
*/
Approach 2: Integration tests
it("documentation example: fetch user", async () => {
const user = await getUser("550e8400-e29b-41d4-a716-446655440000");
expect(user.email).toBe("alice@example.com");
});
Approach 3: Snapshot tests
# Example API response is captured
GET /api/users/123 →
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "alice@example.com"
}
If API changes, snapshot test fails and docs must be updated.
Docs CI Pipeline
Your CI should validate docs:
# .github/workflows/docs.yml
name: Documentation
on: [pull_request, push]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install -d
- run: npm run docs:lint # markdownlint, etc.
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install -d
- run: npm run docs:generate # TypeDoc, OpenAPI, etc.
- run: npm run docs:build # Docusaurus, etc.
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install -d
- run: npm run test:docs # Run documentation examples
deploy:
needs: [lint, generate, test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- run: npm run docs:deploy
On every PR:
- Markdown is linted
- Docs are generated
- Doc examples are tested
- Docs build successfully
If anything fails, PR can't merge.
Docs-as-Code with Docusaurus
Docusaurus is a docs framework for technical documentation.
Structure:
docs/
api/
auth.md
users.md
guides/
getting-started.md
deployment.md
blog/
2026-03-15-new-features.md
Markdown with metadata:
---
title: Authentication
sidebar_position: 1
---
# Authentication
## Overview
...
## API Endpoints
...
Build:
npm run build # Generates static HTML site
npm run serve # Serve locally
Deploy: Push built site to Vercel or GitHub Pages.
Advantages:
- Version control all docs
- Review docs in PRs
- Search built-in
- Versioning (multiple API versions)
- MDX support (React components in Markdown)
Measuring Doc Quality
Track these metrics:
- Search abandonment rate: If users search but don't click, docs are missing
- Page bounce rate: If users land and leave immediately, content is wrong
- Time on page: If low, docs are unclear
- Feedback votes: "Was this page helpful?" widget
Use Segment or Mixpanel to track. Use Hotjar for heatmaps.
Example metric: If < 50% of docs pages have < 10s average time, those sections need improvement.
Checklist
- Keep JSDoc comments in all exported functions
- Generate OpenAPI spec from code
- Use TypeDoc to generate API reference
- Store ADRs in
/docs/adr/ - Store runbooks in
/docs/runbooks/ - Use conventional commits
- Auto-generate changelog
- Test documentation examples in CI
- Lint Markdown in CI
- Deploy docs on every merge to main
- Track doc quality metrics
- Review docs in every code PR
Conclusion
Documentation-as-code means docs live where code lives, are reviewed like code, and are validated in CI. They can't rot because they're not separate. Update code, update docs, merge together. This is how production teams maintain accurate documentation in 2026.