← All posts
Jun 14, 2026 · 13 min read · GitBook • docs migration • documentation drift

The GitBook migration trap

Moving your documentation to a new tool feels like progress the afternoon you start — your pages are in the new place by lunch. But the obvious way to migrate quietly carries the exact problem you were trying to leave behind. The fix isn't a better importer. It's a different definition of what "migrated" should mean.

Most teams that decide to leave GitBook — or Confluence, or a folder of Markdown, or any docs tool — reach for the same first step: export everything and import it into the new home. Page for page, heading for heading. It's the natural instinct, and every migration tool is built to reward it. Your content shows up intact, the table of contents matches, and the project looks done before the day is out.

The trouble is that "the pages moved" and "the documentation got better" are not the same thing. In most migrations they're barely related. To see why, it helps to ask a question the export-import workflow never asks: why are you leaving in the first place?

What you're actually running from

Almost nobody migrates docs because they dislike the rendering. They migrate because the docs stopped being true. A CLI flag was renamed and three pages still show the old one. An endpoint moved and the quickstart points at a 404. A default value changed two releases ago and the "Configuration" page never caught up. The content drifted away from the product it describes, and the gap got wide enough that the docs became a liability — sending users and support tickets in the wrong direction.

That drift is a structural problem, not a tooling one. Docs that live as hand-written prose, disconnected from the code they describe, fall behind the moment the code changes and nobody is assigned to chase every edit. The platform isn't what made them stale. The way they were maintained made them stale. Which means the choice of new platform, by itself, fixes nothing.

Why copy-migration imports the problem

The drift comes with the pages

When you export your docs and import them verbatim, you're not moving knowledge — you're moving a snapshot of prose that was already out of date. Every wrong flag, dead endpoint, and stale default makes the trip intact. You've relocated the drift into a new tool, where it now wears your new brand and looks freshly published. The first week feels like a win. The first time a user hits the same wrong example they hit last month, it's clear nothing was actually repaired.

Frozen islands in a living site

There's a second, subtler cost. Modern docs tooling increasingly keeps reference content in sync by regenerating it from the code — when a signature or an endpoint changes, the page changes with it. But a page you imported as hand-authored is, by definition, off-limits to that regeneration. The tool was told a human owns it, so the tool leaves it alone.

Carry a few hundred pages over that way and you've built a site with two maintenance models living side by side. Half the docs update themselves; half are frozen islands that rot exactly the way your GitBook did. Readers can't tell which is which. Maintainers have to remember which pages are "automatic" and which need manual chasing. That split is the precise failure you set out to escape, rebuilt inside the new tool on day one.

A better definition of "migrated"

The way out is to stop measuring migration by whether the pages moved, and start measuring it by two things that actually matter:

  • Does the new documentation cover the same topics your readers relied on — the concepts, the workflows, the reference surface?
  • Will it stay true as the product changes, without someone manually chasing every edit?

Notice what drops out of that definition: structural fidelity. Whether page 14 of the old site maps to page 14 of the new one is irrelevant. What matters is that the subjects survive and the content stays honest. Once that's the goal, a much better migration shape becomes available — one where the old docs play a different role than "the thing you move."

Treat the old docs as a seed, not a payload

Your existing documentation is enormously valuable — just not as the output. As input, it's the richest signal you have about what your product is, who it's for, and what the docs need to cover. The table of contents tells you the shape of the subject. The prose tells you the audience and the tone. The hard facts stated throughout tell you the constraints any new version must not contradict.

So use it for that. Read the old docs to distill a compact description of intent — product identity, audience, the topics that must be covered, the facts that must hold — and treat that as a seed, not a layout to reproduce. A seed steers what the new docs are about. It explicitly does not dictate that the new docs mirror the old structure heading-for-heading, because the old structure is part of what you're trying to improve.

Regenerate from the source of truth

