Catching Up Laravel

How to Safely and Efficiently Upgrade an Out-of-Date Laravel App

Feature image: Catching Up Laravel

Is Your Laravel App ... a Little Behind?

If you are running a Laravel application in the 5.*'s, don't have any tests, are running a legacy application bootstrapped inside Laravel, or are staring in silent terror at just how long ago your server's PHP install stopped receiving security updates, this post is for you!

In this post, we'll be focusing on applications that are already running Laravel. If you're running a legacy PHP application that isn't bootstrapped inside of Laravel, take a look at another post on this blog titled Converting a Legacy App to Laravel; Andrew put together an incredible step-by-step guide for taking those first big steps.

It's easy to feel locked down and overwhelmed by the seemingly endless, overlapping responsibilities of updating an old application. So, let me tell you a few things, human-to-human:

  • Upgrading Real World Applications is Hard
    Really hard. There is endless nuance to consider: learning which technologies are most crucial to upgrade first, considering downtime, data migrations, and ensuring continuity of application behaviour. You need to do all of this while managing pressure from stakeholder feature requests
  • Mistakes and Bugs are Unavoidable
    Make sure your supervisors understand this. If you're in an industry where mistakes can not happen, advocate for the testing, quality assurance (QA) processes, and/or client trial deployments to minimize problems to the degree appropriate for your industry.

    I entreat you: don't let the fear of new bugs be more powerful than the fear of an application that isn't getting security updates.
  • It's OK to Ask for Help
    Upgrading an application takes skill and experience that you may not have. This truth applies if you've just been doing your best to maintain an application that was dumped on you, or if you're the one who originally wrote it ten years ago. If you don’t have time or in-house resources to upgrade, then consider hiring someone to help. You often get what you pay for, but at minimum, I recommend you be able to easily communicate with any contractor you hire.
  • Beware Anyone Who Claims Ease
    You may feel thwarted by your application in every attempt to improve it. Even so, you are an incredibly valuable resource for knowing how it works now, what needs to be upgraded first, what might break, and how high of a priority each of the necessary changes is. Gently challenge claims that something should be quick when you have experience-based doubts.
  • One Step at a Time
    I rarely recommend one big push to upgrade all of your technologies. The pull request (PR) necessary to execute this update might need to be active for months or years. While that PR is in progress, your application still needs routine bug fixes, features, etc.

    Any changes to your application need to be pulled into your update PR, which means each feature/bugfix needs to be QA'ed twice: once on your feature or bugfix branch, and once in your update branch. Not doing this double-QA can result in one of the most annoying things a customer can experience: something is broken for them, is fixed, and then weeks or months later breaks in exactly the same way. Shorter running upgrade PRs can help avoid this situation.

Triage Your Concerns

It's easy to freeze or get lost in the endless bunny-trails of things that need to be done. Let's break down this big task into triage levels.

Triage Point #1: Get Secure

First, we want to audit your site to make sure you don't have any potential security issues. This is, obviously, more important than any other part of the upgrade process—missing features is one thing, but actual vulnerabilities trump anything else.

Perform an Initial Security Audit

Perform an audit to determine which of your technologies have active vulnerability advisories, are scheduled to drop from security support, aren't receiving security fixes, or are EOL (end-of-life). Check out LaravelVersions and PHPReleases, and the GitHub Security Advisories for your project, to get started. Take that list and order them by severity. Pick the first item off of that list and start experimenting to discover the minimum number of technologies you need to update to support this change. Use this list to guide your priorities for the rest of this post.

In many cases, the three primary security updates of a server running a Laravel app will be the server's operating system, PHP, and Laravel. After that, it might be your database, Composer package dependencies, or JavaScript build dependencies.

Your goal: curate an informed set of instructions regarding the minimum number of upgrades needed to get your application out of the security danger zone. Move quickly and loosely to find and document blockers. Your focus right now should be documenting these blockers and security issues, not necessarily resolving them right at that moment. In most cases, I think this discovery and note-taking process should take a day or a few days, but not weeks.

If you're running a local or staging environment, try upgrading those first. Take notes about what it takes to run these upgrades, and your solutions to random gotchas as you go. You might find yourself in a situation where you need to restart the entire upgrade process, and it'll be a LOT easier if you document what worked.

Split Up the Security Work

Now that you have a list of blockers and the steps needed to upgrade, take some time to think about how you can split this work up into as many separate Pull Requests (PRs) as possible. Even if you opt to release them all at the same time, breaking your code up into individual chunks makes it easier to keep on track and avoid scope creep (and overwhelm).

In my experience, in most cases an application can be updated in two releases:

  • A first update, to releases of its dependencies where packages and services are still getting security updates
  • A second update, to current releases of its dependencies

You may find that there is only a little extra work necessary to get your application up to current releases. In other cases, there may be a significant data or code overhaul needed before you can even start upgrading dependencies.

