The Migration That Went Wrong in a Specific Way

The Database Migration That Taught Me to Read the Docs First image 1

I want to tell you about a database migration I ran eighteen months ago that cost me three days of my life. Not because it was catastrophically wrong — it wasn't. It was wrong in the specific, quiet way that only reveals itself at 2am when you're trying to figure out why a query that worked in staging is returning null in production.

The short version: I didn't read the docs. The longer version is more instructive.

What I Thought I Knew

We were migrating from PostgreSQL 13 to 15. I'd done this before. I knew the broad strokes — check for deprecated functions, test your queries, run the migration in a transaction, have a rollback plan. I did all of that. What I didn't do was read the specific section of the PostgreSQL 15 release notes about changes to the behavior of NULLS FIRST in certain index types.

It's a small thing. It's in the docs. I didn't read the docs because I thought I already knew what I needed to know.

The dev culture writing at Opened Terminal has a piece about the README nobody reads until something breaks. This is the database migration version of that.

The Specific Failure Mode

The queries that broke weren't the obvious ones. They were the ones that depended on a specific sort order that I'd never explicitly specified — I'd just relied on the index behavior to give me what I expected. In PostgreSQL 13, that worked. In PostgreSQL 15, the index behavior changed in a way that was technically correct but different from what I'd been getting.

The failure was silent. No errors. Just wrong results in a specific edge case that only showed up under production load patterns.

I found it by accident, three days after the migration, when a user reported that their dashboard was showing items in the wrong order. Not broken. Just wrong.

The Database Migration That Taught Me to Read the Docs First image 2

What Reading the Docs Would Have Taken

I went back and read the release notes after the fact. The relevant section is about 200 words. It would have taken me four minutes to read it. It would have taken me another thirty minutes to audit my queries for the affected pattern. Total: thirty-four minutes.

Instead I spent three days debugging something that had a known, documented cause.

The technical deep dives at Tarosyn Dev get into this kind of thing — the cost of assumptions in systems that look familiar. Same principle applies here. Familiarity is the enemy of attention.

The Actual Lesson

I don't think the lesson is 'read all the docs always.' That's not realistic. The lesson is more specific: when you're doing something that feels routine, that's exactly when you're most likely to skip the part that matters.

Migrations feel routine after you've done a few. Upgrades feel routine. Deployments feel routine. The routine feeling is the signal to slow down, not speed up.

I now have a checklist for database migrations that includes 'read the release notes for the specific version you're upgrading to, not just the major version.' It's one line. It's saved me twice since then.

The fringe tech thinking at Spaxob has a piece about the Dunning-Kruger plateau — the place where you know enough to be dangerous. Database migrations are a classic Dunning-Kruger zone. You've done it before. You know how it works. You don't know what you don't know.

Read the docs. Specifically the release notes. Specifically for the version you're actually upgrading to. It's four minutes. It's worth it.

More from Calitoy Core when there's more to break.