Thirty-six migrations, folded
0.18.1 folds Nibble's thirty-six migrations into one per table, each creating its table as it is now. The catch was the order: foreign keys decide which migration has to run last.
Mahfuz Shaikh2 min read

Nibble had thirty-six database migrations after a week. Most created a table. The rest added foreign keys, recreated the search index with a new column, renamed the identity tables, and added a column to sessions. Nobody was running Nibble except this site, so there was no history worth keeping. 0.18.1 folds them into one migration per table, each creating its table exactly as it is today.
Keeping the versions
A migration's timestamp is its version: it's what a database records when the migration runs. I kept each table's original timestamp. A database that's already on 0.18.0 has every one of those versions recorded, so it has nothing to run and doesn't notice the change. Only a database from before 0.18.0 has to be created afresh.
The order is the whole job
Folding would have been trivial if each table's foreign keys could sit in its own migration. They can't, because tables were created in an order that had nothing to do with which tables point at which. Drafts, entries and sessions were all created before users, and they all point at users.
The rule I settled on: a foreign key goes in whichever of its two tables' migrations runs later. So the keys pointing at users live in the users migration, and by then every table involved exists. That works on any database, not just SQLite, which forgives a key to a table that isn't there yet.

Proving it
I didn't trust myself with this one. So the check was: delete the schema file, build a database purely from the new migrations, dump its schema, and diff it against the old one. It matched, with one difference. Two Active Storage foreign keys had been declared twice, once by Rails and once by an old Nibble migration, and now they're declared once. Then the full CI run built a brand new site from the migrations and upgraded it.
Tidy history is nice. Knowing it produces the same database is the part that lets you sleep.