Skip to content
ExponaBLOG
GIT & GITHUB FOR PROFESSIONAL DEVELOPERS · DAY 079 / 100

Using Git for documentation: what developers should know

Docs as code.

Expona Admin7 min read

Documentation often lives outside your codebase—in wikis, shared drives, or scattered across Slack threads—making it easy to fall out of sync with the code it describes. Using Git for documentation: what developers should know is essential because it treats docs exactly like code: versioned, reviewed, and deployed automatically. We'll walk through how to integrate documentation into your workflow so it stays current, stays accurate, and stays useful.

Docs in the repo — Change docs in the same PR as code

The simplest and most effective practice is keeping documentation alongside your code in the same repository. When you modify an API endpoint, update the database schema, or change authentication logic, you update the corresponding docs in the same pull request. No separate process, no "we'll document it later" promises that never happen.

Consider a team building a payment service. Your feature branch includes:

bash
feature/add-refund-api/
  ├── src/
  │   ├── services/
  │   │   └── refundService.ts
  │   ├── routes/
  │   │   └── refundRoutes.ts
  │   └── tests/
  │       └── refund.test.ts
  └── docs/
      └── api/
          └── refund-endpoint.md

Your refund-endpoint.md describes the new /refunds/create endpoint, includes request and response schemas, error codes, and example cURL commands. When you open the PR, both the implementation and documentation are up for review together. A teammate who reviews the code also reviews the docs—and they're reviewing them against the actual implementation happening in the same commit.

This approach scales across teams. Whether you're documenting REST endpoints, database migrations, infrastructure changes, or CLI commands, everything lives in version control. You can use a /docs folder at the repo root, or organize by feature:

bash
docs/
  ├── getting-started.md
  ├── api/
  │   ├── authentication.md
  │   ├── users.md
  │   └── payments.md
  ├── deployment/
  │   ├── docker.md
  │   └── aws-setup.md
  └── guides/
      ├── local-development.md
      └── debugging-tips.md

One concrete benefit: your docs are not a separate artifact. They're part of your source. When someone checks out a release tag from two years ago, they get the docs that were accurate for that release. No hunting through old wiki pages or deprecated internal docs.

Review docs — Reviewers check docs like code

Code review catches logic errors, security issues, and performance problems. Doc review—when it happens—should catch clarity issues, outdated examples, and gaps in explanation. By treating docs as code in the review process, you ensure quality on both fronts.

When a reviewer looks at your PR, they should ask about docs the same way they ask about tests:

  • Does this PR add new features? The docs should be updated.
  • Does this PR change how something works? The relevant docs should reflect that.
  • Are the examples still accurate after this change? Verify them.
  • Is the documentation clear enough that a junior engineer could follow it?

In practice, this means your code review checklist looks like:

Code review checklist: Does the PR include code changes without corresponding doc updates? If yes, ask the author to add them before merging.

Real example: Your team is refactoring the authentication flow from session-based to token-based. The code PR is solid—tests pass, security review clears it. But the docs/guides/authentication.md still describes the old session flow. A good reviewer catches this and asks the author to update it. The PR doesn't merge until docs are current.

Some teams make this even more explicit by requiring a "documentation" label on PRs that touch documented features. Others use commit hooks or CI checks to flag when a PR modifies code in certain directories without touching corresponding docs.

The payoff: your documentation accuracy rate increases because reviewers treat it as a quality gate, not an afterthought. Over time, you build a culture where "we document as we go" becomes the norm.

Publish automatically — Build sites from Markdown on merge

Once your docs are in Git, you can automate publishing. Every time you merge a PR, a CI/CD pipeline builds your documentation site and deploys it. No manual upload, no "deploy the docs" checklist item—it happens automatically.

Most teams use a static site generator for this. Popular choices include MkDocs (Python), Docusaurus (JavaScript/React), Hugo (Go), or Jekyll (Ruby). You write docs in Markdown, the tool converts them to HTML, and a CI pipeline publishes the result.

