Every legacy modernization starts with someone saying the system is old, and stalls when someone else asks what that costs. Age is not the problem. A system that is stable, understood and cheap to run can be twenty years old and entirely fine. The problem is when the cost of keeping it alive starts eating the budget for everything else, and that is measurable.
What the evidence actually shows
The best public dataset on this is the US Government Accountability Office’s work on federal legacy systems, because it is a large, audited sample that private companies never publish.
In its July 2025 report, GAO reviewed 69 systems and singled out 11 across 10 agencies as most in need of modernization. Two Treasury systems were 59 and 51 years old, running COBOL and Assembly. Four systems depended on hardware or software the manufacturer no longer supports. Of the nine agencies that had a plan, only three had one containing all the elements GAO considers necessary.
That last figure is the one to sit with. These are not organizations that failed to notice. They are organizations that noticed, agreed something must be done, and still did not produce a complete plan. Modernization fails at the planning stage far more often than at the engineering stage.
The trap that keeps systems alive
This is why “we will modernize next year” is usually wrong. Next year the maintenance share is larger, the people who understood the system are fewer, and the unsupported components are more unsupported. The cost of the decision is not flat over time, which makes doing nothing an active choice with a price on it. The same dynamic, on a smaller scale, is why maintenance costs after launch deserve a line in the original budget.
The four treatments
These are not competing philosophies. They are tools, and a real programme uses several.
Wrap
- Put a clean API in front of the old system and leave the inside alone
- Cheapest and fastest. Nothing about the risk profile changes
- Right when the system works, is understood, and is simply hard to integrate with
- Becomes wrong when the platform underneath goes out of support
Migrate
- Same logic, newer platform. Lift to cloud, or a supported runtime
- Removes hardware and support risk without touching behaviour
- Right when the risk is the platform, not the design
- Does not fix a bad data model. It relocates it
Replace incrementally
- New services take over one capability at a time, behind a router
- Slower per unit of progress, and far lower risk
- Right for most systems that genuinely need to change
- Needs discipline: half-finished migrations are their own legacy
Rebuild
- New system built alongside, one switchover date
- The option with the worst track record, and the most appealing pitch
- Defensible when the old system is small, or genuinely beyond understanding
- Requires a frozen target, which businesses rarely provide
The rebuild is the one to be honest about, because it is the one everybody wants. It promises to convert a pile of accumulated compromise into a clean system on a known date. What it actually does is run two systems in parallel for years, freeze the old one while the business keeps changing, and concentrate every risk into a single switchover weekend. It is sometimes correct. It should never be the default, and anyone proposing it should be able to say why the incremental route was rejected.
How incremental replacement actually works
The pattern has a name and a history. Martin Fowler described the strangler fig in 2004, after the plants that grow around a host tree and gradually replace it. The mechanism is simple: put a routing layer in front of the old system, then move capabilities across it one at a time.
Two pieces make this work, and skipping either is how it goes wrong.
The router has to be real. Not a branch in application code. A layer that can send a request one way or the other as configuration, so a rollback takes seconds. If reverting a capability requires a deployment, the safety that justifies the slower approach is not there.
An anti-corruption layer protects the new code. Old systems carry data models shaped by decisions made decades ago, including fields that mean three different things depending on another field. If the new services speak that language directly, you have rebuilt the legacy system in a newer framework. A translation layer between the two keeps the old model from spreading, and it is deleted along with the last legacy component.
There is a failure mode specific to this approach, and it should be named: the programme stops halfway. Funding moves, priorities change, and the organization is left running two systems and a router indefinitely. That is worse than either endpoint. The defence is to sequence by capability rather than by technical layer, so every increment delivers something a stakeholder can see, and to treat the remaining legacy share as a tracked number that someone owns.
The part that is not code
Rewriting the logic is tractable. Discovering the logic is not, and that is where these programmes actually spend their time.
- Behaviour nobody documented. A report runs at 4am because a process downstream depends on it, and the person who knew that retired. Every legacy system has dozens of these, and they surface as production incidents after cutover rather than as requirements before it.
- Data that does not mean what the schema says. A column repurposed in 2011. A status code that is only valid for records created before a certain date. Migration reveals these. Nothing else does.
- Integrations you were not told about. Another team’s script reads the database directly. A partner pulls a nightly file. These are discovered when they break.
- Processes built around the defects. People have adapted to the quirks, sometimes profitably. Fixing a bug can break a workflow that depended on it.
Budget for the archaeology explicitly. A discovery phase on a legacy programme is not the same exercise as on a new build: it involves reading code, tracing database access and sitting with the people who use the system, and it produces a capability inventory rather than a feature list. It is also the single best predictor of whether the estimate will hold.
A sequence that works
- Inventory by capability, not by module. What does this system do for the business, and which parts of it carry the risk? Modules are an implementation detail. Capabilities are what you migrate.
- Score each capability on two axes. How much business value does changing it unlock, and how much risk does leaving it carry. Unsupported platforms and single points of knowledge are risk, whether or not anything is currently broken.
- Assign a treatment per capability. Most systems end up with a mix: wrap the stable parts, migrate the platform underneath, replace the two or three capabilities that are genuinely holding the business back.
- Put the router in first. It delivers nothing visible, and every subsequent step depends on it.
- Start with something valuable and reversible. Not the easiest thing, which proves nothing, and not the hardest, which stalls. Something a stakeholder will notice.
- Track the legacy share as a number. Percentage of traffic, or of capabilities, still served by the old system. Programmes without this metric are the ones that quietly stop.
On cost, the honest position is that legacy work is harder to estimate than new work, because the unknowns live inside the existing system rather than in the requirements. The ranges in what custom software actually costs apply, with wider error bars until discovery is done. If a supplier quotes a fixed price for a full modernization before reading the code, they are pricing an assumption.
Worth asking before any of it: does this capability need to exist at all? A modernization programme is the best opportunity you will get to retire things, and some of what the old system does is only done because it always was. The same reasoning applies as in build or buy: several capabilities in a twenty-year-old system are now commodity products.
We work on systems that are still running the business and cannot be switched off, which is most of them. If you have one and want an outside read on which parts deserve which treatment, describe what you are running. Our custom software team does this work alongside the people who currently keep the system alive, not instead of them.
Common questions
Should we rewrite or modernize our legacy system?
Rarely a full rewrite. A rewrite freezes the old system while the business keeps changing, runs two systems in parallel for years, and concentrates every risk into one switchover. Incremental replacement behind a routing layer keeps the old system serving the business while capabilities move across one at a time, and each move is individually reversible. Rewrites are defensible for small systems, or ones genuinely beyond understanding, but they should never be the default.
What is the strangler fig pattern?
An approach described by Martin Fowler in 2004 in which a routing layer is placed in front of an existing system and new services take over capabilities one at a time, until the old system handles nothing and can be decommissioned. It is named after the plants that grow around a host tree and gradually replace it. The value is risk: a bad migration of one capability is a routing change rather than an incident.
Why do legacy modernization projects fail?
Most often at the planning stage rather than the engineering stage. GAO found that of 11 critical federal legacy systems, only three had modernization plans containing all necessary elements and two had no plan at all. The other common causes are undocumented behaviour discovered after cutover, data that does not mean what the schema says, and programmes that stop halfway and leave two systems running indefinitely.
How much does legacy system modernization cost?
Wider ranges than new builds, because the unknowns sit inside the existing system rather than in the requirements. A discovery phase that reads the code, traces database access and inventories capabilities is what narrows the estimate, and it is the best predictor of whether the number will hold. A fixed price quoted before anyone has read the code is a priced assumption, not an estimate.
Sources
- US Government Accountability Office, Information Technology: Agencies Need to Plan for Modernizing Critical Decades-Old Legacy Systems, GAO-25-107795, 17 July 2025. Reviewed 69 systems and identified 11 across 10 agencies as most needing modernization. Oldest 60 years (Department of Defense). Two Treasury systems aged 59 and 51 running COBOL and Assembly. Eight of 11 using outdated programming languages. Four with unsupported hardware or software. Nine with documented plans, of which three contained all key elements, and two agencies with no plan.
- GAO reporting on the federal IT dashboard: about $83 billion, roughly 79%, of planned fiscal year 2025 IT spending across the 24 Chief Financial Officers Act agencies was for operations and maintenance. Federal IT spending exceeds $100 billion annually.
- Martin Fowler introduced the strangler fig pattern in 2004. The anti-corruption layer comes from domain-driven design and predates it.
- The federal figures are the largest audited public dataset of its kind. Private-sector maintenance ratios are not published at comparable scale, so treat the 79% as an illustration of the dynamic rather than a benchmark for your own organization.
Written September 2026.

