All articles

Legacy Software Modernisation: When to Rebuild vs Refactor [Decision Guide]

How to decide whether to modernise, refactor, or completely rebuild your legacy software — with a structured decision framework, cost comparisons, and the common mistakes that turn modernisation projects into expensive failures.

11 November 2025
Cyberbeak Team
Legacy Software Modernisation: When to Rebuild vs Refactor [Decision Guide]

There is a common misconception that "legacy" is a synonym for "old." It is not. Legacy software is not defined by its age — it is defined by the cost of change. A system built five years ago using the wrong patterns, with no test coverage and no documentation, is as much a legacy system as a mainframe application written in COBOL in 1987. What they share is the same property: changing them is expensive, slow, and risky in ways that are disproportionate to the size of the change.

The inverse is also true. Systems written decades ago, maintained with discipline and care, kept well-tested and documented, are not really legacy systems at all. They are mature systems — and there is a meaningful difference.

We work with businesses that are sitting on software at all points of that spectrum. Some are genuinely ancient systems with thousands of undocumented business rules buried in stored procedures. Some are relatively young codebases that decayed fast because they were built under pressure without the right foundations. What almost all of them share is a moment of recognition: the system is now costing more to maintain than it is worth to keep in its current form, and something needs to change.

What comes next — how you decide to modernise, what approach you take, how you manage the risk — is one of the most consequential technical decisions a business can make. Done well, modernisation can unlock years of competitive advantage. Done poorly, it can cost several times the original estimate, deliver nothing for 18 months, and sometimes destroy the underlying business.

This guide is our honest attempt to give you the framework to get it right.


What Makes Software "Legacy"?

Age is a factor, but it is rarely the defining one. The real markers of legacy software are structural and operational, and they accumulate regardless of how long the system has been running.

Unmaintainable code is the most obvious signal. Code with no separation of concerns — where business logic is tangled into database queries, where functions are thousands of lines long, where changing one thing requires understanding the whole system — does not have to be old to be unmaintainable. Poor engineering decisions made quickly under pressure produce legacy code in months.

No test coverage means every change is a leap of faith. Without automated tests, developers cannot confidently touch the codebase without the risk of silent regressions. This creates a self-reinforcing trap: the code is too risky to change, so no one writes tests for it, so it becomes even riskier to change. Teams respond by building around the problem — bolting new features onto the edges rather than modifying the core — which makes the architecture progressively worse.

Unsupported technology creates a different kind of risk. When a runtime, a framework, or an operating system reaches end of life, security patches stop coming. A system running on an unsupported version of Java, a deprecated version of Node, or an end-of-life Linux distribution is not just technically outdated — it is a liability that keeps growing. Every passing month without a patch widens the attack surface.

Inability to hire is a commercial dimension of legacy that business leaders often underestimate. There are very few developers who know VB6, Delphi, Classic ASP, or ColdFusion — and the ones who do are typically senior engineers who could be doing more valuable work. When your stack is so niche or so dated that you cannot hire for it, you become dependent on a shrinking pool of people. That dependency is expensive and fragile.

Hardware dependency is another signal that is easy to overlook until it becomes critical. Systems that run only on specific physical hardware — or that depend on operating systems tied to hardware generations that are no longer manufactured — create a single point of failure that cannot be resolved without modernisation. We have spoken to businesses that are one server failure away from complete operational paralysis because the system will only run on hardware that has not been made for fifteen years.

Security vulnerabilities that cannot be patched are perhaps the most urgent category. When a CVE is published against a component your system depends on and no patch exists — because the vendor no longer supports it — you have no good options without modernisation. You can isolate the system, restrict access, and add monitoring, but none of those mitigations remove the underlying exposure.


The Business Triggers for Modernisation

Businesses rarely decide to modernise legacy systems for purely technical reasons. The decision is usually forced by one or more of the following business realities.

Feature velocity has collapsed. When it used to take a week to ship a new feature and it now takes three months — and most of that time is spent understanding the existing system, managing regressions, and manually testing things that should be automated — the system is costing you competitive ground. Every quarter of delayed shipping is a quarter your competitors have the field to themselves.

Security risk has become unacceptable. Regulators, insurers, enterprise customers, and boards are all paying closer attention to software security posture than they were five years ago. Cyber insurance underwriters are now asking detailed questions about patch cadence, dependency hygiene, and end-of-life runtime exposure. Legacy systems frequently fail those assessments — and the consequences range from premium increases to loss of cover to breach liability.

