Founder of Revin. Engineer by training, specialist in software development and digital products.

Old code is usually the only living documentation of the rules your revenue depends on
You can take a PHP 5.6 system to PHP 8, and in most cases that reach my desk it costs far less than a rewrite. What changes the math is the second half of the question. Moving to PHP 8 and dropping Laravel on top of what already exists are two different jobs with two different risk profiles, and the second one is the one that eats a quarter.
Before you pick a side, measure three things, in this order: how many files PHP 8 actually breaks, how many of your direct dependencies nobody maintains anymore, and how much of your revenue runs through code you cannot execute end to end without a real customer on the other side. That is about two days of work for a developer who already knows the codebase. Cheap next to the price of getting this call wrong.

Half an hour of PHPCompatibility turns "feels like a lot" into a per-file count
A client once asked me whether going from MySQL 5 to MySQL 7 was the same thing as moving to Postgres. He was not technical, so I answered with the fruit bowl. MySQL 5 is a Gala, MySQL 7 is a Granny Smith, Postgres is an orange. All three are fruit, all three are SQL databases, and nobody swaps an apple for an orange halfway through a recipe expecting the same cake.
PHP 5.6 to PHP 8 is swapping one apple for another. Same language, almost all the syntax your team wrote still parses, and the work sits in specific incompatibilities and in behavior that quietly changed. Putting Laravel inside a procedural system is the orange. The framework decides where the request enters, how routing resolves, how the database gets touched, and where business rules live. That is not an upgrade, it is replacing the foundation while the building stays occupied.
Both are doable. But whoever promises both on the same timeline usually delivers the first one and spends the rest of the year on the second.
Stop arguing from vibes. Install PHPCompatibility on top of PHP_CodeSniffer, point it at your target version (8.2 or 8.3), and run it across the repo. Half an hour later you move from "feels like a lot" to a per-file count.
The result usually lands on the good side. In a mid-sized system the report flags somewhere in the low hundreds of occurrences, most of them clustered in a handful of files. Nearly all of it is mechanical: `each()` gone, `create_function` removed, `mysql_` calls that already died in 7, arguments passed by reference where that is no longer allowed. Rector clears a big slice of it on its own. What is left is a boring afternoon, not a project.
The danger lives in what does not break. Loose comparison between strings and numbers changed in PHP 8. On 5.6, `0 == "abc"` returned true. On 8, it returns false. Nothing crashes, no log lights up, the `if` just takes the other branch. If that branch decides a discount, a shipping tier, or an access check, your business rule changed and nobody signed off on it.
So the order matters. First write characterization tests over the flows that make money, freezing today's behavior even where it is odd. Then bump the version. Teams that do the reverse learn about the change from a support ticket three weeks later, on a wrong invoice total.

On the middle path the router hands over one route at a time and the system runs split for a while
Second measurement, half a day: `composer outdated --direct` and `composer audit`. Add the native extensions your server loads, because mcrypt left core back in 7.2 and homegrown crypto is still glued to it in plenty of shops.
This is where the decision flips. If your direct dependencies have PHP 8 compatible versions, even ones that need work, you have a short upgrade path. If the system is welded to a framework that stopped shipping fixes years ago, something is getting rewritten either way. The difference is that now you know it from a measurement, and you can rewrite the entry layer instead of the whole system. Rewriting routing is a task. Rewriting ten years of business rules is a different animal, and it is the one that kills timelines.
One more thing that shortens the boardroom debate: running PHP 5.6 today means running without security fixes since the end of 2018. That does not decide between upgrade and rewrite. It removes the third option, the one everyone secretly prefers, which is leaving it alone and revisiting next year.
The third measurement is not test coverage. List the four or five flows that generate money (order capture, pricing, billing, invoicing, the integration with whoever pays you) and ask one question about each: can I run this end to end on a clean machine with no customer involved?
That answer decides more than any stack debate. A system whose money flows are reproducible survives a version migration fine. A system where the only way to know billing worked is to wait for tomorrow's statement survives neither path, because in both you are shipping blind.
And be careful with a pretty report. I have opened projects with high coverage where the tests called the method and asserted nothing, no `expect` anywhere. The metric existed, the guarantee did not. Same reasoning applies when you decide who does the migration: an embedded senior squad measures first and writes second, which is how we work on inherited code.
That line shows up in almost every thread on this topic. It is true, and it is the worst possible argument for a rewrite.
Ugly code is the living documentation of your rules. That strange `if` in the commission calculation is a deal someone closed with a partner in 2019 and never wrote down anywhere else. When a team rewrites from scratch, it does not rewrite what the code does. It rewrites what it managed to understand, which is always less. The remainder comes back as production bugs, one a week, for months.
There is also the line nobody puts on the slide. A rewrite freezes the product. While the team rebuilds what already exists, your competitor ships and your sales team keeps demoing the same screens. Given what engineering costs per hour, a quarter with no new customer-facing work is money you pay twice, once in payroll and once in the frozen roadmap.
What works in most cases is three moves, in this order:
During step three the system runs split, and that bothers every engineer who likes clean things. Sit with the discomfort. It is reversible on any given day, which a nine-month rewrite is not.
Now the limits, because advice that only asserts is worth nothing. This path fails in three situations. When a core dependency has no PHP 8 compatible version and the vendor is gone, you are already rewriting and just have not said it out loud. When the business changed so much that the system models a company that no longer exists, a version bump preserves the wrong model in newer syntax. And when the whole operation fits in eight screens, the cost of rebuilding drops to weeks and the math flips, though I would count those screens with the code open, because that number always grows.
If you are taking this to a meeting this week, ask for two numbers before you look at a single proposal: how many occurrences PHPCompatibility flags, and how many direct dependencies have no PHP 8 release. If nobody on your team can answer that in two days, your problem is not choosing between migration and rewrite. It is that nobody has opened the system to look yet.
8 read minutes
Article content: