Back to Blog
documentation best-practices workflow

How to version your documentation (without losing your mind)

Most teams ignore docs versioning until they have three major versions live and no one knows which page is current. A practical system for versioning docs alongside your product.

Anna Oleksenko
Anna Oleksenko
Co-founder & COO · · 9 min read
How to version your documentation (without losing your mind)

The first sign your docs versioning is broken is usually a support ticket. A customer on v2 pastes an API call from your docs. It fails. You look at the docs URL. It is the v3 page. The parameter they used was renamed six months ago. The docs never branched when v3 shipped.

This is the moment teams realize they have been treating versioning as a future problem. It is now the present problem.

The counterintuitive truth: docs versioning is harder than code versioning, not easier. Your code gets a git tag and a branch. Your docs need a parallel structure that maps to every major version still in use, updated every time any of those versions changes, with a navigation layer that lets users find the right one without thinking. Most documentation platforms bolt this on as an afterthought. Most teams implement it even later than that.

Here is a practical system that actually holds.

When you actually need versioning (and when you do not)

Not every product needs versioned docs. A SaaS app with a single current version and automatic upgrades does not need it. If your users have no choice about which version they are on, there is only one docs page that matters.

You need versioning when any of the following are true:

  • You publish a public API and customers integrate against specific versions.
  • You ship installable software (on-prem, self-hosted, CLI tools) where users stay pinned to older releases.
  • You have a mobile SDK and app stores delay rollouts — a customer on iOS 3.1.2 still needs docs for 3.1.2.
  • You have enterprise customers with contractual stability guarantees.
  • You deprecated something in a major version and the old behavior is still actively used.

The rule of thumb: if a customer can be on a version that is not your current one, you need versioned docs. If everyone is always on your latest, you probably do not.

The spectrum of versioning complexity

There is no single “correct” approach to docs versioning. There is a spectrum, and the right level depends on how many versions are actively used and how fast your product moves.

Level 1 — Version banners. Add a banner to old pages: “This page covers v2. [View the v3 version →]”. Zero infrastructure. Manually maintained. Works for one or two legacy versions where the diff is small.

Level 2 — Separate URL paths. /docs/v2/ and /docs/v3/ are different static trees. A version selector in the nav lets users switch. The most common approach for docs platforms.

Level 3 — Branch-based versioning. Each version lives on a git branch. Merging a change to version/2.x rebuilds the v2 docs site. The same way you ship patches to old software releases.

Level 4 — Content variants with a single source of truth. One content file, with version-tagged blocks that render conditionally. Complex to author but eliminates duplication.

Most teams doing this well land at Level 2 or Level 3. Level 4 sounds appealing but becomes unmaintainable quickly. Level 1 buys time, not a system.

The folder structure that scales

For teams building a static docs site (Astro, Next.js, Docusaurus, plain Markdown), a versioned folder structure is the most transparent approach. Here is one that works:

docs/
├── current/          ← always latest, redirects to versioned slug on publish
│   ├── getting-started.md
│   ├── api-reference.md
│   └── changelog.md
├── v3/               ← frozen when v4 ships
│   ├── getting-started.md
│   ├── api-reference.md
│   └── changelog.md
└── v2/               ← frozen, low-traffic, minimal maintenance
    ├── getting-started.md
    └── api-reference.md

When v4 ships:

  1. Copy current/v3/ (this freezes the v3 snapshot).
  2. Start writing v4 content in current/.
  3. Add v3 to the version selector.
  4. Redirect /docs/ to /docs/v4/ or keep current/ as the default.

The invariant: current/ always reflects what you are actively developing. Versioned folders are frozen snapshots. You only touch an old version folder when you need to backport a correction.

The five mistakes teams make

Mistake 1: versioning the whole site when only part of it changes

❌ Copying every page into every version folder, including your marketing-adjacent content.

✅ Only version the content that actually differs between releases: API reference, SDK docs, configuration options, migration guides. Your “About” page, pricing, and conceptual overviews are not versioned.

Mistake 2: never deprecating old versions

❌ Keeping v1 docs fully live, indexed, and equal in the nav five years after v1 EOL.

✅ Set a deprecation policy. Common thresholds: when a version hits EOL, add a banner warning and a sunset date. When it crosses sunset, add noindex to the old pages, remove from the version selector, keep the URLs live (do not 404 paying customers), but stop spending editorial time on them.

Mistake 3: the version selector lives only in the header

❌ A version dropdown only at the top of the page. User reads half the page, does not realize they need the other version, hits a dead end.

✅ Put version context inline on the page — a callout near the top of API reference pages and any page with a breaking change: “You are reading the v2 docs. This endpoint was renamed in v3.” One sentence prevents ten support tickets.

Mistake 4: not maintaining a clear changelog per version

❌ A single changelog page that lists everything in reverse chronological order.

✅ A per-version changelog clearly separated from other versions. When a v2 customer reads the changelog, they should only see changes relevant to v2. A monolith changelog on a product with three live versions is unreadable.

Mistake 5: no policy for backporting corrections

