Skip to content
Engineering

Making the go-migration docs tell the truth

A full accuracy audit of the go-migration documentation, plus LLM-friendly endpoints so machines read the same honest source as you do.

Andrian Prasetya Andrian Prasetya

When I introduced gopackx, I said open-swaggo existed “because I was tired of docs that lied about my API.” It turns out the documentation site for go-migration had quietly started lying too. So this week I sat down with the real source code on one screen and every docs page on the other, and went through all of it line by line.

This post is what I found and what I changed.

Docs drift is the default, not the exception

Documentation rots the same way orphaned libraries do: silently, and always in the direction of being wrong. A function grows a return error. An import path moves under /pkg. A method you swore existed — m.Install() — turns out to be something you imagined during an early design and never shipped. None of these break the build of the docs site, so nothing tells you. The page renders, the syntax highlighter is happy, and a reader copies an example that has never once compiled.

That was the state of a good chunk of the go-migration docs. Not catastrophic — the shape was right — but wrong in exactly the ways that waste a new user’s first afternoon.

What the audit fixed

I went through nearly everything under content/docs/** against the source. The concrete corrections:

  • Import paths now point at the real /pkg/... layout instead of the flattened paths the docs assumed.
  • Function signatures match reality — most notably the ones that return an error the old examples silently dropped.
  • The grammar/builder API (bp.Blueprint, bp.ID(), blueprint return values) is documented as it actually behaves, including the index and foreign-key methods.
  • MigrationStatus.Applied and the migration status surface are described with the real field names.
  • Hook signatures are correct, and the phantom m.Install() that never existed is gone.
  • CLI flags and error messages that were invented or stale are now the ones the binary actually prints.
  • Config format and environment variables match what the loader reads.

I also cleaned up the homepage: dropped a CTA banner and an install card that were more marketing than useful, and rewrote the code card so it uses the genuine API — bp.Blueprint, bp.ID(), returning the error like grown-up Go.

One language, end to end

A few pages still had leftover Indonesian prose from early drafts — the changelog and the migrator reference, mostly. I translated the remainder to English so the whole site reads in one voice. Small thing, but a docs site that switches languages mid-paragraph is its own kind of lie about how finished it is.

Closing the last gaps

The source had a few capable things the docs never mentioned, so I added them:

  • database.WithTransaction — the helper for running work inside a manual transaction — now has a documented home, and the batch-insert seeder page references it where it belongs.
  • schema.Raw() is noted in the column-modifiers page for the cases where the builder isn’t enough and you need to drop down to raw SQL.
  • A “Deterministic / Seeded Data” guide for the factory, because reproducible test fixtures shouldn’t be folklore passed between teammates.

And while I was in batch-insert, I fixed one more lingering index bug in an example. That’s the thing about going page by page: you find the last one only after you stop assuming it’s the last one.

Docs for humans and machines

The other half of this update is that the docs now speak to LLMs directly. If you’ve used a coding assistant against a docs site, you know the failure mode: the model scrapes rendered HTML, inherits the nav chrome and the cookie banner, and hallucinates the API anyway.

So go-migration’s docs now expose a clean, machine-readable surface:

  • /llms.txt — a structured index of the documentation, the convention agents look for first.
  • /llms-full.txt — the entire docs as one plain-text payload, for when you want to drop the whole thing into a context window.
  • Raw markdown per page — every doc page is available as its unrendered .mdx, via a rewrite so any URL can be fetched as source.
  • “Copy” and “Open in ChatGPT / Claude” buttons on each page, so handing a page to an assistant is one click instead of a copy-paste-and-pray.

The point is the same one that runs through all of gopackx: the machine should read the same honest source you do. If the human-facing example compiles, the LLM-facing one does too, because they’re the same bytes.

Why this is worth a whole post

Because “I updated the docs” undersells it. The work wasn’t cosmetic — it was making a promise true. A migration tool whose docs don’t compile is asking you to trust it with your production schema on the strength of examples it can’t back up. That’s exactly backwards.

If you’re using go-migration and an example bites you anyway, that’s now a bug, not a known hazard — and I’d like to hear about it on the repo. A docs problem with a copy-pasteable repro is, genuinely, one of the most useful things you can send a maintainer.