Legacy Laravel upgrades

Your Laravel app is four versions behind.
It doesn't need a rewrite.

I upgrade legacy Laravel applications — 5, 6, 7, 8, 9 — to a current release without a maintenance window and without a big-bang rewrite. The app keeps shipping while we migrate it, one deprecated pattern at a time, with the missing tests written on the way.

$60/hr · weekly cap 12+ years Laravel · since v4 UTC−3 · US overlap
Sound familiar?

Why the upgrade keeps getting postponed

Upgrades rarely stall on Laravel itself. They stall on one of these four.

01 · the blocker
"We can't afford the downtime"
→ how it's handled
There is no cutover night. The upgraded app runs beside the old one and routes move across in batches. Traffic never meets a maintenance page, because at no point are both versions down.
02 · the blocker
"We have no tests, so we can't touch it"
→ how it's handled
Tests get written immediately before each area is touched — characterisation tests that pin down what the code does today, so the upgrade can prove it still does it tomorrow. You finish with the suite you never had time to write.
03 · the blocker
"Half our packages are abandoned"
→ how it's handled
Each dead dependency gets a decision, not a wish: replace, vendor and maintain, or delete because the feature is gone. That audit happens first, because it's usually the real reason the last attempt died.
04 · the blocker
"Nobody left here knows why that code exists"
→ how it's handled
Strange code is usually scar tissue from a real incident. It gets read, tested and kept until something proves otherwise — never deleted because it looked odd. That's the difference between an upgrade and an outage.
The decision

Upgrade in place, strangle, or rewrite?

Three routes exist. Picking the wrong one is more expensive than any of them. Here's how I choose.

~/route/01-upgrade-in-place
Cheapest, when it fits.
You walk the app up one major version at a time in its own repo. Fast and low-drama — but only if you're one or two versions behind, have some test coverage, and your dependencies are all still alive.
✓ 1–2 versions behind · tests exist
~/route/02-strangle
The default for genuinely old apps.
A current-version app boots alongside the legacy one and takes over routes in batches. Both are live the whole time. The legacy app shrinks until it's serving nothing, then becomes a read-only archive. No freeze, no cutover night.
✓ 3+ versions behind · can't freeze features
~/route/03-rewrite
Almost always the wrong answer.
A rewrite throws away every edge case the old code silently handles, and buys a long feature freeze to get back to where you already were. It wins in exactly two cases: the business rules themselves are being replaced, or Laravel is no longer the right tool at all.
✗ rarely — see the two exceptions above
Straight answer

What "AI-assisted" actually means here

It means specific things, and it doesn't mean others. Anyone selling you a fully automated framework upgrade is selling you a diff that nobody understands.

What it genuinely accelerates
  • Finding every occurrence of a deprecated pattern across a large codebase — the sweep that humans do incompletely.
  • Drafting characterisation tests for untested code, so there's a safety net before anything moves.
  • Translating old syntax to current syntax: helpers, facades, config shapes, Blade and frontend build changes.
  • Reading an unfamiliar 200k-line codebase fast enough to make the first week productive instead of archaeological.
What stays human
  • Deciding what gets deleted. Models are confidently wrong about which weird code was load-bearing.
  • Choosing the route — in place, strangle, or rewrite. That's a business call, not a code call.
  • Judging which abandoned dependency to replace and which to absorb and maintain.
  • Reviewing every generated change. A migration that ships unreviewed AI output is how you turn an upgrade into an incident.
FAQ

The questions that come up every time

Yes, because the upgrade never happens as a single cutover. The current app keeps serving traffic while the upgraded one is built alongside it. Routes move across in batches, and the old app stays reachable until nothing points at it any more. There's no maintenance window because there's no moment where both versions are down.
That's the normal starting point, not a disqualifier. Tests get written as part of the migration, immediately before each area is touched — characterisation tests that pin down what the code does today, so the upgrade can prove it still does it tomorrow. You end the project with the test suite you didn't have.
Upgrade, in almost every case. A rewrite only wins when the business rules themselves are being replaced, or when Laravel is no longer the right tool at all. If the app is broadly doing the right thing and is simply on an old version, a rewrite throws away years of encoded edge cases and buys a long feature freeze in exchange. See the three routes above.
For a typical business application it's usually weeks rather than months, and because the work is incremental, value lands throughout instead of all at the end. The honest answer depends on three numbers: how many major versions behind you are, how much test coverage exists, and how many abandoned packages are in your composer.json. The first call is about establishing those three.
It does the wide, mechanical, boring parts extremely well, and the decisions badly. Full breakdown in the section above — the short version is that it sweeps and drafts, and I decide and review.
Yes, and preserving them is part of the work rather than an afterthought. Legacy paths get mapped and permanently redirected, so inbound links and search rankings survive the move — a URL that had years of accumulated links keeps resolving after the app behind it changes.

Send me the version you're on and what's blocking the upgrade. I'll tell you which of the three routes fits — even if the answer is that you don't need me.

Get an upgrade plan