Modernize a legacy codebase in flight, without a freeze.
The legacy system still takes the money and nobody wants to touch it. Modernization moves it forward one seam at a time, so the roadmap keeps shipping while the foundation changes.
Nobody gets to stop shipping and rewrite.
The codebase that scares your team is usually the one paying for it. CodeIgniter, classic PHP, a jQuery layer that grew organs. It works, it earns, and every change costs more than the last one — which is exactly why a big-bang rewrite sounds so tempting.
It is also why so many of them fail. A rewrite freezes the roadmap for as long as it runs, and the old system keeps changing while the new one chases a moving target. So we don't do that. We take one slice at a time and put it into production.
That is the strangler pattern, and it is unglamorous work: find a seam, stand the new path beside the old one, route a fraction of traffic through it, and only then delete what it replaced. Incremental rewrite by design. The old code leaves in pieces.
We can't stop shipping features while you work.
You won't have to. Each strangler slice ships on its own, so feature work continues in the old code right up to the point that path is retired.
Nobody left here understands how it works.
Then the first job is reading, not moving. We map dependencies and write down the behaviour we found, including the parts that look like bugs but are load-bearing.
Our data model is older than our business.
Database migration runs in parallel, not overnight. Dual-write to both shapes, backfill, verify, then read from the new one — the old table dies last.
The last modernization attempt stalled halfway.
Usually because nothing was ever decommissioned, so the team maintained two systems. Every slice we start has a defined ending: the old path gets deleted.
Scope, spelled out.
Strangler-slice delivery
One capability at a time moved behind a seam, shipped to production, and proven in traffic before the next slice starts.
Dependency and risk map
What calls what, which cron jobs matter, where the undocumented behaviour lives, and which parts of the monolith nothing depends on any more.
Monolith decomposition
Business capabilities pulled out of the monolith with a boundary you can see, rather than a folder rename that changes nothing.
Framework migration
CodeIgniter and older PHP moved onto a maintained framework, route by route, with the old routes still answering until they no longer need to.
Database migration
Schema changes delivered with dual-write, backfill and verification, so no cutover depends on a long weekend and a held breath.
Front-end untangling
jQuery grown into a framework of its own, replaced page by page, keeping the markup the rest of the app quietly relies on.
Decommissioning
The step everyone skips. Old routes, dead tables and unused jobs removed on purpose, so you stop paying to keep two systems alive.
What actually happens, week by week.
Map it
We trace dependencies, cron jobs and the undocumented behaviour, then rank slices by risk and value.
Find seams
Where the monolith can be cut without a rewrite: a capability, a boundary, a routing layer.
One slice
The new path is built beside the old one and takes real traffic before anything gets deleted.
Dual-write
Data goes to both shapes, gets backfilled and verified, so no cutover rests on a single night.
Decommission
The old route, table or job is removed on purpose, so you never maintain two systems at once.
The tools we actually use here.
Modernization is defined by where you are starting, not where you land. This is what we most often find on the way in, and what usually sits underneath it.
Deliverables, outcomes and who this is for.
- Dependency and risk map
- Seam plan, ranked by risk
- One production slice per cycle
- Dual-write and backfill scripts
- Decommission list, signed off
- Feature work never stops
- No big-bang cutover weekend
- Old paths actually deleted
- A codebase new hires can enter
Owners and CTOs of a system that still earns, still scares the team, and cannot be taken offline while it gets fixed.
No case study here. The method instead.
Why we don't lead with a rewrite
A rewrite quotes well and delivers badly, because the estimate is made against the system you can see and the bill arrives from the behaviour you cannot. Meanwhile the roadmap stops. Every month the new build spends catching up, the old one moves again. We'd rather ship a smaller thing sooner.
How the old routes stay alive
The seam is a routing decision. Requests keep hitting the old entry point, and named routes are forwarded to the new implementation one at a time. Old and new run side by side, sharing a session and a database, so a slice that misbehaves is reverted by moving one route back. Nothing is a leap.
Dual-write, then read, then delete
Data migration is the part that cannot be rolled back casually, so it goes in stages. Write to the old shape and the new one together. Backfill history. Compare the two until the differences are explained rather than dismissed. Only then move reads across, and only after that drop the table.
Decommissioning is the deliverable
A modernization that never deletes anything has only added a second system to maintain, which is how these projects quietly stall. So each slice carries its own ending: the route removed, the table dropped, the cron job gone, the dead code out of the repo. If we cannot name the ending, the slice waits.
Questions we get on the first call.
Yes, and that constraint shapes the whole plan. Slices are chosen so the code your team is actively changing gets migrated last, or gets migrated with your developers in the room. What we do ask is that nobody restructures a slice while it's mid-move. Everything else keeps shipping.
Not as one number, because anyone who gives you one is guessing and will recover it later as change requests. We price the mapping stage, which is bounded, then price each slice once we can see it. You approve slices one at a time — and stop when the returns stop justifying the spend.
Sometimes, and we'll say so. If the application is small, the domain is genuinely understood, and nobody needs a new feature for the duration, a rewrite can be the honest call. That is a narrow set of conditions. Most systems worth modernizing fail at least one of them.
That is the normal case, not the exception. Undocumented behaviour is discovered the same way anywhere: read the code, read the database, read the error logs, and talk to whoever uses the thing daily, because they know the workarounds. What we find gets written down, and that document is yours.
Two things make that work, and both are boring. Your team owns main and we branch off it like anyone else, so nobody is stuck in a long-lived fork that gets harder to merge each week. And the seam sits behind one routing layer, so your releases never wait on our slices.
More in Full-Stack Team on Demand.
Move the old system forward without stopping.
Thirty minutes on the system you're afraid to touch. Bring the file everyone avoids, and we'll tell you where the first seam goes.