Frameworks are replaceable. A production database with eight years of data in it is not, and most modernisation projects live or die on how carefully that part is handled.
Read the constraints before the tables
Foreign keys, unique indexes and check constraints tell you more about the actual business rules than any document does, because nobody bothers to keep the document current once the constraint exists. We export the schema and read the constraints first, the column names second.
Find the columns that mean three things
Every legacy system has at least one column that has been repurposed over time — a status field with values that only made sense under a process nobody runs any more, a text column holding structured data because a migration never happened. These are the columns that break new code in ways that pass every test written against clean sample data.
- Grep the application code for every place a column is read, not just written.
- Sample the actual distribution of values, not the documented enum.
- Ask who is still writing to it — a batch job, a spreadsheet import, a partner integration nobody remembers commissioning.
Migrate data before you migrate logic
We move data into the new shape first, behind a feature flag, and run both systems against it in parallel before any user-facing change ships. This catches the mismatches — a nullable column that turns out never to be null in practice, a currency stored as a string with three different formats — while there is still an old system to compare against.
A migration script that has never run against production data is a hypothesis, not a plan.
The rollback that actually gets tested
Every migration gets a rollback script, and the rollback script gets run in staging before the forward migration does. Teams write forward migrations carefully and rollback migrations in a hurry, on the assumption they will never be needed. They are needed often enough to be worth the same care.
What this costs and what it buys
Doing this properly adds a week or two to a modernisation project that could otherwise have started coding on day three. On every project where we skipped it under time pressure, we paid the week back later, usually with interest, usually in production.
