Laravel has a reputation for aging well, and mostly it deserves it. That reputation is also why a lot of Laravel apps get neglected for years — the app "still works," so nobody looks under the hood until something breaks in production.

Here's what we actually check for when a client asks us to look at an existing Laravel codebase.

You're more than one major version behind

Laravel ships a new major version roughly every year, with security support tapering off after that. Being one version behind is normal. Being three or four behind usually means every dependency in your composer.json has drifted too, and the upgrade path isn't "bump the version number" anymore — it's untangling a dozen packages that each need their own compatible major version, some of which may be abandoned entirely. We've walked into projects still on Laravel 6 with a composer.lock full of packages with no commits since 2021. That's not a Laravel problem, it's a maintenance-deferred problem, and it compounds every quarter you wait.

Queries that don't scale past a certain data size

This is the one we see most often, and it's rarely about Laravel itself — it's about how Eloquent gets used. N+1 queries hiding behind foreach loops over relationships, missing eager loading, and models with dozens of relationships that all get loaded regardless of whether the page needs them. This is invisible in development with a few hundred rows of seed data and becomes a five-second page load in production with real volume.

We ran into this directly on an equipment marketplace client with 30,000+ pieces of equipment cross-referenced against 200,000+ compatible parts — a genuinely complex relational graph. The queries had been written for a much smaller catalog, and by the time it grew, some pages were timing out and the standard response had been to keep bumping the RDS instance size. That's treating a symptom. We went back into the query layer, fixed the eager loading and indexing, and the same infrastructure that used to choke now runs the same lookups instantly. If your instinct when pages get slow is "let's upgrade the database instance" before you've profiled a single query, that's a sign worth paying attention to.

Everything runs synchronously, including things that shouldn't

Laravel's queue system is one of its best features, and it's astonishing how often it's unused. Sending a confirmation email, generating a PDF, calling a third-party API, syncing to an ERP — if any of that happens inline during the request/response cycle instead of on a queue, your users are waiting on infrastructure that has nothing to do with what they asked for, and a slow third-party API becomes your outage.

No tests, and everyone's afraid to touch the checkout code

You don't need 100% coverage. But if there's a part of the app everyone tiptoes around because nobody's sure what will break, and there's no test suite to tell you, that's technical debt with a very specific, very expensive shape: every future change to that code carries hidden risk that compounds with every developer who touches it without full context.

What this actually costs you

None of these show up as an outage until they do. The real cost is slower feature development, because every change has to account for undocumented fragility, and a growing gap between what the business needs and what the codebase can safely support. A modernization pass — upgrading the framework, fixing the query layer, adding queues where they're missing, and building a test suite around the riskiest paths — is almost always cheaper than the rewrite it eventually forces if left alone. We wrote more about that trade-off in Technical Debt: When to Refactor and When to Rebuild.