Every operator inherits a system they did not build. It works, mostly. It also takes three weeks to add a field, nobody left at the company understands the billing logic, and the last person who did left in 2021. Somebody in the room says the word “rewrite,” and everyone nods, and eighteen months later the new platform is 70% done and the old one is still running production.
Legacy system modernization fails far more often as a planning problem than as an engineering problem. The engineering is usually tractable. What sinks these programs is scope chosen by instinct rather than evidence — teams rebuild the parts that are annoying to work in instead of the parts that are actually costing money or blocking revenue. The fix is unglamorous: triage every component against a single question, then sequence the work so the business never has to stop while you do it.
Start with the carrying cost, not the code
Before you scope anything, price what the current system costs you to keep alive. Most teams have never done this, which is why modernization budgets get argued on aesthetics.
The macro numbers are sobering. The Consortium for Information & Software Quality estimated the cost of poor software quality in the US at $2.41 trillion annually, with roughly $1.52 trillion in accumulated technical debt — a future bill, not an annual one, but a bill nonetheless. In the public sector the pattern is starker: the US Government Accountability Office found that about 79% of planned federal IT spending for FY2025 — roughly $83 billion — went to operations and maintenance rather than new capability. North of the border, Canada’s Auditor General reported that only 38% of government applications were in healthy condition as of 2023, against a target of 60% by 2030.
Those are large institutions, but the mechanics are identical at 30 people. Your version of that number is concrete and findable in about a week:
- Direct run cost. Hosting, licenses, vendor support contracts, the one expensive database you can’t downgrade.
- Maintenance drag. What percentage of engineering hours went to keeping the old system upright last quarter versus shipping something new? Pull it from your ticket system, not from memory.
- Velocity tax. How much longer does a typical change take in the legacy component than in your modern code? A 3x multiplier on half your roadmap is a real cost even though it never appears on a P&L.
- Revenue blocked. Deals lost or delayed because the system can’t support SSO, an audit trail, a data residency requirement, or an API the buyer expects.
- Risk exposure. Unsupported dependencies, unpatchable components, compliance findings you keep deferring.
The last two items usually dwarf the first three, and they are the ones that justify capital. “Our monolith is unpleasant” does not get funded. “Three enterprise deals stalled last quarter on audit logging we cannot build in the current architecture” gets funded in a week.
Rebuild, wrap, or retire: triage before you scope
Once you have the carrying cost by component, sort every meaningful piece of the system into one of three buckets. The goal is to make the expensive bucket as small as honestly possible.
Retire
Start here, always, because it is free. In every system we have taken over there are features nobody uses, reports nobody reads, integrations pointed at vendors the company left years ago, and admin tooling built for a workflow that changed in 2022. Instrument usage, wait four to six weeks, and delete what nothing touches. Every component you retire is one you never have to rebuild, test, document, or run.
Wrap
The middle bucket is the one most teams underuse. If a component is stable, rarely changed, and correct — an old pricing engine, a settlement calculation, a state machine that has been battle-tested by a decade of edge cases — the right move is often to leave the implementation alone and put a modern, well-documented API in front of it. You get the integration surface you need, your new code stops depending on the old code’s internals, and you defer the rebuild indefinitely. Sometimes forever, which is a win.
Wrap is the right call when the code is ugly but the behavior is valuable. That old module encodes years of hard-won business rules that exist nowhere else — not in a spec, not in anyone’s head. Rewriting it means rediscovering those rules by breaking things in production.
Rebuild
Reserve this for components where the architecture itself is the constraint: it cannot scale, cannot be secured, cannot support the compliance posture your buyers require, or changes so frequently that the velocity tax compounds. In our experience that set is consistently smaller than the first proposal on the whiteboard suggests.
And when you do rebuild, rebuild in slices, not all at once. Route one capability at a time through the new implementation behind a facade, run both paths in parallel long enough to compare outputs on real traffic, then cut over and delete the old path. The pattern is old and well documented, and it works because every step is independently reversible. A big-bang rewrite has exactly one moment of truth; a sliced migration has fifty small ones, and you learn something from each.
Where AI helps, and where it quietly hurts
AI-assisted development has changed the economics of modernization work — but not evenly across the lifecycle.
It helps most in discovery. The hardest part of modernizing an unfamiliar system is understanding it: mapping dependencies, finding hidden coupling, identifying architectural seams, and extracting business rules from code nobody has read in years. That work used to take senior engineers months. Model-assisted code analysis compresses it substantially, and unlike generated code, a wrong answer in discovery surfaces the moment you test it.
It helps in characterization testing. Before you touch a legacy component, you want tests that pin down what it currently does — including the bugs, which some customer is certainly depending on. Generating that suite is tedious, mechanical, high-volume work. It is close to an ideal use of the tooling.
Be more careful with generated production code. Google’s 2025 DORA research found AI adoption at roughly 90% among software professionals and, for the first time, correlated with higher delivery throughput — but also with higher delivery instability. Their framing is the useful one: AI amplifies whatever your organization already is. Teams with strong review, test, and rollback discipline get faster. Teams without it ship defects faster. The same report found about 30% of developers have little or no trust in AI-generated code.
The implication for your schedule: you can move much faster on understanding and testing the old system than you could three years ago, and only somewhat faster on safely replacing it. Do not let a fast discovery phase seduce you into an aggressive cutover.
Check whether the work is already partly funded
A detail founders on both sides of the border routinely miss: some modernization work may fall inside existing R&D incentives.
In the US, the One Big Beautiful Bill Act restored immediate expensing of domestic research and experimental costs, including software development, for tax years beginning after December 31, 2024, reversing the amortization regime that squeezed a lot of small software companies. In Canada, the federal government raised the SR&ED expenditure limit eligible for the enhanced 35% credit from $3 million to $4.5 million, with a further increase to $6 million and broadened eligibility proposed in Budget 2025.
The caveat matters: routine migration and maintenance generally does not qualify — the work has to involve genuine technological uncertainty and systematic investigation. But if part of your program involves solving a problem with no known solution, that portion may be treated differently than the rest. This is not tax, legal, or financial advice; talk to a qualified professional about your specific situation before you plan around it.
The first ninety days
A sequence that consistently works:
- Weeks 1–3: Instrument everything. Usage by feature, change frequency by module, incident history, direct run costs. Decisions from data, not opinion.
- Weeks 3–5: Price the carrying cost per component and sort into retire, wrap, rebuild. Write it down as a one-page document a non-engineer can read.
- Weeks 5–7: Execute the retire list. It is fast, it is free, and it shrinks everything downstream.
- Weeks 6–10: Build characterization tests around the components you plan to touch. This is your safety net; everything after it depends on it.
- Weeks 8–12: Ship the first rebuild slice end to end — the smallest one that proves the pattern, running in parallel with the old path.
Twelve weeks in, one slice migrated. That feels slow to anyone expecting a rewrite. It is not. It is the only version of this that reliably finishes, because at every point the business kept operating and every step could be undone.
How we think about it
At Protocol 42 we take on companies rather than clients, which means we live with these decisions afterward. That changes the calculus. A consultant is rewarded for the size of the rebuild; an owner is rewarded for the smallest change that removes the constraint. When we assess a platform, the first question is never “what would we have built” — it is “what is this costing us, and what is the least we can change to stop it.” That discipline runs through how we structure, build, and operate the companies we back.
If you are staring down a modernization decision and want a second opinion from people who will have to live with the answer, tell us what you are working on.
