Every enterprise has at least one: a system that runs the business but nobody dares touch. It's slow, brittle, undocumented, and held together by tribal knowledge from two developers who left in 2019.
The answer seems obvious — modernize it. Rewrite it. Get off the old stack.
So the project kicks off. Six months later, it's still going. A year later, there's a scope freeze. Eighteen months in, the project gets quietly shelved, and the old system keeps running.
This is not rare. By most estimates, 70–90% of large-scale modernization efforts fail to deliver on their original promise — on time, on budget, or at all.
We've worked on enough of these to recognize the pattern. Here's what goes wrong, and what actually works.
The "Big Bang" Trap
The most common failure mode is treating modernization as a single, large project with a defined end state: "We'll rewrite everything in the new stack and switch over on a date."
This is almost always wrong.
The problem is risk accumulation. Every month the new system isn't live, you're paying for two systems — the old one still running, and the new one being built. Meanwhile, business requirements keep changing. By the time the rewrite is done, the spec from month one is obsolete.
The better mental model is continuous modernization: incrementally extracting and replacing components while keeping the system live. You never have a Big Bang cutover — you have a series of small, reversible deployments.
Nobody Owns the Data Migration
Technical rewrites get the attention. The data migration gets the spreadsheet.
This is backwards. In most legacy systems, the real complexity is in the data — years of inconsistencies, nullable fields that mean different things in different contexts, records that violate the new schema's constraints.
We've seen projects where the code rewrite took three months and the data migration took eight.
What to do instead: start the data migration design on day one. Run it in parallel against a copy of production early. You will find things that change your schema decisions — better to find them in week two than week thirty.
The Team Doesn't Include People Who Know the Old System
Product managers and architects who understand the desired end state plan the modernization. The developers who built and maintained the old system are kept on "keeping the lights on."
This splits the knowledge. The new team doesn't know what the old system actually does — not what the docs say it does, but what it actually does in production, including all the edge cases and workarounds accumulated over years.
The result: the new system misses behaviours that turn out to be load-bearing. Things break in production in ways that are completely predictable if you knew the history.
Fix: at least one person who deeply knows the old system must be embedded in the modernization team, not just "available for questions."
Measuring Progress by Code Written, Not Value Delivered
Modernization projects tend to report progress in percentage-complete terms: "We've rewritten 40% of the modules." This feels like progress. It isn't, until something is live and delivering value.
The healthier metric is: what can users or the business do today that they couldn't do before this project started?
If the answer is "nothing yet," that's a risk signal — not necessarily a problem, but something to actively manage.
What Actually Works
The pattern we've seen succeed consistently:
-
Strangler Fig over Big Bang. Identify the highest-value, lowest-risk component of the old system. Extract it, build the new version, route traffic to it, validate it works. Repeat.
-
Dual-write early. Before you decommission any old component, write to both old and new systems and compare outputs. This builds confidence and surfaces discrepancies before they become production incidents.
-
Treat data as the primary constraint. Design the data migration first. Let it inform architecture decisions, not the other way around.
-
Define "done" at the component level, not the project level. Each extracted component should have its own definition of done — fully live, monitored, old code deleted.
-
Keep the business informed in business terms. "We migrated the order processing module and it's handling 100% of live traffic with a 40% latency improvement" lands differently than "we completed the Q2 migration milestone."
Legacy modernization is hard because it's never purely a technical problem. It's a coordination problem, a risk management problem, and a communication problem that happens to involve code.
The teams that succeed treat it that way.
Have a legacy system you're trying to modernize? We've been through this a few times. Book a call — happy to talk through your specific situation.