PushBackLog

Documentation as Code

Soft enforcement Complete by PushBackLog team
Topic: management Topic: documentation Topic: delivery Skillset: engineering Skillset: engineering-management Technology: Markdown Technology: MkDocs Technology: Docusaurus Technology: GitHub Stage: execution Stage: delivery

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

  1. Documentation lives in the repository — alongside the code it describes
  2. Changes to documentation go through the same PR review process as code changes
  3. CI validates documentation — broken links, lint issues, and build errors fail the pipeline
  4. 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

TypeLocationContent
Module README/src/[module]/README.mdPurpose, 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 main via CI
  • PR template prompts engineers to update documentation with code changes