See Upgrade Errors as Your Friends

Once you settle on an intended scope and start working, you'll probably find yourself deep in a quagmire of errors. I find it helpful to reframe this as errors being my guide on the path forward, rather than the bane of my existence. 🙃

When you discover an error, don't be afraid to move forward and dig deep down the stack, or try approaching a specific error from a different direction; oftentimes there may only be a line or two of code that's blocking you from that version upgrade... the magic is in finding which lines.

In some situations, I've saved myself from needing to upgrade more technologies/versions by tweaking my application code. Consider moving some logic outside of a package when a specific method of that package calls a deprecated/end-of-lifed method (or one that requires a higher version of PHP). Build your own wrapper for the package so you can isolate the offending code from use. Sometimes you can extend a class of that package and leverage Laravel's IoC binding capabilities to load your patched version of that class instead of the one that throws the error. In really tight spots, you can use Composer and PSR-4 to override specific classes of a package with ones inside of your repository.

Triage Point #2: Ensure the App Keeps Working With Testing & QA

Every application can benefit from a balance of automated testing and manual QA. These processes ensure your users will have a consistent experience through the upgrade process. The more effective your automated testing, the less you'll need to rely on manual QA to ensure the application is behaving correctly at each step.

When to Use Automated Tests vs. QA

Balance the cost of whatever QA system you employ against the user confidence undermined by bugs introduced by your upgrade. If your app doesn't have meaningful test coverage, it's worth evaluating whether it's more affordable to pay for programmers to write more tests or for QAs to cover the majority of the app during the upgrade process. You also may find that your users are more or less tolerant of bugs depending on your industry and your relationship with them, so be sure to include that in your calculations.

The Value of Automated Tests

If you've already developed a strong testing suite, the process of upgrading and then running your tests will likely give you an incredible amount of guidance in the form of all of those Es and Fs. Remember, errors are your guide! It might be a slog but at least it's structured; take a block of time and work through them, reminding yourself to take breaks when you tire or can't figure out how to solve a problem.

I love that once a test is written, I can use it to assert a specific behavior until the end of time (that is, when business logic changes and a new timeline is created in the multiverse). Writing a good test is an investment in your future happiness as a programmer. Computer cycles are cheap compared to human ones. Luckily, the computers I work with do not dread repetition.

Triage Point #3: Rewrite Existing Features

Sometimes, you'll hit a bit of code, or an entire feature that is written in a way that can't come along with the upgrade. If you need that feature, it's probably time to rewrite it. In these moments, ask yourself this question: "When's the last time I wrote this kind of code in fresh Laravel, Vue, React, JavaScript, etc?"

If you've been spending a long time maintaining this legacy application, you may not be up-to-date on the best practices of writing new code in this tech stack. So, before you set out to write this feature again from scratch, it may be worth taking a pause to catch up on tutorials, write a similar project in a greenfield (brand new) app, or whatever else helps you get up your confidence.

One great part about this part of an upgrade: since you already know this feature will be a necessary part of your upgrade, consider doing this work in an earlier PR, before you start on your upgrade. You can write it, test it, QA it, and merge it, and now you've made the upgrade that much easier.

Offer Little Wins

Most of the time we upgrade, we're primarily focusing on keeping the exact same existing feature working. But when something breaks during an upgrade, or even if nothing breaks but it takes time and money to upgrade with no visible progress, users and stakeholders can often get frustrated.

It can be challenging to empathize with humans when they submit a support request like "This thing has worked for me for the last 5 years and I logged in today to do some work with a deadline of yesterday and everything is broken!" They have no idea how much time and effort you put into that update that accidentally broke something for them, and you don't know how much pressure they're under.

Addressing this issue is easier for customer relations when you can point to improvements you released with that update. When you're upgrading, especially if you're having to re-write a feature from scratch as a part of the upgrade, add a few niceties everyone's been waiting on, or speed the page up a bit with youre new code. If users or stakeholders do come at you frustrated, be sure to point out the clearer design you made for a heavily trafficked page, or a tweak of application behaviour that saves the user a couple of clicks or keystrokes. Often in upgrades, you'll have made significant improvements in some page load times. Keep a list of improvements handy to reference in your customer service requests.

Small Applications Addendum

If you run a small application (under 25 routes), it may be easier and faster to spin up a fresh Laravel application and rebuild your application using the existing app as a reference.

Take the First Step!

I hope this post offers you a bit of guidance and structure to move towards starting to make the moves to get your application upgraded. It might be a monstrous task, but I believe in you!

Get our latest insights in your inbox:

By submitting this form, you acknowledge our Privacy Notice.

Hey, let’s talk.

By submitting this form, you acknowledge our Privacy Notice.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Thank you!

We appreciate your interest. We will get right back to you.