Compliance requirements cannot be met. GDPR, PCI DSS, ISO 27001, SOC 2 — many compliance frameworks require capabilities that legacy systems simply cannot provide. Data residency, audit logging, encryption at rest, granular access controls: if the original system was never designed for these requirements, retrofitting them into an unmaintainable codebase is often more expensive than rebuilding.

The developer talent crisis has arrived. When you can only maintain your system using a team of two people who have worked on it for a decade, you have a key-person risk problem wrapped inside a technology problem. When those two people leave — and eventually they will — the institutional knowledge leaves with them. Before that happens, businesses often reach a point where they simply cannot hire replacements for the skills the system requires.

Competitive pressure is making the cost visible. Competitors who modernised two years ago are shipping features in days that take you months. They are integrating with third-party platforms through modern APIs while you are building custom point-to-point integrations. The compound effect of that gap is often what finally makes the business case for modernisation undeniable.


The Modernisation Options

Not every legacy system problem requires a complete rebuild. The range of approaches runs from very low disruption to very high disruption, and the right choice depends on your specific situation.

Encapsulate and Wrap

This approach leaves the legacy system completely untouched internally. Instead, you build an API layer — essentially a facade — that sits in front of the legacy system and exposes its functionality through a modern interface. External consumers interact with the API layer; the legacy system handles the underlying processing exactly as it always has.

Best for: Systems where the internals are too risky or too expensive to touch, but where the primary problem is that nothing modern can integrate with them. Also useful as a first step in a longer modernisation journey, giving you time to build out a replacement while maintaining continuity.

Limitations: You are not fixing the underlying problems — you are hiding them behind a cleaner interface. The technical debt, the security risk, and the maintenance cost of the legacy system remain. This is a stabilisation strategy, not a modernisation strategy on its own.

Refactor Incrementally

The boy scout rule — leave every module you touch better than you found it — applied at scale. Rather than setting aside time specifically for modernisation, the team makes structural improvements as part of regular feature development. Tests get added. Functions get extracted. Modules get separated. Dependencies get updated.

Best for: Systems where the codebase is maintainable enough to work in, the core logic is sound, and the problems are primarily about code quality rather than architectural fundamentals. Works well when you have a stable team with long-term ownership of the codebase.

Limitations: Slow. The improvements are real but they accumulate over years, not months. If the system has deep architectural problems — the wrong data model, synchronous processing where it needs to be async, a monolithic structure that cannot scale — incremental refactoring will not reach those problems without a more deliberate architectural intervention.

Strangler Fig

Named after the strangler fig tree, which grows around an existing tree and gradually replaces it, this pattern involves building a new system alongside the old one and migrating functionality incrementally. Individual features, modules, or user journeys are rebuilt in the new system and traffic is redirected to it piece by piece, until the legacy system handles nothing and can be decommissioned.

Best for: Complex, mission-critical systems where a big-bang replacement is too risky. It gives you a real, running production system to validate against at every stage. If something goes wrong with the new system, traffic can be redirected back to the legacy system. You are never in a state where nothing works.

Limitations: You are running two systems for an extended period, which has both infrastructure cost and cognitive overhead. It requires a disciplined approach to routing and careful thinking about which components to migrate in which order. Data consistency between the two systems during the transition period is a significant challenge.

Re-platform

Keep the existing business logic but move it to a new technology stack. A system running on Windows Server 2008 gets migrated to a modern cloud environment. An application built on an end-of-life framework gets rebuilt on its modern equivalent. The data model, the domain logic, and the core workflows remain the same — what changes is the infrastructure and the runtime.

Best for: Systems where the underlying logic is sound and well-understood, but the technology beneath it has become untenable. Often the right choice when the primary driver is an end-of-life runtime, a hardware dependency, or an infrastructure that can no longer be operated economically.

Limitations: You are still translating the existing system, which means you will encounter all of the hidden complexity in the existing logic. "Re-platform" projects regularly expand in scope when teams discover undocumented business rules, data quality problems, and edge cases that were never written down anywhere.

Complete Rewrite

Build an entirely new system from scratch, based on a clean understanding of what the business actually needs. The legacy system is used as a reference but not as a template — you are designing the new system properly rather than recreating the old one.

Best for: Systems where the existing logic is fundamentally wrong, not just badly expressed — where the data model does not fit the domain, where the architecture makes future requirements structurally impossible, or where the codebase is so degraded that any other approach would cost more than a fresh start.

Limitations: Highest risk. Longest timeline. Most likely to exceed budget. We discuss this in detail in the next section.


The Rebuild vs Refactor Decision Framework

