Modernization

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.

The brief, honestly

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.

Problem 01

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.

Problem 02

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.

Problem 03

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.

Problem 04

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.

What's included

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.

How we work

What actually happens, week by week.

01

Map it

We trace dependencies, cron jobs and the undocumented behaviour, then rank slices by risk and value.

02

Find seams

Where the monolith can be cut without a rewrite: a capability, a boundary, a routing layer.

03

One slice

The new path is built beside the old one and takes real traffic before anything gets deleted.

04

Dual-write

Data goes to both shapes, gets backfilled and verified, so no cutover rests on a single night.

05

Decommission

The old route, table or job is removed on purpose, so you never maintain two systems at once.

Tech stack

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.

PHPCodeIgniterjQueryBootstrapMySQLPostgreSQLMongoDBREST APIsWebSocketApacheAWSGoogle CloudDigitalOcean
What you get

Deliverables, outcomes and who this is for.

Deliverables
  • Dependency and risk map
  • Seam plan, ranked by risk
  • One production slice per cycle
  • Dual-write and backfill scripts
  • Decommission list, signed off
Outcomes
  • Feature work never stops
  • No big-bang cutover weekend
  • Old paths actually deleted
  • A codebase new hires can enter
Ideal client

Owners and CTOs of a system that still earns, still scares the team, and cannot be taken offline while it gets fixed.

Proof

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.

Frequently asked

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.

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.