With that seed in hand, generate the documentation fresh from the code itself — the actual functions, endpoints, commands, and types, steered by the intent you distilled from the old docs. The output covers the same topics your readers needed, but it's derived from the source of truth rather than from a years-old snapshot of prose. It's free to organize itself better than the legacy site, and — this is the part that pays off for years — it stays in sync, because it regenerates whenever the code changes. No frozen islands. One maintenance model for the whole site.

This inverts the usual migration. Instead of the old docs being the thing you carry forward and the code being an afterthought, the code becomes the thing you generate from and the old docs become guidance for that generation. The product's actual behavior, not last year's description of it, is what readers end up with.

Then measure what survived — the part everyone skips

Here's the objection, and it's a fair one: "Regenerate from code" sounds great until you realize you have no idea whether the new docs actually cover everything the old ones did. Verbatim import has one genuine virtue — you can at least see that every page made it across. Regeneration trades that visible completeness for freshness. Unless you close that gap, you're asking people to trust that nothing important got dropped.

So don't ask them to trust it. Measure it. "Did the migration keep everything?" is usually a gut call, but it doesn't have to be. Take the old documentation, break it into topics, and for each one check whether the regenerated docs cover it semantically — not whether the same words appear, but whether the subject is genuinely addressed somewhere. Embeddings find the closest counterpart for each old topic; a language model judges the ambiguous cases — covered, partially covered, or missing. The result is a coverage score and a concrete gap list: "the old ‘Webhook setup’ page has no counterpart in the generated docs."

That turns the scariest part of regenerating — "what did we lose?" — into a worklist instead of a worry. You see the number, you see the gaps, and you decide what to do about each one. Migration is "done" when coverage clears a threshold you set and the remaining gaps are consciously handled, not when an importer reports it copied N pages.

The exceptions, handled honestly

Some content genuinely can't be regenerated from code, and pretending otherwise is its own trap. Onboarding narrative, business context, the "why we built it this way" essays, conceptual guides that live above any particular function — the repository simply doesn't contain that information, so no amount of generation will produce it.

That's exactly what the gap list surfaces. Where a topic can't be derived from the code, you bring the old content over deliberately — as a hand-authored page you've consciously chosen to maintain. The difference from copy-migration is that this is the exception, flagged and intentional, not the default forklift. You end up with a small, known set of human-owned pages and a large, self-maintaining set derived from code — and you know precisely which is which, because you decided it topic by topic instead of importing everything and hoping.

Frequently asked questions

Isn't regenerating riskier than just importing my pages?

Importing feels safer because you can watch every page arrive, but it imports the staleness with them. Regeneration is only riskier if you can't see what it covered — which is why the coverage measurement is the essential half of the approach. With a coverage score and a gap list, regeneration is the safer option: you get docs that match the product today and a concrete account of anything that didn't carry over, rather than a pile of pages you have to re-verify by hand.

Will the new docs look completely different from my old ones?

The structure can change — and usually should, since the old structure is part of what you're improving. What's held constant is topic coverage, not page-for-page layout. If a particular organization matters to you, that's a constraint you put into the seed; everything else the generator is free to organize better than the legacy site.

What about my product's older versions?

Start with the current version. Code-derived docs accrue versions going forward — they regenerate at each release — so the cleanest path is to migrate the latest and let history build from there, rather than trying to forklift every historical snapshot at once.

How much work is this for my team?

Less than a verbatim migration, in practice. The expensive part of copy-migration is the cleanup afterward — re-verifying hundreds of imported pages against the live product. Seeding from your existing docs and regenerating from code front-loads the accuracy and hands you a gap list instead of a re-verification project. Your effort concentrates on reviewing the seed and closing the handful of genuine gaps.

Git2Docs is built around exactly this approach. Instead of copying your GitBook pages over, it reads them to seed a configuration — product identity, audience, the topics to cover, the facts to hold — then regenerates your documentation from your code so it's accurate today and stays in sync going forward. A coverage validator compares the original GitBook content against the generated docs and reports, topic by topic, what carried over and what didn't.

GitBook migration is available July 1, 2026. Read the launch announcement, or start a free trial to line up your migration with the first cohort.

Related

Start free — 30 days on usMore posts