The following table is a starting point for structuring the decision. No single criterion is decisive on its own — the answer emerges from looking at them together.

CriterionPoints Toward Refactor / Strangler FigPoints Toward Rebuild
Scale of technical debtLocalised to specific modulesSystemic; affects the entire codebase
Domain logic correctnessLogic is sound, just poorly expressedLogic is incorrect or no longer fits the business
Data modelBroadly correct, schema is workableFundamentally wrong; cannot evolve without migration
Test coverageSome coverage exists as a safety netNo tests; changes are pure guesswork
Team knowledgeTeam understands the existing systemKnowledge has left; system is a black box
Risk toleranceCannot afford a period of instabilityCan absorb transition risk for long-term gain
Timeline12–18 months acceptable; incremental delivery preferred18–36 months acceptable if the outcome is clean
BudgetLimited; must preserve ongoing operationsSufficient for parallel running and full rebuild
Compliance / security urgencyCan be addressed incrementallyImmediate; current system cannot be made compliant
Technology stackMaintainable with effortNo-one can be hired; vendor has EOL'd the runtime

A rule of thumb we use internally: if more than half of these criteria point toward rebuild, and the budget and risk tolerance support it, the rebuild case is strong. If the split is closer, the strangler fig pattern — which gives you the clean end state of a rebuild with the incremental risk profile of a refactor — is often the right answer.


Why Complete Rewrites Almost Always Take Longer Than Expected

If you ask most engineering teams how long a complete rewrite will take, the answer will be wrong. Often materially wrong. This is not a planning failure unique to bad teams — it is a structural property of complete rewrites, and understanding why helps you plan around it.

The second system effect, described by Fred Brooks in The Mythical Man-Month, is the tendency for the second version of a system to be over-engineered by a team that now knows what they wish the first system had been. Engineers designing a replacement want to do it properly this time — which is a reasonable instinct — and the scope expands to include every feature that was compromised the first time around. The second system becomes more ambitious than the first, and the timeline stretches accordingly.

Hidden business logic is the more immediate problem. Legacy systems, especially those that have been running for ten years or more, encode an enormous amount of institutional knowledge in their behaviour. Not in documentation. Not in specifications. In the code itself — and sometimes not even obviously in the code, but in the interactions between components, in the order operations happen, in the edge cases that were handled years ago by a developer who has long since left and never told anyone what they were doing or why. Reverse-engineering that logic from a running production system is far harder and more time-consuming than anyone expects.

Data migration complexity (we cover this in the next section) consistently adds months to timelines that were scoped without it.

Integration discovery adds more. Every legacy system has more integrations than anyone remembers — third-party services, internal tools, manual processes that feed data into the system, export formats that downstream systems depend on. Each one of these needs to be discovered, mapped, and reproduced in the new system.

Our experience is that complete rewrites take roughly 1.5x to 2x the initial estimate. That is not pessimism — it is what the data shows. Build that assumption into your business case before you commit.


Data Migration: The Often-Underestimated Piece

Data migration is the part of modernisation projects that gets the least attention in the planning phase and causes the most problems in the delivery phase. We have seen data migration work cost as much as the new system itself.

Dirty data is the first problem. Production data accumulated over years almost always contains quality issues that were never visible because the legacy system worked around them — or because no one ever looked. Duplicate records. Nulls in fields that should never be null. Date fields with values like "00/00/0000" that a developer entered once as a placeholder and a process somewhere depends on. Foreign key references to records that no longer exist. Normalising and cleaning this data before it can be migrated to a new schema is a significant piece of work, and the full scope of it is almost impossible to estimate until you actually look at the data.

Schema translation is rarely one-to-one. If the new system has been designed properly, its data model will be different from the old system's data model — sometimes radically so. Translating data from the old schema to the new one requires careful mapping, transformation logic, and validation at every step. The more the schemas diverge, the more the translation work grows.

The parallel running period adds another layer of complexity. When both systems are running at the same time, data written to the old system needs to be reflected in the new one, and vice versa. Keeping two databases in sync during a transition period requires either a robust synchronisation mechanism or very careful sequencing of the cutover — and both approaches take engineering time that was not typically in the original plan.

Zero-downtime migration is often a business requirement but is rarely trivial to implement. Strategies like the expand-contract pattern, blue-green deployments, and event sourcing during the transition period can achieve it, but each adds complexity. If zero downtime is a hard requirement, scope the data migration work specifically for it — do not assume it can be achieved as an afterthought.


Running Old and New Systems in Parallel

