The migration went perfectly. Every row transferred. Zero data loss. The rollback plan was never needed. We celebrated with bad coffee in the server room at 4am.

The Database Migration That Taught Me to Fear Success - image 1

Three weeks later, we found the first assumption.

What "Perfect" Actually Means

A database migration is perfect when it moves data from A to B without losing anything. That's the definition we were working with. It's not wrong, exactly. It's just incomplete in ways that only become visible after the fact.

The first assumption we'd gotten wrong: we assumed the old schema's implicit constraints were documented. They weren't. They were in the heads of two engineers who'd left the company eighteen months earlier. The new schema enforced different constraints. The data moved fine. The behavior changed.

I've written about this kind of thing on dev culture before — the gap between what the system does and what anyone thinks it does. This was a textbook case.

The Second Assumption

The second assumption was about read patterns. We'd profiled the write load carefully. We'd indexed for the queries we knew about. What we hadn't accounted for were the queries that ran once a month — the reporting queries, the audit queries, the "I just need to check something" queries that nobody had thought to mention during planning.

These queries had been slow on the old system too. Nobody had complained because they'd always been slow. On the new system, they were slower. Now people complained.

The technical experiments has some good writing on this — the way systems reveal their assumptions under load. We were experiencing that in real time.

The Database Migration That Taught Me to Fear Success - image 2

The Third Assumption

The third assumption was the one that hurt. We'd assumed that "the same data" meant "the same behavior." It doesn't. Data has context. The context lives in the application layer, in the business logic, in the mental models of the people who use the system every day.

Moving the data moved the data. It didn't move the context.

We spent six weeks after the migration fixing things that weren't broken in any measurable way but felt wrong to the people who used them. That's a real cost. It doesn't show up in the migration metrics.

The lesson I took from this: a successful migration is one where you've identified your assumptions before you start, not after. We're better at that now. We have a pre-migration assumption audit that we run on every significant schema change. It's not perfect. But it's better than celebrating at 4am and finding out three weeks later what you missed.

More on systems thinking at this blog and fringe systems — the places where we're working through what it means to build things that actually hold.