What It Actually Costs to Be Three Laravel Versions Behind
Nobody decides to fall behind. It happens one skipped release at a time, and the bill arrives as a security patch you cannot apply and a quote four times what it should have been.
An application three major versions behind did not get there through a decision. There was a quarter where an upgrade did not fit, then a release where a package had not caught up yet, then a year in which nothing broke - and the thing about a framework going out of support is that nothing breaks on the day it happens.
The cost is real anyway. It is just deferred, and it compounds.
The bill, itemised
Security fixes stop arriving. This is the part with a date on it. Each Laravel release gets about two years of security fixes; miss that window and a vulnerability disclosed in the framework has no patch you can apply. The fix exists - it is in a version your application cannot install.
Your dependencies leave without you. Packages track the framework. A year behind, you are pinning a version or two. Three years behind, the package you need has dropped support for your constraint entirely, and installing anything new means resolving a graph that has no solution. Composer tells you this in the least helpful way available.
PHP goes out of support underneath it. Laravel versions carry PHP requirements, and older PHP stops receiving security fixes on its own schedule. Now the language, the framework and the packages all need moving, and they have to move in an order.
Hiring gets harder and onboarding gets slower. A developer who has worked in Laravel for three years has never seen your version's conventions. Every piece of documentation they find describes something else. The framework's own strongest argument - that any Laravel developer can read any Laravel codebase - stops applying to yours.
The work itself slows down. Not because the old version is slow, but because everything is a workaround. Features that ship as one line in current Laravel are a package, or a trait, or four hundred lines somebody wrote in 2019 and then left.
Why it gets worse rather than staying the same
An upgrade from one version to the next is mostly mechanical. The breaking changes are documented, there are usually few of them, and the release notes tell you what to look at.
Three versions is not three times that work, for two reasons.
The changes interact. A deprecation introduced in one release and removed two later is invisible in the individual upgrade guides and unavoidable when you do them together. And you cannot step through cleanly, because the packages that would have carried you through the intermediate versions no longer have versions that satisfy both ends.
So the work stops being apply the guide and becomes understand what this application actually does - which is expensive precisely because nobody currently does.
What it looks like done properly
Read before writing. What the application does, which parts have tests, which dependencies are abandoned, what PHP it needs. This produces a document and a sequence, and it is the part people want to skip.
Get a safety net first. An upgrade without tests is a rewrite with extra steps. Feature tests over the routes that carry money and the jobs that touch other systems are usually enough - not full coverage, which costs more than the upgrade.
Language first where it applies. If PHP has to move, it moves before the framework, because the framework cannot.
One version at a time, merged each time. Upgrade, fix, deploy, repeat. Each step is small enough to reason about and to roll back. A single branch attempting all three ends as a merge conflict with a deadline attached.
Delete on the way through. Three versions of accumulated workarounds include some that exist only because the framework could not do it then. It can now. Those deletions are the part of the work that pays for itself twice.
The number worth knowing
The honest version of the arithmetic: an upgrade done every year is a small predictable cost. Left for three, the same work costs several times as much - not because the code changed more, but because the knowledge of what the application does has to be rebuilt before anything can be moved safely.
And the compounding does not stop while you decide. Another year adds another version, another set of abandoned dependencies, and another group of people who have left the company since anyone understood the payment module.
If you are reading this because you already know you are behind, the useful first step is not a quote. It is finding out precisely how far, in what order things have to move, and which parts of the application nobody can currently explain - because that last list is what the price is actually made of.
Reading the application is an audit. Moving it is upgrades and rescue. Once it is current, staying current is a small monthly job, which is the whole case for a maintenance arrangement.
If the application is not on Laravel at all, the same interest is accruing on whatever it is on. We move those across incrementally. We do not rewrite them.
