A PHP application that has quietly run a business for eight years is not a problem. It is a working asset that has already paid for itself several times over, and the instinct to replace it because it "looks old" is one of the more expensive instincts in software.

But some ageing applications genuinely do need work, and one condition in particular makes it urgent rather than optional. This article is about telling those apart.

The one thing that actually forces action

Everything else on this page is a judgement call. This is not.

If your application only runs on a PHP version that no longer receives security fixes, you have a security problem, not a technical debt problem.

PHP branches get roughly two years of active support followed by two years of security-only fixes, then nothing. The authoritative list of which branches are still supported is php.net/supported-versions, and it is worth checking against your actual version today rather than trusting a number you remember.

Why this is different in kind: when a critical vulnerability is found in the PHP engine, the project issues patched releases for supported branches only. In October 2026 two critical remote code execution flaws were patched in PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. Nothing was released for 8.1 or earlier, and nothing will be. An application that cannot run on a supported branch has no route to a patched engine — there is no vendor to escalate to and no configuration that substitutes.

So the first question is not "is this code modern?" It is "can this run on a supported PHP version, and does it?" If the answer is no, that work is now the priority, ahead of every feature on the roadmap. If the answer is yes, you probably have more time than you think.

Signs worth taking seriously

Beyond the runtime, these are the symptoms that reliably indicate real cost rather than cosmetic age.

Changes take longer than they used to, consistently. Not one awkward feature — a trend. If a change that would once have taken a day now takes a week, something structural is making it expensive, and that cost compounds.

Nobody can say what a change will break. The reliable tell is a developer who will not touch a file without being asked twice. Usually it means business logic is duplicated in several places, or spread through templates, so no single change is contained.

Dependencies cannot be updated. A library that is three major versions behind, pinned because upgrading breaks something, is a door you have locked from the inside. The gap only grows, and eventually a security advisory arrives for the version you are stuck on.

It only runs on one machine, configured by someone who has left. No reproducible environment, no documented setup, deployment by uploading files over FTP. This is an availability risk disguised as a workflow.

Mobile is an afterthought in a measurable way. Not "it looks dated" — people use it on phones and cannot complete tasks.

The database schema no longer matches the business. Columns called extra_field_3, status values encoded as numbers whose meaning lives in someone's head, relationships maintained by convention rather than constraint. This is the most expensive category to leave alone, because every new feature has to work around it.

Nobody is confident deploying on a Friday. A culture signal that accurately reflects a technical reality.

Signs that look alarming and usually are not

Equally important, because acting on these wastes money.

It is "just PHP" rather than a framework. Plenty of hand-rolled PHP applications are well structured, and plenty of framework applications are not. The framework is not the measure.

The code style is old. snake_case, no type declarations, no namespaces. Dated, not dangerous. Modernising style is a nice thing to do while you are in a file for another reason, and a poor reason to open a project.

It uses a technology that is no longer fashionable. jQuery is not a defect. Server- rendered HTML is not a defect. If it works, serves users well and can be maintained, it is doing its job.

It is not "cloud native". A single well-maintained server running a business application is a perfectly respectable production architecture, and for most businesses it is the cheapest reliable one.

One developer thinks it should be rewritten in their preferred stack. Sometimes correct. Ask for the business case in terms of cost and risk, not elegance.

The real risk ladder

Roughly in order of how much it should worry you:

  1. Unsupported PHP version. Unpatchable, exploitable, and already public. Act now.
  2. Unpatched dependency with a known advisory. Same category, usually narrower in scope.
  3. No working backups. Not strictly modernisation, but if you discover it during an assessment it jumps to the top. Untested backups are the most common serious finding we make.
  4. No ability to deploy a fix quickly. When a patch is needed and nobody can safely deploy it, every other problem gets worse.
  5. Unmaintainable code around money or personal data. Where a mistake is expensive rather than embarrassing.
  6. Everything else. Real cost, no deadline.

Gradual modernisation versus rebuilding

The default assumption should be gradual. Full rewrites of working business applications fail far more often than they succeed, for a reason that has nothing to do with engineering skill: the existing system encodes years of accumulated business rules, most of them undocumented, many of them discovered only when they stop happening. A rewrite has to rediscover all of it while the business continues to run on the old system — and the old system keeps changing while you do.

Gradual modernisation usually looks like this, in order:

  1. Get it onto a supported PHP version. Nothing else first. This is often smaller than feared: the deprecations between recent PHP versions are well documented in the migration guides (8.3, 8.4, 8.5), and static analysis will find most of the affected code before you run it.
  2. Make it deployable and reversible. Version control if it is not already there, a reproducible environment, a deployment you can undo. This is what makes everything after it safe.
  3. Get tests around the parts that handle money. Not full coverage. The payment path, the invoicing calculation, the permission checks.
  4. Update dependencies, one at a time, with the tests in place.
  5. Then improve the areas that cost you most, guided by where changes actually take the longest rather than by which code is ugliest.
  6. Modernise the front end where it earns it. An existing PHP application can be given a genuinely app-like interface with htmx and Alpine.js — partial page updates, no full reloads — without rebuilding it as a single-page application. That is a fraction of the cost of a rewrite and keeps every server-rendered page you already have. It is how this site is built.

A rebuild is justified when: the application cannot be made to run on a supported runtime at a sensible cost; the data model is so misaligned with the business that every feature fights it; the product needs to become something structurally different (a multi-tenant platform, say, where it was built single-tenant); or the code is genuinely unintelligible — no structure, no naming, and nobody who understands it.

And even then, prefer replacing it in pieces: run the new system alongside the old one, move one workflow at a time, share the database where you can. We have done this on inherited applications where the brief was initially "rebuild it", and the result was a stabilised platform with the viable parts kept — the Reserve King and Pewny Parking case studies both took that route. Our Kyoto Daily Tours project was the other kind: a decade-old CodeIgniter platform where a rebuild genuinely was the right answer, with the original data migrated across.

Protecting the business while you work

The non-negotiables, from doing this a number of times:

Nothing happens without a tested restore. Not a backup — a restore you have actually performed.

Stand up a staging copy with real-shaped data. Anonymised if it contains personal data, but structurally the same. Bugs that only appear with real data are the expensive ones.

Write down the business rules as you find them. Every undocumented rule you discover in the code is an asset. Capture it. This document is frequently the most valuable deliverable of a modernisation.

Keep a working rollback at every step. Able to put yesterday's version back within the hour, at any point.

Do not change behaviour and infrastructure at once. Upgrade PHP. Verify. Then change features. When something breaks after a combined change, you have two suspects and no clean way to separate them.

Test the once-a-month paths. Month-end invoicing, scheduled reports, reconciliation jobs, the annual renewal process. These are where version changes surface, weeks after everyone declared the upgrade successful.

Tell the people who use it. They know which screens matter and which have been broken for a year, and their list is better than yours.

How we assess one

If you are unsure which situation you are in, the useful first step is small: establish which PHP version actually serves the application, which dependencies have outstanding advisories, whether backups restore, and how long a typical change currently takes.

That is usually a few days of work and it produces a straight answer — upgrade the runtime and leave the rest alone, modernise in stages, or genuinely rebuild — with reasoning you can check rather than a recommendation you have to trust. We do this as a contained assessment precisely because the three answers have very different price tags, and nobody should commit to one of them on a hunch.

Our Application Modernisation service covers both routes. We will tell you if the honest answer is "leave it alone and keep it patched", because for a surprising number of applications that is the right advice.