Keeping dependencies up to date

If you work with JavaScript or TypeScript today, you have a package.json with all your dependencies in it, and the same is true for the JVM with build.gradle. In fact, every framework has this package management system, and you can easily use it to keep your dependencies up to date.

In my role, every time I add a new feature or fix a bug, I update those dependencies to keep the system alive. This pattern originates from my belief that part of being a good programmer means following the Boy Scout Rule. I was recently asked if I believe that these dependency upgrades are risky and whether we should batch them up and do them later, since that would make code reviews smaller and our code less likely to break from a dependency change.

I disagreed—but saying "the Boy Scout Rule" isn’t enough of a reason to disagree. That’s just a way of working. The reasons I disagreed are:


Versions & Volume

All dependency version upgrades carry the risk of failure—meaning they break our code in unexpected ways. There’s a standard that minor version changes should be safe to upgrade, which is why I often do them all at once with minimal checks. Major version changes, however, require more care and understanding.

Major changes typically happen naturally. That’s because a major version update is how dependency developers signal, "Hey, there’s something you should know." But relying on "major vs. minor" as hard rules is flawed. These distinctions are more like guidelines—signposts for how to approach the situation. Much like when you drive a car, a change in road speed is a signal to adjust your caution accordingly.

For example, even type version changes and the volume of changes aren’t absolute factors. Let me illustrate this with last week’s experience: I performed two minor version updates on a backend system as part of a normal feature addition. It broke the testing tools because one of the dependencies had a breaking change—a minor version with a breaking change. This was human error on the part of the dependency’s developer: they released a minor version but didn’t mark it as a major change. That mistake impacted how I approached the updates—and it always increases the chance of issues.

Software is built by humans. Humans, not versions, will always be the source of errors.


Risk & Reward

I do like the word "risk" when discussing whether to update, because risk never exists in a vacuum—it always comes with a reward. How often have you heard people say, "Updating is too risky"—focusing solely on the chance of something breaking—without mentioning the reward of updating?

Stability is not a reward; stability is what customers expect as the baseline.

When we do update, we gain code that performs better, costs less to maintain, and is more secure. The discussion isn’t "What will break?" but "Why wouldn’t we want faster, safer, and cheaper code?"

I’ve inherited code from a team that never updated dependencies. It’s packed with outdated versions—a high chance of breaking as we start upgrading. But contrast that with projects my team builds, where we update dependencies every time we make a change. We’re only ever dealing with one or two small updates at a time. When issues appear, they’re easy to spot and fix.

Death, taxes, and having to update your code. As a developer, the only way to avoid updating your code is to pass it to someone else or change teams. Eventually, you’ll need to upgrade—and doing it often, in small batches, is cheaper and easier for you.

Using the backend example again: I had only two small changes to dependencies, so my issue was isolated to one of them. I could quickly check both and found the release notes for one within 15 minutes—clearly documenting the logic change. That let me adjust the code and keep us on the new version. If I had 100 changes? I would’ve rolled everything back and gone to lunch, and future me would have hated past me for it.


Architects & Gardeners

Our job isn’t to build a stable monument and abandon it. I believe deeply in DevOps, and thus in the truth that software is evolutionary—it needs care.

We are gardeners of living software, not architects of software towers. In our world, when things stop… they’re dead.

Maintenance and fixing what breaks is core to our belief that the best way to deliver value is through living software.