The strangler fig pattern is our most-recommended approach for complex legacy modernisation precisely because it avoids the binary risk of a big-bang replacement. But running two systems simultaneously has its own challenges.

Feature flags are your most important tool during a parallel running period. Rather than routing all users to the new system at once, feature flags let you expose the new system to a controlled subset of traffic — internal users first, then a small percentage of real users, then progressively more as confidence increases. If a problem surfaces, you turn the flag off and route everything back to the legacy system. This granularity of control is what makes the strangler fig approach manageable.

Traffic migration should be gradual and monitored. Moving from 0% to 100% of traffic in one step is exactly what you are trying to avoid. A sensible sequence might be: internal team only, then 1% of users, then 5%, then 25%, then 50%, then 100% — with a monitoring period at each step. Define the metrics you are watching (error rate, latency, business outcomes like successful transactions) and the thresholds that will trigger a rollback before you start the migration, not after.

Rollback planning is not optional. Every step in a traffic migration should have a documented, tested rollback plan. "Tested" means you have actually performed the rollback in a staging environment, not that you believe it should work in theory. The rollback plan also needs to account for data written to the new system during the period it was live — if you roll back, what happens to that data?

Decommissioning the legacy system is often treated as the end goal but is actually the hardest step to complete. There is almost always a long tail of functionality — rarely used features, scheduled processes, reporting integrations, admin tools — that is only discovered once the main migration is done. Plan explicitly for a decommissioning phase rather than assuming the legacy system will simply be turned off when the new one is live.


How to Cost a Modernisation Project

Modernisation project costs vary enormously because the factors that drive them vary enormously. A focused refactor of a well-understood module with clean data and no integrations is a very different piece of work from a full enterprise re-platform with fifty years of accumulated data, forty-three integrations, and a compliance requirement that touches every screen.

The primary cost drivers are:

Codebase size and complexity. Lines of code is a crude proxy, but it is a starting point. More important than raw size is the density of the complexity — how much undocumented business logic, how many special cases, how deeply coupled the components are.

Data complexity. Number of tables, volume of records, data quality issues, and the divergence between the old and new schema all contribute. Projects with large, dirty data sets in complex schemas can have data migration costs that match or exceed the application rebuild cost.

Integration count. Every integration with a third-party system, an internal tool, or an upstream data feed is a piece of work that needs to be scoped, built, and tested. Projects with twenty integrations are not twice as complex as projects with ten — the coordination and testing overhead scales faster than linearly.

Compliance requirements. If the new system needs to meet PCI DSS, ISO 27001, or SOC 2, the compliance-specific work — audit logging, access controls, data residency configuration, penetration testing, documentation — is a substantial line item in its own right.

Typical cost ranges we see in practice:

ScopeTypical RangeNotes
Focused refactor (single module or service)£15,000 – £50,000Well-defined scope, existing tests, no major data migration
Re-platforming a small system£40,000 – £100,000Modest integration count, relatively clean data
Full application rebuild (SME scale)£80,000 – £250,000Multiple integrations, data migration included
Enterprise re-platform or full rebuild£250,000 – £1,000,000+Complex data, compliance requirements, large integration landscape

These figures assume a UK-based or equivalent-cost team. Offshore delivery can reduce the labour component, but adds coordination overhead and — for systems with complex legacy logic — discovery risk that partially offsets the saving.


Common Modernisation Mistakes We See

These are the patterns that turn well-intentioned modernisation projects into expensive failures. None of them are exotic — they recur regularly enough that we track them explicitly.

Trying to do too much at once. The excitement of starting a modernisation project often leads to scope creep before the project even begins. Teams want to not just replace the legacy system but also add the feature backlog that the legacy system could never support. Every addition to the scope increases timeline, budget, and risk. The goal of the modernisation project should be a clean, modern equivalent of what the current system does — the feature additions come after, when the new system is stable.

Not involving the people who understand the legacy system. The developers who built and maintained the legacy system are not obstacles to modernisation — they are essential sources of knowledge that exists nowhere else. Business rules that were implemented fifteen years ago for reasons that are still valid, edge cases that were discovered through painful production incidents, integrations that were built to work around limitations in third-party systems: none of this is documented, and none of it will be discovered by reading the code alone. Those people need to be in the room.

Skipping the discovery phase. A proper legacy modernisation begins with a systematic audit of what the current system actually does — not what the documentation says it does, and not what anyone thinks it does. The audit covers codebase structure, data model, integration map, business rules inventory, and performance characteristics. Teams that skip this phase and go straight to building consistently discover scope they did not account for, usually at the worst possible moment.

