Skip to content
Practical Guides

Legacy system modernization: the hybrid path 80% of companies should choose

Rewriting everything from scratch is the most expensive and risky way to modernize. The strangler fig pattern delivers value at every step and keeps the business running.

Practical GuidesJohnny Carreiro·February 17, 2026·3 min read

There is a predictable moment in the life of every successful system: it gets old. The code that sustained years of growth is now fragile, poorly documented, and expensive to evolve. Someone proposes the heroic solution — "let's rewrite it from scratch, properly this time" — and that is where most modernization projects begin to fail.

Why the big-bang rewrite almost always goes wrong

The total rewrite looks clean on paper: new team, modern stack, no technical debt. In practice, it ignores three uncomfortable truths.

First: the old system contains years of business rules nobody documented. That strange if in the middle of the billing code exists because a large customer demanded something in 2019. The rewrite discovers those rules late — in production, when something breaks.

Second: while the team rewrites, the business stops evolving. For months, no new features, because the resources are on the parallel project. Competitors do not wait.

Third: the cutover is a moment of maximum risk. Swapping the whole system at once means that if something goes wrong, everything goes wrong at the same time. The rollback of a total rewrite is, in practice, going back months in time.

The hybrid path: strangler fig

The strangler fig pattern — named after the fig tree that grows around a host tree until it replaces it — proposes the opposite: modernize incrementally, with the old system still running.

The mechanics are straightforward. You put a routing layer in front of the legacy system. At first, it just forwards everything to the old code. Then you rewrite one piece — a module, a set of endpoints — in the new system, and configure the layer to route only that piece to the new code. The rest stays served by the legacy, untouched.

With each cycle, you strangle one more piece of the old system, validating in production before moving on. Over time, the legacy shrinks until it disappears. At no point did the business stop.

What you gain by doing it this way

Value at every step, not just at the end. The first modernized module already delivers benefit — better performance, new features, fewer bugs — while the rest keeps working. Controlled risk: if a step has a problem, you revert only that piece, not the whole system. And the hidden business rules surface in each module's diagnosis, one at a time, instead of all exploding together at cutover.

That is how we modernized the multi-tenant authentication of a European retailer with around 2,000 units: we migrated to Keycloak in stages, with observability wired and rollback prepared at each one, never taking the operation down.

When a total rewrite is justifiable

There are legitimate exceptions. If the stack is obsolete to the point of having no security support, if the system is small enough to rewrite in weeks, or if the architecture is so coupled that you cannot isolate modules — then the big-bang may be the way. But those cases are a minority, and even then it is worth doing the risk inventory first.

Where to start

Honest modernization starts with a risk inventory, not with a stack choice. What the system does, what it depends on, where the fragile points are, which rules are hidden. Then observability to understand the real production behavior. Only then do you design the strangling order — which module first, which next, with validation criteria at each step.

The right question is not "which new language to use." It is "what is the smallest piece I can safely modernize first." Answer that, repeat, and the system modernizes without the business feeling the jump.