Without a written policy, docs teams either update every version every time (too much work) or update only current (leaves users on old versions with wrong information).

A policy that works for most teams:

  • Bug fixes (factual errors, broken code samples): backport to all supported versions.
  • Clarifications: backport to the last major version, optional for older.
  • New content (features, guides): current only, no backport.
  • Security corrections: backport to all versions, no exceptions.

Write it down. Make it the default answer when someone asks “do I need to update v2 too?”

A before/after: the API reference problem

Docs versioning breaks most visibly on API reference. Consider what happens without it:

Before (no versioning):

GET /api/users/{id}/settings

Parameters:
  theme: string   (light | dark)
  notify: boolean

You ship v3. notify is split into notify_email and notify_sms. The endpoint is the same but the parameter is gone. Someone updates the docs. The page now reflects v3. Every v2 customer hitting that page is confused. Support load goes up.

After (versioned docs):

# /docs/v2/api/users-settings
GET /api/users/{id}/settings

Parameters:
  theme: string   (light | dark)
  notify: boolean
# /docs/v3/api/users-settings
GET /api/users/{id}/settings

Parameters:
  theme: string   (light | dark)
  notify_email: boolean
  notify_sms: boolean

> Breaking change from v2: `notify` was split into `notify_email` and `notify_sms`.
> See the migration guide.

Same endpoint, two pages, zero ambiguity. The v2 customer finds their page. The v3 customer finds theirs. The migration guide exists and is linked from the diff.

The cost is the duplication. The benefit is that support tickets drop and customers trust the docs.

Where automation fits

The hidden cost of versioned docs is maintenance overhead. Every time you fix a typo in current/, you might also need to fix it in v3/ and v2/. Every time an endpoint changes, you update the current reference and potentially backport a note to older versions.

This is where tooling pays off. GitDocAI connects to your repository and tracks file changes across branches. When you merge a correction to current/, it surfaces a pending change for each older version branch you have configured, pre-drafted, ready for one-click review. You are not doing the diff yourself. You are reviewing what the tool found and deciding whether to apply it.

The workflow shifts from “remember to check every version” to “review the list of proposed backports”. That second workflow survives a busy sprint. The first one does not.

The versioning trigger: when to cut a new version

Teams wait too long to cut a new docs version. The right trigger is not “we shipped a lot of things”. The right trigger is a breaking change.

A breaking change is anything that causes existing integrations to fail or behave differently without code changes on the customer’s side:

  • Removing or renaming a parameter.
  • Changing a response shape.
  • Deprecating an endpoint with a replacement.
  • Changing authentication flow.
  • Removing a supported value from an enum.

Non-breaking additions (new optional parameters, new endpoints, new fields in responses) do not need a new version. They go in current/, documented as additions, available to all versions via your upgrade path.

The moment you ship a breaking change, you have two audiences: people on the old behavior, and people on the new. That moment is when you cut the version.

If you wait until after the release to version the docs, you have a window where both audiences hit the same page. That window is where the confusion and tickets come from.

The version selector UX decisions that matter

Your version selector will either prevent confusion or cause it. A few non-obvious decisions:

Default to the latest version, but remember the selection. If a user is reading v2 docs and navigates to another page, they should stay in v2 docs. Do not reset to current on every page load. Store the selection in a cookie or URL parameter.

Make the current version obvious. “v3 (latest)” is better than “v3”. Users who land on old versions from Google should immediately know they are not on the latest.

Preserve the path on version switch. If a user is reading /docs/v2/api/authentication and switches to v3, take them to /docs/v3/api/authentication, not the v3 home page. If that page does not exist in v3, take them to the v3 home page — but try the path first.

Show version in the page title. Authentication | v2 Docs vs Authentication | Docs. Search engines index both. Users who land from search should see the version in the browser tab before they read a single word.

The minimum viable versioning policy

If you are starting from zero and need something working this week, here is the minimum:

  1. Create a vX folder for your current docs and start treating current/ as the working draft.
  2. Add a version banner component to every page that shows “Version: vX | [Latest: vY →]” when the page is in an older version folder.
  3. Set up a redirect from /docs/ to /docs/vX/ (or the latest).
  4. Write down your backport policy in one paragraph and link it from your contributing guide.
  5. Add <meta name="robots" content="noindex"> to version folders older than your deprecation threshold.

That is not the final system. That is enough to stop the bleeding while you build the right system.

The long game

The teams that get docs versioning right share one trait: they treat the docs the same way they treat the code. You would not ship a v3 API change without tagging the release in git and keeping the old branch alive for hotfixes. The docs release should follow the same lifecycle.

When docs versioning is tight, something else happens: your changelog becomes trustworthy. Customers can read it, understand exactly what changed and in which version, and make upgrade decisions with confidence. That is not just a docs win — it is a trust win.

If your current docs setup makes versioning hard, or if you are manually tracking which pages need updating across three version branches every sprint, take a look at GitDocAI. We built the automation layer for this exact workflow — branch-aware sync, backport suggestions, and version-scoped publishing — so the maintenance work stops competing with the writing work.

Keep reading