Underestimating data migration. We mentioned this above, but it bears repeating as a mistake because it continues to happen. "We will sort the data migration at the end" is a sentence that has derailed more modernisation projects than almost any other. Data migration needs to be scoped, resourced, and planned from the start — not treated as a cleanup task at the end of the build phase.

No rollback plan. If the new system goes live and something is wrong — a business rule is missing, a data quality issue surfaces, an integration behaves differently than expected — you need a path back. Teams that go live without a tested rollback plan are betting the business on the go-live being clean. It rarely is, and without a rollback option, they have to fix the problem under production conditions with users affected.


How We Approach Legacy Modernisation at Cyberbeak

Our starting point for every legacy modernisation engagement is a codebase and infrastructure audit. Before we write a single line of new code or recommend a specific approach, we need to understand what we are actually working with. That means reading the code, mapping the data model, documenting every integration, running the system in a controlled environment and observing its behaviour, and interviewing the people who understand it.

The output of the audit is a modernisation report that covers: the current system's architecture, an honest assessment of the technical debt, a data quality analysis, an integration map, a risk register, and a recommendation for the modernisation approach — with the rationale for why that approach fits the specific situation.

From there we work in defined phases, typically structured around:

  • Phase 1 — Foundations: Development environment, test harness, CI/CD pipeline, staging environment. Nothing production-facing, but everything you need to work safely.
  • Phase 2 — Core domain: The central business logic, data model, and primary user workflows. This is where most of the complex decisions live and where we validate the approach.
  • Phase 3 — Integrations and data migration: Rebuilding the integration landscape and running the data migration against production data in staging to validate the process before go-live.
  • Phase 4 — Parallel running and cutover: Controlled traffic migration using feature flags and monitoring, with a tested rollback plan in place at every step.
  • Phase 5 — Stabilisation and decommission: Post-cutover monitoring period, addressing the long tail of edge cases, and systematic decommissioning of the legacy system once confidence is established.

We do not offer fixed-price estimates before the discovery phase is complete. Any agency that gives you a fixed-price quote for a legacy modernisation project without first auditing the system is guessing — and those guesses almost always land on the optimistic side.


Frequently Asked Questions

How do we know it is time to modernise?

If two or more of the following are true, the conversation is worth having: you are spending more than 40% of your development budget on maintenance rather than new features; you have had a security incident or compliance failure directly attributable to the legacy system; your development velocity has declined measurably over the past 12–24 months; you cannot hire developers for the technology stack; a key person who understands the system is at risk of leaving.

Can we modernise without stopping feature development?

In most cases, yes — but it requires discipline. The strangler fig approach is specifically designed to allow ongoing feature development on the legacy system while the new system is being built alongside it. The key constraint is that new features built on the legacy system during the modernisation period may need to be rebuilt in the new system, so you should minimise discretionary investment in the legacy system during the transition. Features that are required for compliance or commercial reasons are the exception.

How long does legacy modernisation typically take?

A focused refactor of a single module can take four to eight weeks. A full application rebuild for an SME-scale system typically takes nine to eighteen months from audit to decommissioning. Enterprise-scale re-platform projects regularly run to two or three years. The most important variable is scope — and scope is what the discovery phase is designed to define accurately.

What if the people who understand the system are leaving?

This is one of the strongest arguments for starting modernisation immediately rather than waiting. Knowledge capture — structured interviews, documentation sessions, code walkthroughs — should begin before those people leave, regardless of when the modernisation project formally starts. If key knowledge holders are departing in the near term, factor in a knowledge transfer phase at the very start of the project and treat it as a risk item in the plan.

Is it worth the cost?

For businesses where the legacy system is genuinely constraining growth, creating security or compliance risk, or consuming disproportionate maintenance budget, the answer is almost always yes — provided the modernisation is approached correctly. The return comes from reduced maintenance cost, faster feature velocity, lower risk exposure, and the ability to hire and retain modern development talent. The businesses we see regret modernisation are almost always the ones who underscoped it, skipped the discovery phase, or tried to shortcut the data migration. The businesses that do it properly rarely look back.


If you are facing a legacy system decision and want an honest assessment of your options, we are happy to talk. Our legacy modernisation audits are scoped as standalone engagements — you get a clear picture of what you are dealing with and a recommended path forward before you commit to anything larger. Get in touch and we will set up an initial conversation.

Ready to build?

Talk to our team about your project

We work with businesses across the UK, USA, UAE, KSA, Canada, Australia and Germany to build custom software, SaaS platforms and marketplace systems.