Skip to content

Approaching a major version dependency upgrade

Erica Pisani
Erica Pisani
4 min read

Keeping dependencies up to date has become significantly easier over the years with free tooling such as Dependabot and Renovate now widely available and having robust configuration for however you and your team want dependency updates to be handled within a given project.

While these tools are easy to incorporate into your projects regardless of their size and complexity, getting all of your dependencies caught up to the latest versions can be quite the investment from a time and engineering resources perspective.

Inevitably, there will be some dependencies within a project that are several major versions behind - likely due to the time required to make the necessary changes and properly test them - and these may need to be approached cautiously due to their function within a project (e.g: a framework that everything is built on like React or Restify).

One of my team mates recently asked me how I generally approach doing such an upgrade, so I thought I’d share my answer in case others are looking for ideas for their own personal ‘dependency upgrade’ checklist.

Checklist

Feel free to jump down to the relevant section for more details, but at a high level, this is what I generally follow depending on the risk and impact of the upgrade:

  • Read the release notes published by the package
  • Audit the code base and its related user-facing functionality
  • Evaluate backwards compatible options
  • Organize a bug bash
  • Schedule a day and time for the deployment and coordinate with the relevant support teams

Read the release notes

If you’re using one of the automated dependency management tools mentioned above, and depending on how the maintainers of the package like to share their release notes, these notes may be in the pull request summary that is automatically created by the tooling.

However, if you don’t have such a tool configured for your project, some places to look for the release notes are:

  • The Github (or other source control equivalent) project itself, either in the CHANGELOG.md file or the ‘Releases’ section
  • Package registry service (such as NPM)
  • The website of the project, likely under where their documentation lives

For larger, more encompassing changes within popular projects, the maintainers sometimes outline a helpful migration guide to make it easier to understand the motivation for the breaking changes that they’ve made and how to go about addressing them within your own project.

Audit the code base and user-facing functionality

Based on the release notes and migration guides, I audit the code base for occurrences of the functionality mentioned as either being deprecated or removed as part of the release.

As part of that, I also make notes about the user-facing functionality that the code is related to. Are the users in this case internal to the company (a tool the support or product team uses)? Or are they external to the company? Is the feature heavily used (a dashboard that appears when a user first logs in)? Or is it an early release feature that’s only offered to a small subset of customers that understand it may change at any time?

Another aspect to be mindful of (especially in very early stage start ups) is to determine the test coverage of the code. Are there unit or integration tests for the code? Are there end to end tests that are regularly run to confirm that critical user-facing functionality is working as expected?

All these things play a role in the amount of risk posed by the changes that need to be introduced for the upgrade.

Evaluate backwards-compatible options

In some cases, maintainer will offer backwards-compatible functionality or configuration in order to maintain the behaviour of a certain aspect of their package that they have deprecated.

Leveraging the backwards-compatibility of deprecated functionality can be helpful in clearing a path to upgrading to a newer version of the package sooner rather than later within a project, but it’s something that will need to be monitored closely over time, as there’s often a reason the previous way is being replaced with the newer way.

As part of performing an upgrade where a backwards-compatible option exists, a question that will be asked is “is it better to make the change now given it will likely be removed eventually? Or later due to the risk and uncertainty of the impact that the change will have in today’s codebase?”

Depending on what was deprecated, a risk of not migrating away from that functionality sooner rather than later is that other developers may continue to introduce that old functionality and associated patterns into newer code, thereby increasing the amount of technical debt that will need to be paid down later when it needs to be fully migrated over.

On the other side of that coin though, the risk of migrating to the new functionality (again depending on what the functionality itself is and its use in the context of your codebase) could be so high as to require a larger, coordinated effort on the part of the engineering team within the organization in order to ensure minimal disruption to the end user of the project.

Organize a bug bash

  • Organize a bug bash (i.e: manually test common user workflows in the product) with the folks who know the code affected and user-facing features best, with a particular emphasis on the functionality that you would’ve made a note of earlier in your audit.

Schedule date and time for the deployment and coordinate with the relevant support teams

  • For particularly high risk upgrades, schedule a day and time for when you plan on deploying the changes. Have someone very familiar with the code and functionality available to help you monitor the release, and ensure that the support team (or whoever is acting in this capacity) as well as the individual(s) that are on-call are aware of the release so that they can surface anything that looks related to your release to you immediately.
  • If available, doing a canary release will help ensure all users aren’t impacted if something is going wrong, and can help focus your monitoring for potential issues to specific accounts and/or users.

Wrapping up

Depending on the size and maturity of the company that you work at there might be more or less steps here to follow within your engineering and product organization for such an upgrade process, but if you’re looking for a lightweight TODO list to get you started, I hope my list is helpful.


📫
Enjoy this post? Subscribe to be notified when I publish new content!
tips-and-tricks

Comments


Related Posts

Members Public

How to install nvim-ufo in LazyVim to enable foldable code blocks

A feature that I've found myself missing since making the switch to Vim as my editor of choice is the ability to easily fold and expand code blocks. I know this functionality is included in Neovim, but I couldn't understand the native implementation enough to use

How to install nvim-ufo in LazyVim to enable foldable code blocks
Members Public

Git Log's Hidden Gems: Using -S and -L for Powerful Code History Search

Ever needed to track down when a specific piece of code was first introduced in a project? As part of some refactoring I had to do recently, I needed to do just that for a variable on a Django model. I was already familiar with the basic git log command,

Git Log's Hidden Gems: Using -S and -L for Powerful Code History Search
Members Public

Using XOR to write concise conditionals

It's not uncommon that I sometimes write if statements where the overall conditional is true when both conditions are true or both are false. As an example, let's say I'm validating input from an API call where I'm updating information on a