Modernising a Legacy Web App Without Stopping the Business
In short
Old web apps can't simply be switched off. How to modernise one step by step — using the strangler fig approach — while the business keeps running on it.
Legacy web apps are usually the ones the business depends on most: the billing tool, the booking system, the member portal. They're slow, hard to change and often insecure — but they can't be switched off while a replacement is built. The good news is that they don't have to be. Martin Fowler's 'strangler fig' approach replaces an old system piece by piece while it keeps running. Here's how I apply it.
Start with an honest audit
Before planning anything, understand what the app actually does — including features nobody documented. Talk to the people who use it daily, list every screen and report, and note every integration with other systems.
- Which features are used daily, rarely, or never
- Where the data lives and how clean it is
- Integrations: payments, SMS, email, accounting, external APIs
- Known bugs, performance problems and security risks
- Which parts cause the most pain for users and staff
Why a 'big bang' rewrite is risky
Rewriting everything and switching over on one day sounds clean, but it concentrates all the risk at the end. The old system keeps changing while the new one is built, features are forgotten, and launch day becomes a gamble. Many big-bang rewrites run late or are abandoned.
The strangler fig approach
The strangler fig pattern — named after a plant that grows around a tree until it replaces it — builds the new system around the old one. Requests are routed so that modernised features are served by the new code while everything else still goes to the legacy app. Over time, more routes move across, until the old system can be retired.
Each step is small enough to test and roll back. Users see steady improvement instead of one risky switch, and the business keeps running throughout.
Choose the first piece carefully
Start with something valuable but self-contained: a slow report, a public-facing page, or a form that causes most complaints. An early visible win builds confidence, and it tests the routing and data-sharing approach before harder parts are tackled.
Handle data with care
Data is the hardest part of any modernisation. Often the new and old parts share the same database for a while; sometimes data is synchronised between them. Either way, migration scripts should be repeatable, and totals should be reconciled against the old system before each cut-over.
Fix security early
Legacy apps often store passwords in outdated ways, build SQL queries from raw input, or check permissions only in the interface. These are serious risks, and some can be fixed in the old app before full modernisation — for example, moving to properly hashed passwords and parameterised queries as described in the OWASP cheat sheets.
A phased plan, step by step
A typical modernisation, broken into phases that each deliver something useful:
- Phase 0 — Audit: features, data, integrations, risks; agree priorities
- Phase 1 — Foundations: new app skeleton, authentication, routing in front of the legacy app, deployment pipeline
- Phase 2 — First slice: move one valuable, self-contained feature and ship it
- Phase 3 — Data: improve the data model gradually, with reconciled migrations
- Phase 4 — Remaining features, one by one, highest value first
- Phase 5 — Retire the legacy app: read-only period, archive, switch off
Example: modernising an old booking system
Imagine a booking system built years ago in PHP. Staff complain about the slow daily report; customers struggle with the booking form on phones; passwords are stored with an outdated hash.
A sensible order: first, fix password storage in the old app, because it's a live risk. Second, put a new front door in place that routes requests. Third, rebuild the customer booking form in the new stack — the most visible improvement — while it still writes to the existing database. Fourth, rebuild the daily report on the new side. Then continue with the admin screens until nothing is left in the old code.
Testing that protects the business
Before moving any feature, capture how the old version behaves: what it shows for real inputs, what totals it produces. Automated tests that compare the new feature's results with the old one's catch differences before users do. For reports and billing, reconcile totals between old and new systems for a period before switching fully.
Keeping users on board
Users who've relied on an old system for years have habits — keyboard shortcuts, report layouts, field orders. Involve a few of them early, keep familiar terms and layouts where they make sense, and explain what's changing and why before each release. Adoption is part of modernisation, not an afterthought.
Signs the modernisation is working
- Each release ships on schedule and can be rolled back
- Staff report fewer workarounds and complaints
- Page load and report times drop measurably
- Security issues from the audit are closed
- The share of traffic served by the legacy app keeps shrinking
Choosing the target architecture
The new system should be boring in the best sense: a mainstream web framework, a relational database, configuration in environment variables, automated tests and deployments. Avoid rebuilding a monolith as dozens of microservices unless you genuinely have the scale and team to operate them — for most business applications, a well-structured single application is easier to run and change. The Twelve-Factor principles are a good checklist for keeping it portable between hosts.
Routing between old and new
The practical heart of the strangler approach is a layer in front of both systems that decides, request by request, which one handles it. It can be a reverse proxy, the new application itself forwarding unmigrated routes, or rewrites at the hosting level. Users see one address; behind it, the share of routes served by the new system grows release by release. Login sessions need special care so users aren't asked to sign in twice when they move between old and new pages.
Estimating cost and time
Because modernisation is phased, estimate it phase by phase rather than as one number. The audit and foundations phases can be estimated fairly precisely; later phases are estimated as they approach, with better knowledge of the old system. This also lets the business pause, reprioritise or stop at any point with working software in place — something a big-bang rewrite can't offer.
When not to modernise
Sometimes the right answer is to replace the app with an off-the-shelf product — when its features are generic, like standard invoicing or basic HR. Sometimes it's to leave it alone — when it's stable, secure enough, rarely changed and near the end of its useful life. Modernisation earns its cost when the application supports processes that are specific to your business and that you expect to keep evolving.
Documentation as you go
Legacy systems are usually poorly documented — that's part of why they're hard to change. Don't repeat the problem. As each feature moves, document what it does, the business rules it encodes, where its data lives and how it's tested. Short, current documentation in the repository is worth more than a long document written once and never updated. It also means the organisation is never again dependent on one person's memory of how the system works.
Performance and scale in the new system
Legacy apps often got slow because of unindexed database queries, reports calculated on every page load, or everything running in one request. The new system can fix these systematically: add indexes for the queries users actually run, move heavy reports and exports to background jobs, cache results that don't change often, and paginate large lists. Measure before and after — users notice a report that drops from a minute to a few seconds more than any design change.
Keeping integrations working
Integrations — payment gateways, SMS providers, accounting software, partner APIs — are where modernisations most often stumble, because they depend on systems you don't control. List each one during the audit with its credentials, callbacks and failure behaviour. Move them deliberately, test against the provider's sandbox where available, and keep the old integration running until the new one is proven with real traffic.
Communicating progress to stakeholders
Modernisation projects lose support when stakeholders can't see progress. A simple, regular update — which features have moved, what improved for users, what's next, and any risks — keeps owners, managers and users aligned. Show before-and-after comparisons where possible: a report that took a minute and now takes seconds, a form that now works on phones, a security issue that's closed.
Budgeting for the long run
Modernisation isn't only a one-time cost. Plan for the new system's ongoing maintenance — dependency updates, hosting, monitoring and small improvements — from the start. The goal is a system that stays modern, so the organisation doesn't face another painful rewrite in ten years.
A modest, steady maintenance budget is far cheaper than letting software decay until it has to be replaced again.
Retire the old system deliberately
When the last feature has moved, keep the old system available read-only for a period, archive its data, and then switch it off. Document what was retired and where each function now lives.
Frequently Asked Questions
Can we keep using the old app while it is modernised?
Yes. With the strangler fig approach, the old app keeps running and features move to the new system one at a time.
Do we have to change the database?
Not immediately. The new code can often use the existing database at first, with the data model improved gradually.
How long does modernising a legacy app take?
It depends on size and complexity. The advantage of a phased approach is that value arrives early, with improvements shipped every few weeks.
What stack should the modernised app use?
Something mainstream and well supported, so other developers can maintain it. For web apps I typically use Next.js, TypeScript and PostgreSQL.
Is it cheaper to buy off-the-shelf software instead?
Sometimes. If an existing product covers your workflow well, it can be cheaper. Custom modernisation makes sense when the app encodes processes that give your business its edge.
What should be modernised first?
Critical security issues first, then a valuable, self-contained feature that users will notice — it proves the approach and builds confidence.
Should we rebuild as microservices?
Usually not. For most business applications a well-structured single application is simpler to run and change. Microservices suit large scale and large teams.
Key Takeaways
- Audit real usage, data and integrations before planning.
- Avoid big-bang rewrites; replace the system piece by piece.
- Start with a valuable, self-contained feature.
- Make data migration repeatable and reconcile totals.
- Fix critical security issues early, even in the old app.
Sources & further reading
- Strangler Fig application — Martin Fowler
- Patterns of legacy displacement — martinfowler.com
- Password storage cheat sheet — OWASP
- SQL injection prevention cheat sheet — OWASP
- The Twelve-Factor App — 12factor.net
Related work
Keep reading
Have a project that touches website redesign? See Website rebuild & redesign.
Get in touch