Skip to content
HHussain C

How I upgraded a Laravel 7 app to Laravel 13 in 3 days

A SaaS app on Laravel 7 and PHP 7.2, with almost no tests, moved to Laravel 13 and PHP 8.5 in 3 days with no downtime. Here's the order I worked in and where it got hard.

I recently took a client's SaaS app from Laravel 7 on PHP 7.2 to Laravel 13 on PHP 8.5. It took 3 days from start to live: 2 days of changes and 1 full day of testing. There was no downtime.

The twist is where it started. The app hadn't had a security fix since 2021, it had almost no tests, and a coding agent did most of the typing. That sounds like a recipe for a bad month. It wasn't, because of the order the work was done in.

Why upgrade an app that still works?

Because "it works" and "it's safe" stopped being the same thing years ago.

Laravel 7 stopped getting security fixes on 3 March 2021. PHP 7.2 reached its end on 30 November 2020. So this app had spent more than five years running on software that nobody patches any more. When PHP2Shell hit in October, apps like this were the ones with no fix to install. I wrote about that night in how I patched 35 servers after PHP2Shell.

There are slower costs too. New packages don't support old versions, so every new feature gets harder. And fewer engineers want to work on a codebase that's six major versions behind.

Why not jump straight from 7 to 13?

Because six upgrades at once means six sets of changes failing together, and no way to tell which one broke what.

Each Laravel version has its own upgrade guide and its own minimum PHP version. Going one step at a time keeps every change small enough to understand.

QuestionMinimum PHPWhat changes most
Laravel 8PHP 7.3Model factories become classes, and models move to their own folder by default
Laravel 9PHP 8.0New versions of the Symfony and file storage libraries underneath
Laravel 10PHP 8.1Type hints across the framework, and long-deprecated methods removed
Laravel 11PHP 8.2A slimmer structure for new apps. Upgraded apps can keep theirs
Laravel 12PHP 8.2Mostly dependency updates. The Breeze and Jetstream starter kits stop getting updates
Laravel 13PHP 8.3Small behaviour changes, like renamed queue event properties and cache prefixes
Each step from Laravel 7 to 13, with the PHP it needs and what changes most.

PHP moves up alongside Laravel. By the end the app was on PHP 8.5, the newest release, which Laravel 13 supports.

How did the coding agent and Shift split the work?

Two tools, each doing what it's good at. Laravel Shift is a service that makes the routine upgrade changes for one version and hands them over as a pull request. Claude Code, a coding agent that reads and edits the codebase from the terminal, handled everything Shift couldn't: the app's own code, the packages and the failures.

  1. 01ShiftThe routine changes for this version
  2. 02Claude CodeThe rest, working from the upgrade guide
  3. 03TestsRun the full set, fix what fails
  4. 04ReviewI read every change before it's kept
  5. 05CommitThen on to the next version
The loop I ran for each Laravel version, from 8 through to 13.

The agent was fast at the tedious part: renaming, updating method calls, working through a list of failing tests one by one. What it doesn't do is decide anything. My rules for the agent are the same on every job: it never merges its own work, never touches production, and anything in sign-in or payments gets a line-by-line read from me. I wrote more about these rules in how I use coding agents without shipping their bugs.

How do you add tests to an app that has none?

You start with the parts that would hurt most if they broke, and you write those tests before changing anything.

For a SaaS app that usually means signing in and out, signing up, payments, and the main screens customers use every day. I write those tests against the old version first and make sure they pass. From then on, every version step has to keep them passing. A step that breaks sign-in fails right there, not three versions later.

This is the most important step of the whole job. Without tests, "the upgrade worked" only means "the page loaded when I clicked it".

Where did it get hard?

Not in the framework. Laravel's own changes were well documented and mostly mechanical. The hard parts were the things around it.

  • Abandoned packages. Some packages the app relied on had stopped being updated years ago and would never support PHP 8 or a new Laravel. Each one needs a decision: swap it for a maintained package that does the same job, or move the small piece of it the app actually used into the app's own code.
  • The old auth scaffolding. The login and registration screens came from a scaffolding package built for the Laravel 7 era. They had to be brought up to date carefully, because auth is the one place where a quiet mistake either locks people out or lets the wrong person in.
  • PHP 7.2 to 8.5. PHP 8 is much stricter. Code that used to pass the wrong type, read a missing array key, or call a removed function now fails loudly instead of quietly. Most fixes were small, but there were a lot of them, and that's exactly where an agent working through a list of failing tests earns its keep.

How did it go live without downtime?

By making the real switch boring. The third day was all testing. The upgraded app ran on staging, a copy of production with the same setup, and was tested there end to end before anyone touched the live site. Once it held up, it went live in one switch.

Nobody had to stop using the app while it happened.

Is your Laravel app due an upgrade?

  1. Is it on Laravel 11 or older?Yes →It's out of security support. Plan the upgrade now, one version at a time.No ↓
  2. Is it running on PHP 8.1 or older?Yes →That PHP gets no more fixes. Moving it up is part of the same job.No ↓
  3. Would nothing warn you if a change broke sign-in or billing?Yes →Write tests around those flows first. It's the cheapest step and the most important.No ↓
  4. Do any of your packages have no release from the last two years?Yes →Check each one for PHP 8 support before you start. They're where upgrades get stuck.No ↓
  5. Did the last emergency PHP patch skip you?Yes →Your version is too old to patch. Upgrade it before the next one.
  6. Still no →You're in good shape. Upgrade once a year, when the new version comes out, and it stays a small job.
Five questions about the Laravel app you run now. Stop at the first yes.

An old Laravel app is rarely a reason to rebuild. Upgraded one step at a time, with tests first, it's usually days of work, not months. If yours is a few versions behind, that's what my code rescue and upgrades service is for. Or send me a note with your Laravel and PHP versions, and I'll tell you what the path looks like.

Quick answers

Can you upgrade Laravel 7 straight to Laravel 13?

You can try, but it's the slow way. Each Laravel version has its own upgrade guide and its own minimum PHP version, from PHP 7.3 for Laravel 8 to PHP 8.3 for Laravel 13. Going one version at a time, with tests run after each step, means that when something breaks you know exactly which change caused it.

How long does a Laravel 7 to 13 upgrade take?

It depends on the size of the app and its packages. I recently took a SaaS app from Laravel 7 on PHP 7.2 to Laravel 13 on PHP 8.5 in 3 days, start to live: 2 days of changes and 1 day of testing, with no downtime. The hard parts were adding tests, replacing abandoned packages and updating the old auth code, not the framework itself.

Can AI coding agents upgrade a Laravel app safely?

Yes, if they work inside firm limits. I used Claude Code alongside Laravel Shift, one version at a time, with tests run after every step and every change reviewed by me before it was committed. The agent did most of the typing. It never merged its own work or touched production.

What breaks when upgrading from PHP 7.2 to PHP 8.5?

Mostly code that PHP used to let slide. PHP 8 is stricter about types, turns many old warnings into errors, and has removed or deprecated functions that older code and packages relied on. Packages that were never updated for PHP 8 are usually the biggest source of trouble.

Contact

Want this in your product?

I read every message myself and usually reply the same day. A rough idea is enough to start.

Or send a short brief

Hours

Your hours work for me.

  • San Francisco

    --:--

  • New York

    --:--

  • London

    --:--

  • Berlin

    --:--

  • Dubai

    --:--

  • Singapore

    --:--

  • Sydney

    --:--

  • Surat

    --:--

    Me

I keep flexible hours and move my day to overlap with yours, whether that's a morning standup in New York or a late release in Sydney.