How to Keep Your Software Dependencies Updated

Martin Luder
HeyJobs Tech
Published in
5 min readApr 30, 2024
Hand Stopping Domino Effect
Photo by Oleksandr P from Pexels

Dealing with dependency updates is a common challenge in software projects. How they are approached has a large impact on the speed of feature development.

Modern projects often have hundreds or thousands of dependencies. Keeping all of them up to date can be overwhelming without a good strategy. And failing to do so can result in a lot of unplanned work when you have to upgrade a dependency as that often requires upgrading other outdated dependencies as well.

We’ll show why keeping dependencies up-to-date is often the best approach and how we put it into practice.

What does it cost to delay updates?

It’s easy to postpone updating dependencies until there is a reason for it. You can focus on your roadmap, taking time away from that to update dependencies that work fine feels like an unnecessary distraction.

At some point, you will encounter a reason to upgrade a dependency though:

  • A crucial feature is only available in a newer version.
  • A security vulnerability is discovered in the dependency.
  • There is a bug that affects you that is only fixed in a new major version.
  • You want to add a new dependency, but it is incompatible with one or several of your outdated ones.

It’s likely that updating will involve quite some unplanned effort because other dependencies need to be updated as well and any of them could have breaking changes. It can be quite the rabbit hole. As updating has now become urgent, you cannot delay this work. This means more stress for the engineers and, potentially, missed deadlines.

Updating many outdated dependencies at the same time is a complex endeavor, often requiring the attention of an experienced engineer: Any change comes with the risk of introducing a bug. Making many changes at the same time increases the risk exponentially. And when invariably something breaks, you won’t know where to look for potential root causes.

This can be very costly, up to the point that such an update becomes impossible with the available resources. You might end up forking your outdated dependencies to backport a bug fix or implement features available in newer versions yourself. In the worst case, you might even consider a rewrite of the project.

If you have a lot of outdated dependencies, you are always at risk of spending a lot of time on an unplanned update, which makes planning much harder.

So what does the alternative look like?

So let’s look at the same scenario if you’ve frequently updated all your dependencies:

  • You most likely are already using a version of the dependency that includes the feature or bug fix you need.
  • You are already using a recent version that still receives updates so reacting to a new security vulnerability is straightforward.
  • There is no version conflict with the new dependency you want to add.

So probably there is no or little effort involved and you can stay focused on your goals.

That’s great, but keeping your dependencies up-to-date comes with even more benefits:

New features and better performance

The maintainers of your dependencies release new versions with additional features, bug fixes and performance improvements. By upgrading quickly, you get these benefits earlier.

Better developer experience

Improved performance and new features don’t only help on production. Engineers benefit greatly from faster build times, better development environments and new language features. It’s also easier to find documentation and help for current versions.

Keeping your dependencies up-to-date almost completely eliminates the risk of unplanned updates, speeds up development of new features and improves the developer experience.

But won’t this take too much time away from feature development?

In an actively developed project, you end up having to upgrade all dependencies eventually. By doing this in continuous incremental steps you will actually spend less effort overall than if you wait until you have to. Most updates will not even cause any issues, and if they do, you’ll immediately know where to look for potential root causes as the changes are small. You’ll have updated to a fixed version before a bug in a dependency even affects you. Your team is practiced in dealing with dependencies and has improved the tooling around it.

This is analogous to continuous integration: Merging your changes often is less work than doing so after working in a branch for an extended period.

And if you have to meet a tight deadline, you can always pause the frequent updates temporarily.

Updating your dependencies frequently actually saves you time in the long run.

What does it look like in practice?

This sounds great, but we wanted to know if it actually works for us in practice, so we introduced frequent dependency updates in two of our projects.

1. Automate creating update Pull Requests

To ensure the frequent updates come with as little friction as possible, we configured the projects to automatically create Pull Requests updating all dependencies on a schedule and send a review request to the team. From that point we can use our already established review and QA processes to get the updates merged.

2. Set update frequency

Next we set out to find the right frequency to create the Pull Request. We started with a monthly schedule, but switched to weekly after a few months because the monthly Pull Requests were still too large and a weekly rhythm lined up better with the sprint cycles of the team.

3. Put hard updates in your backlog

Sometimes an update requires more work than capacity allows for. In that case we pin the dependency at its current version and create a ticket in our backlog. Then we proceed with just the remaining updates in the Pull Request.

If you choose your strategy so that it aligns with the way the team works and automate the boring parts, you can keep your dependencies up-to-date without distracting the team.

Our learnings

It turns out that most weekly updates can be applied with no or few code changes. And creating tickets for the ones that cause issues makes this technical debt more visible in the project.

It’s important to have good automated test coverage, which is a tremendous help with upgrades and gives us the confidence to merge these updates after a successful run (If your coverage is too low, your time might be better spent improving that). Automated tests do not replace reading the changelogs of the updated dependencies though. Having only a handful of them in a single Pull Request makes reading them carefully less difficult.

The biggest impact comes from having a strategy to update dependencies in place at all and following it. The incremental process keeps the dependencies fresh and new features ready to use. Moreover, reacting to newly discovered security vulnerabilities is distraction free and fast.

Are you interested in working this way? Check out our open positions here and join HeyJobs!

--

--