6 strategies for updating software dependencies

This article was written through the eyes of an Android team. But its concepts apply to almost any kind of software project.



Rare is a project that does not have external dependencies. For an Android project, it’s impossible. These dependencies are what allow developers to more quickly deliver software by building on top of previous work instead of reinventing the wheel.

But every dependency also brings a cost, be it in size, complexity, or maintenance effort. When adding a new dependency you should evaluate its pros and cons. Things to take into account:

  • How big it is and how much it affects the size of your project
  • Does it come with a lot of functionality you don’t need?
  • Is it complex to use and makes your project harder to master?
  • Is the dependency actively maintained?
  • And how big is the team maintaining it?

Now to the main topic of this article. Once you’ve added dependencies to your project, how do you manage them? Especially, how do you keep them up-to-date? Ideally, you want to be using the latest version of a dependency, with the latest improvements and bug fixes. But updating dependencies can result in huge changes to your codebase. And you need to ensure everything is still working correctly.

So what approach should you take to manage dependency updates?


1 – Ignore it

Probably more used times than teams care to admit. This “strategy” works by adding dependencies once, and hope no further update is needed anytime soon. While it's easy to say this approach is wrong, some projects might be simple enough that the dependencies are not that tied to the core of the product. One simple non-critical functionality is all that’s required…

https://media.giphy.com/media/jUwpNzg9IcyrK/giphy.gif

Pros

  • Very low maintenance*;
  • No setup or configuration required.

Cons

  • Once maintenance is required, updating dependencies can become a huge project by itself;
  • Software could be vulnerable to bugs and security exploits on dependencies;
  • Your app might not work on the latest hardware or software.

*no maintenance until a forced update requires huge codebase changes.


2 – Check sporadically

A small improvement on the “ignore it” solution is to run checks on dependencies every now and then, and evaluate if they are worth the time to update them. Usually, it’s something external that motivates it, like:

  • a major security exploit going around;
  • some free time before the end of the year...;
  • Google Play Store warning telling you to update your SDK version or they’ll take down your app;
  • etc ... .

https://media.giphy.com/media/3osxYipCI3tqb7GhVK/giphy.gif

Pros

  • Low maintenance;
  • No setup or configuration required.

Cons

  • Software could be vulnerable to bugs and security exploits on dependencies;
  • Your app might not work on the latest hardware or software;
  • When you do update dependencies, it can still be a big effort.


3 – Check regularly

This is a great solution to keep a product up-to-date, especially for products that don’t have a defined release schedule. This strategy makes sure that when you do go for a new feature or update, you don’t end up with a major refactor over a single dependency update.

This strategy can be automated to run as a cron job in your CI pipeline, allowing the team to easily check what has changed and decide if something should be addressed straight away.

Pros

  • You can pick what is the right cadence for your team;
  • You can choose what to update, knowing that the next check will catch what was left;
  • Safer against bugs and security vulnerabilities;
  • Your app should run on the latest hardware and software;
  • Smaller update effort every time.

Cons

  • Requires regular allocated time to maintain the project;
  • The effort required to update dependencies can vary, for example, if there’s a major SDK version update or not.


4 – Check on every Pull Request

This solution might seem excessive, but it can fit projects that are yet to be released. At this point in a project’s life, there's a high influx of changes in the codebase. As such, keeping up with the changes is easier. And developers can pay closer attention to the release notes of the dependencies to better understand the consequences.

This strategy should be automated with an existing CI pipeline or a simple Git hook. For Android developers, a tool such as Lint can be configured to check for dependencies.

Pros

  • Safer against bugs and security vulnerabilities;
  • Your app should run on the latest hardware and software;
  • The team can have a better understanding of the libraries being used;
  • Updates in small chunks.

Cons

  • Can slow down development;
  • Less adaptable: a big dependency update can stall a small PR.


5 – Check before every major release

This strategy guarantees you ship software with the latest versions. It fits a mature project that has regular releases. And it ties nicely with the release Quality Assurance process a project might have in place.

This strategy can be automated with an existing CI or CD pipeline or a simple Git hook, on specific release branches. For Android developers, you could still use lint to check, but if you have a CI or CD pipeline going with Dependabot might be a good solution, if offers a lot of features and customization.

Example with Github actions

Pros

  • Safer against bugs and security vulnerabilities;
  • Your app should run on the latest hardware and software;
  • If there’s a solid release process, fewer bugs should arise from dependency updates.

Cons

  • Requires additional time when releasing a new version;
  • Without regular releases, dependency bugs and vulnerabilities can stay for too long.


6 – Automate Dependency Management

Most dependency updates can be automated if you have a CI/CD pipeline. Your project stays secure and up-to-date. And 90% of the time it only requires a simple changelog check, PR check, and a merge button click. This solution is particularly helpful to manage libraries and security-sensitive projects. But it requires a thorough test coverage, otherwise, you may be introducing bugs in your software without realizing it.

Depending on the project you are working, Dependabot might be a great solution to automate this process. Dependabot can be configured to open issues or PR’s, with the change logs right there, making it very easy for developers to keep an eye on what is changing.

Pros

  • Safer against bugs and security vulnerabilities;
  • Your app should run on the latest hardware and software;
  • A better understanding of the libraries being used*.

Cons

  • Requires the initial setup;
  • Requires a good test suite;
  • Some tasks might require human attention.

* if you configure the automation to create PR’s with the changelogs.




Notes:

One of the tools we talked about is Dependabot. We are aware it currently has issues with version catalog for Gradle. A good workaround for the current issue is to use ben-manes/gradle-versions-plugin. It can be customized and used with scripts to generate PRs and Issues. But it requires more configuration than Dependabot out-of-the-gate.




Conclusion

So what is the best way to manage dependency updates?

Well as we explained above, it depends! It depends on the stage of the project and the time you want to allocate for maintenance. These are typically the determining factors of what strategy you end up using. But if you can, do invest in dependency management automation, to reap its rewards in the future.


We thank Bruno Correia, David Fortunato, Gabriel Oliveira, Ricardo Silva, and Vasco Almeida for their early review and suggestions of this article.