Here's a simple GitHub Actions workflow that builds and deploys MkDocs:

bash
name: Deploy Docs

on:
  push:
    branches:
      - main
    paths:
      - 'docs/**'
      - 'mkdocs.yml'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      - run: pip install mkdocs mkdocs-material
      - run: mkdocs build
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./site

When you merge to main, GitHub Actions runs this workflow. It installs dependencies, builds your docs into static HTML, and pushes them to GitHub Pages (or any hosting). Your docs site is live within minutes. No manual steps, no delay between code merge and doc publication.

This workflow scales across organizations. Merged a new API endpoint? The docs update automatically. Fixed a typo in the deployment guide? Site is updated without asking anyone. Team members always see the latest docs—no stale information lingering.

For teams using internal doc sites (not public GitHub Pages), you can use AWS S3 + CloudFront, Netlify, Vercel, or your own static hosting. The principle is the same: commit and merge docs, CI builds and publishes automatically.

Version docs — Docs per release tag

Here's a scenario: your product is at version 2.5, but some enterprise customers still run version 1.8. Their API is slightly different, their deployment steps are different, and their troubleshooting advice is different. You need version-specific documentation.

Git tags and branches make this possible. When you tag a release, you capture docs at that point in time. Later, when a user lands on your docs site, they can select their version and see docs accurate to that version.

MkDocs with the mike plugin handles this elegantly. After tagging a release, you build docs for that tag and serve them under a versioned URL:

bash
# Tag your release
git tag v2.5.0

# Check out the tag and build docs for it
git checkout v2.5.0
mike deploy 2.5.0 latest --push

Now your docs site has a version selector. Users see docs.yourcompany.com, pick "2.5" or "1.8" from a dropdown, and get docs accurate to that version. Each version has its own frozen copy of the docs—no confusion.

Another approach: maintain docs in a separate branch per major version. The docs-v2 branch holds docs for v2.x, docs-v3 holds docs for v3.x, and so on. You backport important updates across branches when they apply to multiple versions.

For internal tools and products with a single current version, versioning may be simpler: just keep one set of docs in main, and older versions are archived in Git history (users can check out old tags if needed). For public APIs and multi-version products, automated versioned docs become essential.

Common mistakes

  • Docs in a separate repo. We've seen teams store code in one Git repo and docs in another. They quickly get out of sync. Keep them together so code review naturally includes docs review.
  • Docs as an afterthought. Documenting "when we have time" means docs are usually outdated. Make docs a requirement, not a bonus. Reviewers should block PRs that change functionality without updating docs.
  • No doc versioning for multi-version products. If your product has multiple supported versions, version your docs. Users checking old versions should see accurate, not current, information.
  • Overly complex doc structure. Avoid deeply nested folder hierarchies. Keep your /docs folder simple and searchable. Three to four levels deep is usually enough.
  • Forgotten automatic publishing. If publishing is manual, it won't happen consistently. Automate it in CI/CD so merging automatically triggers a publish. No human step, no excuses.
  • Docs without examples. Conceptual docs are useful, but code examples are essential. Include real command-line examples, cURL requests, code snippets—things users can copy and run.

Key takeaways

  • Store docs in the same repo as code and update them in the same PR. This keeps docs in sync with reality.
  • Treat doc updates as a code review requirement. Reviewers should flag missing or outdated docs just as they would flag missing tests.
  • Automate doc publishing with CI/CD. Build and deploy docs automatically when you merge. No manual steps, no delays.
  • Version your docs if your product supports multiple versions. Use Git tags or branches to maintain version-specific documentation.
  • Use a static site generator (MkDocs, Docusaurus, Hugo) to convert Markdown to HTML and handle versioning.
  • Keep doc structure simple and searchable. Avoid unnecessary nesting and make examples concrete and runnable.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles