A lot of businesses still run on PHP applications that are ten years old or more. They work. They make money. And every change takes longer than it should, because nobody quite trusts the code any more.
The obvious answer seems to be “let’s rewrite it”. We understand the appeal, but we rarely recommend it for an important system. Rewrites take longer than planned, the old system keeps changing while you build the new one, and the business waits a long time to see any benefit. Modernizing the application step by step usually gets you further, sooner, with far less risk. This is how we go about it.
Is it time to modernize?
Some signs that it is:
- Small changes take days, because nobody is sure what else they will break
- Releases are slow, manual or scary
- You run a PHP version that no longer gets security fixes
- One or two people know how the app really works
- New developers take weeks to feel safe in the code
The last two are about people, not code, and they are just as risky. If the person who understands the app leaves, the app is in trouble.
PHP also makes the age of your code a security issue. When two serious PHP bugs were announced in October 2026, PHP 8.1 and older got no official update. We explain it in our guide to the PHP2Shell bugs. An old version means you can’t simply update when something like this happens.
Rewrite or modernize?
A full rewrite is sometimes the right call. Here is how we look at it:
| Modernize step by step | Rewrite | |
|---|---|---|
| Best when | The app does its job, but the code is old and hard to change | The technology is a dead end, or the business has changed so much that the design no longer fits |
| Risk | Low. Each change is small and can be rolled back | High. Nothing works for the business until the new system is finished |
| Time to first benefit | Weeks | Often a year or more |
| New features | Keep shipping the whole time | Often paused, or built twice |
If your main complaint is “the code is old and messy”, modernizing is almost always the better route.
Build a safety net first
Before we change anything big, we make it safe to change things at all:
- Get the code into version control if it isn’t already, and automate deployments
- Add error logging and monitoring so problems are visible
- Write tests around the flows that matter most, even if they start as rough end-to-end checks
- Make sure backups exist, and that restoring one actually works
- Add a static analysis tool such as PHPStan, which finds whole classes of bugs without running the code
None of this is exciting, but it lowers the risk of everything that follows. If you have just inherited the code from another team, start with our application handover checklist.
Upgrade PHP one step at a time
An outdated PHP version is a security risk and a performance drag, and it locks you out of modern libraries. Upgrading PHP, the framework and the key dependencies is often the single most valuable thing you can do.
Here is when security support ended for each version:
How we approach the upgrade:
- Move one version at a time. Get the tests passing on each version before you go to the next. A break then has a small, known cause.
- Let tools do the mechanical work. Rector can rewrite a lot of old syntax automatically. The PHPCompatibility rules for PHP_CodeSniffer list what will break on a given version.
- Check your dependencies early.
composer why-not php 8.3shows which packages hold you back, andcomposer auditlists known vulnerabilities in what you use. Old libraries that nobody maintains are often the hardest part. - Fix deprecation notices as you go. They are PHP telling you what will stop working next.
- Test on the new version before you deploy it. A container makes it easy to run the same code on several PHP versions side by side.
Fix the risky things early
While you are in the code, deal with the problems that put you at risk now:
- Passwords and API keys written into the code. Move them to environment settings.
- Database queries built by joining strings. Switch to prepared statements.
- Passwords stored with an old hash such as MD5. Move to PHP’s built-in
password_hash(). - Input that nobody checks before it reaches the database or the screen.
These are small changes with a big effect, and the safety net from the first step makes them safe to do.
Add structure gradually
Older PHP apps often mix database queries, business rules and HTML in the same file. You don’t have to untangle it all at once.
We usually bring in a modern framework like Symfony or Laravel alongside the existing code and route new features through it. The web server sends some addresses to the new app and everything else to the old one, so users never notice the split. Composer takes over dependency management. Business logic moves out of templates and into small services that can be tested. Duplicate code gets merged as we touch each area.
Every one of those changes is small enough to release on its own, and the app keeps running the whole time.
Replace pieces, not the whole thing
Sometimes one part really does need rebuilding: a checkout nobody dares touch, a reporting module that takes minutes to load, an admin panel from another era. When that happens, we replace just that piece. We put a clear boundary around it, build the new version behind that boundary, and switch traffic over once it’s ready.
Over time more of the system moves to the new structure and the old code shrinks. Developers call this the “strangler” pattern, because the new system slowly grows around the old one until the old one can be switched off.
Keep shipping
The biggest benefit is that the business never has to pause. New features keep going out, often faster than before, while the codebase quietly gets better underneath. You see progress every few weeks, not at the end of a year-long project that might slip.
How to tell it’s working:
- Releases get more frequent and less stressful
- Fewer bugs come back after a fix
- The tests cover more of the flows that make money
- The PHP version moves up, and the list of outdated packages gets shorter
- New developers become productive faster
A full rewrite is sometimes the right call. It should just be a deliberate decision made for a specific reason, and not the default reaction to old code.
Where to start
If you only do three things this month:
- Put the code in version control and automate the deploy.
- Add logging, and write a few end-to-end checks for your main flows.
- Find out which PHP version you really run, and work out the first upgrade step.
If you aren’t sure where your app stands, a short technical and security audit gives you an honest picture and a ranked plan before you commit to anything.
Frequently asked questions
How long does it take to modernize a legacy PHP app?
It depends on the size of the app, how much of it is tested, and how old the PHP version is. The useful part is that you don't wait for the end. The safety net and the first PHP upgrade step are small pieces of work, and each change ships on its own, so you see progress every few weeks.
Can we keep adding features while we modernize?
Yes, and that's the point. New features go through the new structure while the old code keeps running. Nothing has to pause. The only rule is that each change is small enough to release and, if needed, roll back on its own.
What if the app has no tests at all?
Start with a few rough end-to-end checks on the flows that make money, such as sign-up, checkout and the main report. They won't cover everything, but they will tell you quickly if an upgrade broke something important. Add more tests around each area as you touch it.
Should we move to Laravel or Symfony?
It's a good option for new features, because both give you routing, dependency injection and a large ecosystem. You don't have to move the whole app. Run the framework next to the old code, send new features through it, and move old parts over only when they need real changes.
Can we jump straight from PHP 5.6 to PHP 8.4?
You can try it in a test environment to see how big the job is, but we don't recommend doing it in production in one go. Moving one version at a time, with tests passing at each step, means every break has a small, known cause.
When does a full rewrite make sense?
When the technology is a dead end that can't be upgraded, when the business has changed so much that the old design no longer fits, or when the data model itself is the problem. If the main complaint is that the code is old and messy, modernizing is almost always safer and faster.



