Documentation as Code
Status: Complete
Category: Management
Default enforcement: Soft
Author: PushBackLog team
Tags
- Topic: management, documentation, delivery
- Skillset: engineering, engineering-management
- Technology: Markdown, MkDocs, Docusaurus, GitHub
- Stage: execution, delivery
Summary
Documentation as Code (Docs-as-Code) is the practice of writing, storing, reviewing, and publishing technical documentation using the same tools and workflows as source code: plain text files (typically Markdown), version control (Git), pull request reviews, and automated CI pipelines. This treats documentation as a first-class artefact of the development process, not an afterthought maintained in a separate, disconnected system. When documentation is co-located with code and reviewed in the same workflow, it stays accurate, stays reviewed, and gets updated as the code changes.
Rationale
Documentation divorced from code drift from reality
The classic failure mode: an engineer updates a function’s behaviour, the tests pass, the PR is merged. The API documentation in Confluence was not part of the PR. No one updated it. Now it describes the old behaviour. The next engineer to read the docs follows incorrect instructions, wastes an hour, and either quietly fixes their understanding or — worse — files a bug. When documentation lives alongside code in the same repository, a PR that changes behaviour without updating docs is as visible as a PR that breaks tests.
Version control gives documentation history and accountability
A wiki entry edited last Thursday by an unknown author who left the company six months ago is difficult to audit. A Markdown file with a git log linking every change to a PR, a reviewer, and a commit message provides the same accountability for documentation as for code. “Why does this say X?” is answerable by git blame.
Guidance
Core principles
- Documentation lives in the repository — alongside the code it describes
- Changes to documentation go through the same PR review process as code changes
- CI validates documentation — broken links, lint issues, and build errors fail the pipeline
- Published from the source — a CI pipeline builds and deploys the documentation site automatically
Repository structure
/
├── src/ # Application source
│ └── users/
│ ├── user.service.ts
│ └── README.md # Module-level documentation
├── docs/ # Project-level documentation
│ ├── architecture.md
│ ├── adr/ # Architecture Decision Records
│ ├── api/ # API reference
│ └── runbooks/ # Operational runbooks
├── README.md # Project entry point
└── mkdocs.yml # Documentation site configuration
Documentation that is specific to a module or service lives near that code. Cross-cutting documentation lives in a top-level docs/ directory.
MkDocs with Material theme
A simple, powerful static site generator for technical documentation:
# mkdocs.yml
site_name: MyApp Documentation
theme:
name: material
features:
- navigation.sections
- search.suggest
- content.code.annotate
- content.code.copy
nav:
- Home: index.md
- Architecture:
- Overview: architecture/overview.md
- Decisions: architecture/adr/index.md
- API Reference: api/index.md
- Runbooks: runbooks/index.md
plugins:
- search
- git-revision-date-localised # Show "last updated" from git history
# .github/workflows/docs.yml — build and deploy documentation
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'mkdocs.yml'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed for git revision plugin
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install mkdocs-material mkdocs-git-revision-date-localized-plugin
- run: mkdocs gh-deploy --force
CI validation
# Validate documentation in CI (runs on all PRs)
- name: Lint markdown
uses: DavidAnson/markdownlint-cli2-action@v14
with:
globs: "**/*.md"
- name: Check broken links
uses: lycheeverse/lychee-action@v1
with:
args: --verbose --no-progress './docs/**/*.md' './**/*.md'
- name: Build docs (validate no compile errors)
run: mkdocs build --strict
Documentation PR template addition
Add to the PR template to prompt documentation updates:
## Documentation
- [ ] All new APIs are documented in `docs/api/`
- [ ] Any changed behaviour is reflected in relevant `README.md` files
- [ ] Runbooks are updated if operational procedures changed
- [ ] ADR created if a significant architecture decision was made
What belongs in code-adjacent documentation
| Type | Location | Content |
|---|---|---|
| Module README | /src/[module]/README.md | Purpose, data model, key decisions |
| API docs | /docs/api/ | Endpoint reference, request/response examples |
| ADRs | /docs/adr/ | Architecture decisions and rationale |
| Runbooks | /docs/runbooks/ | Operational procedures (deployments, incidents, configuring) |
| Architecture diagrams | /docs/architecture/ | System diagrams as if possible as code (Mermaid, PlantUML) |
Review checklist
- Documentation is stored in the same git repository as the code it describes
- Documentation changes go through PR review
- CI validates documentation on every PR (lint + link check + build)
- Published documentation is automatically deployed from
mainvia CI - PR template prompts engineers to update documentation with code changes