Dependabot has pretty strong views on dependency management. If you use it, you’re signing up to keep your dependencies up-to-date all the time (although it’s easy to ignore versions you don’t want to use).
That’s great, but is keeping your dependencies up-to-date something you actually want to do? We think it should be, for two reasons:
- The latest version is usually the greatest version
- Iterative improvements are better than big-bang changes
The latest version is the greatest version
TL;DR: You probably already believe this. How often do you add a new dependency and chose anything other than the latest version?
Generally speaking, each new release has:
- New features. You knew that. Let’s not spend any more time on it.
- Better security. Yes, updates often include reactive security fixes, but did you know they’re also less likely to be affected by new vulnerabilities?
- Improved performance. Library authors are trying to ship you the best code they can. Generally speaking, it’s getting better over time.
- Bug fixes. Just take a look at these changelogs and tell me you’d be better off on an older version.
Don’t get me wrong, there are exceptions to the above. Rails 3 was slower than Rails 2. Major new releases can have new bugs, as well as fixes. Security vulnerabilities are occasionally only present in the latest version.
Overall, though, when you’re thinking about dependency management you’re looking for a default position, not a hard rule. From a security, performance, robustness and feature perspective, it’s rare that the latest version isn’t the best one. That’s why it’s almost always the version you pick when you add a new dependency.
Iterative improvements beat big-bang changes
TL;DR: You almost certainly already believe this. If you’re not applying it to your dependency management, the only reason is probably a tooling one.
If you’re reading this blog post then the year is at least 2017 and you’re almost certainly taking an incremental approach to improving your product. Everyone tells you to. You may or may or may not be doing so when managing your dependencies. If not, why not?
The argument against making small, incremental updates is when there’s a lot of work to be done for each change, regardless of the size of the change (see maths addendum, if you’re into that). That’s the reason incremental product improvements weren’t sensible before automated testing and deployment, and it might be the reason many people still don’t keep their dependencies up-to-date. When there’s a high fixed cost for each change, the optimal strategy is to make fewer, larger changes.
But this is 2017 — we have the tools to do better. The fixed cost in dependency management comes from checking for updates, finding changelogs, and putting together a pull request; Dependabot does all three for you. And once those fixed costs go to zero, it makes sense for you to do more, smaller updates — i.e., a continuous improvement process.
Who are you, who are so wise in the ways of dependency management?
Hi! Grey and Harry here, the team behind Dependabot. Previously, we ran Product and Engineering at GoCardless, a payments company.
We’ve been managing dependencies for years, and slowly refined and automated our approach. At GoCardless, security and robustness were always our primary concern, and over time we found being on the latest version of our dependencies was best for both. When we did occasionally find bugs in new releases it was almost always our test suite that caught them, and we were able to contribute back to the maintainer.
The result of those years of improvement is Dependabot — a dependable bot who’ll do all the leg-work of keeping your dependencies up-to-date for you, but still leaves you in total control.
It’s relatively straightforward to solve for the optimal updating strategy, given an effort function f(x) where x is the number of versions being upgraded in one go. Total effort to upgrade by N versions, potentially over the course of many years, is then (N/x)f(x), and that’s what we want to minimise.
Assuming f(x) = K + g(x), where K is the fixed cost of making any change (i.e., context switching, finding a changelog, creating a pull request) and g(x) is super-linear (i.e., reviewing the change from 1.0 to 1.3 in one go is harder than reviewing the changes between each version incrementally) the optimal size of each update will be monotonically increasing in K.