Why migrations fail: duplicating the past and calling it modernization
- migration
- legacy systems
- AI
Migrations rarely fail on technology. They fail on a contradictory mandate: duplicate the old system exactly, and at the same time don’t repeat its mistakes. You can’t do both. Copying the system as-is also copies its defects — now on a new platform, with the budget already spent.
Why does a migration stall?
Almost every migration starts with two instructions that sound sensible on their own:
- “Make it work exactly the same. Don’t change anything.”
- “Let’s take the chance to stop carrying the mistakes of the past.”
Together they cancel out. Exact parity with the old system is, by definition, the preservation of its defects. Lift and shift carries the technical debt across intact: the same assumptions, the same bottlenecks, the same patches.
The project doesn’t die of the new platform. It dies of the contradiction.
Why is old code so hard to let go of?
Underneath the mandate there’s something more honest: old code is a monument. That module took eight months. It carries the judgment of people who have since left. It put out fires nobody remembers today.
Psychology already has names for this: the IKEA effect (Norton, Mochon and Ariely) —we overvalue what we build with our own effort— and sunk cost —we defend it precisely because we’ve already invested too much. The two compound each other.
So when someone suggests a tool could rewrite in days what took years of human work, the first reaction isn’t curiosity. It’s refusal. And it isn’t irrational: if the suggestion turns out to be true, it forces an uncomfortable question about the value of all that time.
It’s more comfortable to forbid touching any piece, demand an identical copy, and —when the project stalls— blame the migration.
Wasn’t the rule never to rewrite?
This is the strongest counter-argument, and it deserves respect. In 2000, Joel Spolsky wrote that throwing away the code and starting over is the single worst strategic mistake a software company can make. Netscape did it and went roughly three years without shipping. His underlying reason is still the best description of the problem:
Legacy code looks ugly because it is exhaustive. That strange conditional is there because a customer reported an edge case five years ago. When you throw the code away, you delete the solutions to problems nobody remembers exist.
That’s true. And it’s exactly why you shouldn’t rewrite blindly.
What changed?
The pattern. In 2004, Martin Fowler described the strangler fig: the new system grows alongside the old one and replaces it piece by piece, without stopping the business, and every step can be reversed. Big bang rewrites chasing full parity tend to fail; incremental replacements tend to work.
The cost of rewriting. In 2025, Google published how it uses LLMs for its internal migrations (ICSE 2025): roughly 50% less total migration time, with about 74% of code changes generated by the model, and migrations that had been stalled for years completed with a handful of engineers. The same study carries the caveat that gives it credibility: an LLM with simple prompting isn’t enough.
The asset was never the code
Spolsky never defended the code. He defended what the code remembers.
In 2000, the code was the only place that knowledge lived. That’s no longer true. A model can read the old system and extract that knowledge —the edge cases, the rules nobody ever wrote down— and turn it into tests and explicit criteria.
The asset was never the code. It was the knowledge. A migration that keeps the code but loses the knowledge has already failed. One that keeps the knowledge and discards the code has worked.
The years of human work aren’t lost: they’re cashed in. Turned into rules and into tests the new system has to pass. That honors them far more than leaving them frozen in a file nobody dares to open.
Duplicating the code vs. preserving the knowledge
| Migration that duplicates the code | Migration that preserves the knowledge | |
|---|---|---|
| What gets copied | The implementation, as-is | The rules, the edge cases, the judgment |
| The mistakes of the past | Travel across intact | Get seen, discussed, decided on |
| Parity | An act of faith | Tests the new system must pass |
| The team that built it | Guards the code | Is the source of the knowledge |
| Typical ending | Cost overrun, and “the migration” gets the blame | A new system the business understands |
How do you migrate without a big bang?
- Extract the knowledge before touching anything. The old system’s rules: edge cases, exceptions, decisions. An LLM is especially good at this, at a cost that didn’t exist before.
- Turn it into characterization tests. If the new system passes the tests that describe the old one’s real behavior, parity stops being faith and becomes verification.
- Replace piece by piece. The new system grows alongside the old one and cuts over component by component. Every cut, reversible.
- Let the model propose and a human decide. Gradual autonomy, with an auditable trail.
The warning sign
When a migration holds both “this has to come out exactly the same” and “let’s take the chance to fix what was wrong,” the project has already written its own cause of death. Technology won’t kill it: the contradiction will. And afterwards, the migration will take the blame.
Migrating isn’t moving code. It’s moving knowledge. The old code was the vehicle, never the cargo.
If you have a system nobody wants to touch and a migration that won’t start, message us on WhatsApp. We always begin the same way: understanding what your old system knows, before deciding what to keep.
Frequently asked questions
Why do so many system migrations fail?
Rarely on technology. They fail on a contradictory mandate: requiring the new system to behave exactly like the old one while also not carrying its mistakes forward. Exact parity with the old system is, by definition, the preservation of its defects.
Is it better to rewrite from scratch or migrate piece by piece?
Piece by piece. Big-bang rewrites chasing full parity usually fail; Martin Fowler's strangler fig pattern —the new system grows alongside the old one and replaces it component by component— lets every step deliver value and stay reversible, without stopping the business.
What do you actually lose when you throw away old code?
The knowledge, not the code. A strange conditional usually exists because someone reported an edge case years ago. That accumulated judgment is the real asset. If you extract it before migrating —into rules, specs and tests— the code stops being sacred.
Can AI do the migration?
It can speed it up a lot; it can't replace judgment. Google reported at ICSE 2025 roughly 50% less migration time using LLMs, with about 74% of code changes generated by the model, and it unblocked migrations stalled for years. The same study warns that an LLM with simple prompting isn't enough: it takes tooling, validation and human decisions.
How do you preserve the knowledge in an old system?
By reading the system and extracting its rules —edge cases, exceptions, decisions— and turning them into characterization tests. If the new system passes the tests that describe the old one's real behavior, parity stops being an act of faith and becomes something you can verify.