Many established companies face an identical dilemma: Their core application, frequently a monolith built a decade ago in PHP, legacy Spring/Java or custom database scripts, handles the entire day-to-day business. At the same time, this system consumes immense engineering bandwidth. Every new feature introduces regressions, engineers proficient in the archaic stack are scarce, and system latencies steadily climb.
The instinctive reaction from leadership is often: "Let's discard it and do a clean greenfield rewrite from scratch."
In practice, up to 70% of these big-bang rewrites fail. Why? Because the business requirements of a long-lived application grew organically over years. Hundreds of edge cases, compliance exceptions and tribal business rules were never formally documented, they exist exclusively in the legacy code.
The battle-tested alternative is an incremental migration using the Strangler Fig Pattern.
What is the Strangler Fig Pattern?
The metaphor comes from botany: A strangler fig germinates in the upper branches of a host tree, sends roots down to the soil, and gradually surrounds the trunk over years. Eventually, the fig becomes the primary load-bearing structure while the host tree decays.
In software architecture, this means: We do not turn off the legacy system. Instead, we position a modern reverse proxy or API gateway (such as Traefik, Envoy or NGINX) directly in front of the legacy application.
- Routing: All user traffic initially continues to hit the legacy monolith.
- Vertical Slice: A single, isolated business capability (such as authentication, billing or analytics) is implemented as a standalone modern service using TypeScript, Next.js, Go or .NET.
- Gradual Redirection: The gateway routes inbound requests for that specific capability to the new service.
- Repetition: Successive modules are strangulated until the old core is depleted and can be safely retired.
4 Steps for Successful Execution
1. Pin Down Business Rules with Characterization Tests
Before rewriting a single line of code, we must capture the true runtime behavior of the legacy software. We write integration and end-to-end characterization tests that feed realistic inputs into the legacy system and record exact outputs. This creates an automated safety net: The new service must output identical results.
2. Establish Robust Data Synchronization
During transitional phases, legacy and new systems often need to share datasets. Proven strategies include:
- Change Data Capture (CDC): Streaming relational database changes via event logs (e.g. Debezium or logical replication) directly to the modern service.
- Dual Writing / Shadow Reads: New API layers write synchronously or asynchronously to both datastores to verify data consistency before cutover.
3. Continuous Value for Stakeholders
The most compelling advantage for Product Owners and CTOs: Instead of waiting two years for a far-off release, the first modern modules enter production within 4 to 8 weeks. Real customer feedback is collected immediately, and return on investment begins at month one.
4. Delete Obsolete Code Ruthlessly
As soon as a module proves stable in the new environment, the corresponding legacy code paths are permanently excised. The monolithic codebase visibly shrinks from sprint to sprint.
Summary
Software modernisation is not a theoretical exercise, it is active risk management and operational continuity. Incremental approaches maintain full control over budgets, code quality, and system uptime.
Planning to modernise an aging core system? Learn more about our approach on our Legacy Modernisation Service Page or request an initial Modernisation Audit.
