Picture someone still running version 1 of your product. They open your docs, search for the setting they need, and land on a page that describes version 2. The option was renamed. The page is accurate, just not for them. Then they ask the docs' AI assistant, and it answers confidently from the same version 2 page.
Nothing on that site is wrong. It just only knows about one release.
Documentation versioning means publishing the docs for each supported release side by side, so a reader can pick the version they run. You need it when people use an older release you still support and the differences would mislead them. You don't need it for every minor release. When you do version, keep the current release at the root so its URLs never move, put each older release under its own prefix like /v1/, and make search, AI chat, and anything agents read stay inside the version the reader picked. Since version 0.0.159, the free Doccupine CLI does this from one versions.json file and a folder per version. Here are the decisions, in the order you hit them.
Decide whether you need versioned docs at all
Versioning has a cost that is easy to underestimate. Every frozen version is a copy, and a copy is something you either maintain or knowingly abandon. Fix a typo in an install command and you now have to decide whether to fix it in three places.
So start with a narrower question. Would readers on an old release be misled by the current docs? A short test:
- Version when you still support a release that behaves differently: a renamed option, a removed endpoint, a changed default. Major releases of libraries, SDKs, and self-hosted software usually qualify.
- Don't version a hosted product where every customer runs the latest build. Those docs only ever describe one thing, so versions would be archives nobody can use.
- Don't version for small additive changes. A note like "Added in 2.3" on the page does that job without a second copy.
The common mistake is a version per minor release. You end up with eight entries in the switcher, seven of them nearly identical and slowly going stale. Version the releases where behavior actually diverges, which usually means major versions you still support.
Keep the current version at the root
Where versions live in your URLs matters more than it seems. If every version gets a prefix, including the current one (/v2/guides/intro), then every link anyone ever shared breaks or redirects the day v3 ships. Search engines have to relearn which URL is the real one.
I prefer giving the current release no prefix and putting a prefix on older releases only.
- docs
- index.mdx
- guides
- intro.mdx
- v1
- index.mdx
- guides
- intro.mdx
docs/guides/intro.mdx publishes at /guides/intro, and the frozen copy at docs/v1/guides/intro.mdx publishes at /v1/guides/intro. Links to the root keep pointing at whatever is current, which is what most people sharing a link meant anyway.
With the Doccupine CLI, a versions.json next to doccupine.json declares this:
[
{ "label": "v2.0", "default": true },
{ "slug": "v1", "label": "v1.0" }
]The default entry is the docs root and has no slug. Every other entry's slug is both its folder name and its URL prefix, and the order of the list is the order of the switcher. The file is opt-in: a site without it builds exactly as before.
Freeze a version by copying, then keep editing the root
There are two ways to keep old docs. One is a Git branch or tag per release, with the docs site built from several of them. The other is a folder per version in one branch. Branches keep the history clean, but the build has to stitch several checkouts together, and a fix to an old version means switching branches.
Doccupine uses folders. When a release ships, copy the docs root, minus any language or version folders, into a new folder named after the old version's slug. Add its entry to versions.json, then keep editing the root as the next release. If you skip index.mdx in the version folder, the site generates a home for it that redirects to its first page.
The upside is that everything is visible in one tree and one pull request. The downside is the cost from the first section. A frozen folder is real duplicated content. Decide when you create a version how long you will patch it. "Security and correctness fixes until v1 reaches end of life, nothing else" is a policy a small team can actually keep.
On the hosted platform, the Versions page under project settings does the copying. Create version snapshot takes a slug and a label, copies the current pages into the version folder, and adds the entry to versions.json. If the site is translated, it copies every language too. Like every dashboard edit, the snapshot lands as pending changes, so you review it before anything is published.
Make the switcher land somewhere useful
A version switcher has one hard case. The reader is on a page that doesn't exist in the other version. A new feature has no v1 page, and a removed feature has no v2 page.
In a Doccupine site, the switcher sits in the sidebar footer once two or more versions are configured. Switching takes the reader to the same page in the chosen version when it exists, and to that version's home page when it doesn't. The sidebar only lists the current version's pages, so a v1 reader never browses into v2 by accident.
That fallback is predictable, but it is not clever. It won't find the renamed equivalent of a page. If a page moved between versions, keep the old path in the new version as a short page that links to where the content went.
Keep search, AI chat, and agents inside the version
This is the step older docs setups never had to think about, and it is where the opening example goes wrong. A site can have perfect version folders and still answer every question from all versions mixed together, or only from the latest one.
In a Doccupine site, search results and AI chat answers come only from the version the reader is looking at. The same scoping reaches the tools developers use outside the browser. Each version gets its own llms.txt under its prefix, such as /v1/llms.txt, and the root file links to them. The MCP server has search_docs and list_docs tools that take a version parameter, so a coding assistant working in a v1 codebase can ask for v1 docs explicitly. When it doesn't ask, it gets the current version.
That default is the right one for most readers. It also means an agent that never passes a version will answer v1 questions from current docs. Pinning the version in the request is up to the agent or the person configuring it.
Combine versions with languages
If your docs are also translated, the two combine, language first and version second: docs/de/v1/guides/intro.mdx publishes at /de/v1/guides/intro. Both switchers sit side by side, and search and chat scope to the language and version together.
Before you add both, count the copies. Three languages and three versions is nine sets of pages. That's a lot to keep accurate, and a good reason to version less often.
What Doccupine doesn't do for you
A few limits worth knowing before you set this up:
- No automatic "older version" banner. An old version's pages look like current ones apart from the switcher label. If you want a visible warning, add a callout to the old version's pages yourself, at least on the home page and the most visited ones.
- Old versions are indexed. Each version's pages are in the sitemap and point to themselves as the canonical URL, so search engines can surface a v1 page. Keeping the current version at the root helps it collect links over time, but it doesn't stop an old page from ranking.
- The generated API reference is current-version only. An API reference generated from an OpenAPI spec is published in the default version. Hand-written pages inside a version folder work normally.
- Fixes don't propagate. Correcting the root doesn't touch
v1/. Whether a fix belongs in an old version is a judgment call, and nothing makes it for you.
Checklist
- Version only the releases where behavior differs and you still support users. Usually that means major versions.
- Current release at the root, older releases under a prefix, so shared links never break.
- Freeze a version by copying the docs, and decide upfront how long you will patch the copy.
- Switching versions lands on the same page or that version's home, never a dead end.
- Search, AI chat,
llms.txt, and MCP tools scoped to the reader's version. - Old versions marked clearly enough that nobody mistakes them for current.
- Retire a version when the release it describes is no longer supported.
That reader on version 1 doesn't need a smarter search engine. They need a site that knows which release they run, and keeps every answer inside it. Get the structure right and the rest is ordinary writing.
If you're deciding whether your docs need versions, or you're stuck on how many to keep, email [email protected]. I'd like to hear what you ship and how many releases your users are actually on.