Push and pull: when and why to update your dependencies
Your application likely depends on a variety of third-party packages. As time passes, those dependencies will change, for two reasons:
- Security fixes and critical bug fixes: you don’t want someone stealing your data, or your data getting corrupted.
- Software packages get new releases with new features, new APIs, and less critical but still useful bug fixes.
How do you balance the need for stability with the need to get updates?
First, we’ll discuss the two undesirable extremes: complete stability and complete instability of dependencies. Then, we’ll see how you can get stability in the short term coupled with the ability to adjust over time.
Note: Libraries require different update policies that I won’t discuss here.
You need stability, but not too much
There’s huge value in pinning your dependencies in order to get stable, reproducible builds. If your dependencies today are the same as your dependencies yesterday, anything that goes wrong is purely because of changes in your code. And while security updates and other critical bugfixes do require you to update dependencies, they’re also the easy case: typically maintainers will backport them to stable releases so you don’t have to change your code much.
Testing new dependencies and switching to new APIs has a cost. Yes, updating newer dependencies can get you the latest feature, optimization, or minor bugfix. But it also means you’re spending time on updating rather than on the work that actually produces value to your organization.
It can be tempting to never update anything except for security updates.
But the longer you put off updating your dependencies, the more risky and expensive it will be… and the more you’ll want to put it off. Until the day you can’t put if off any longer.
Eventually you will find yourself running on obsolete software that is no longer receiving security updates. At that point you will be forced to upgrade.
And since you haven’t updated anything for potentially years on end, you now have to update multiple major dependencies at once, possibly jumping ahead multiple releases. This is extra risky, because a lot is changing out from under you, and it’s extra expensive because figuring out where problems are coming from can be difficult if you’re changing every single dependency.
A better cadence for application dependency updates
Total instability is a bad idea, as is rigid stability. A better approach is to have two update mechanisms for your application: push and pull, each running separately and in parallel.
Security updates get pushed to you
Security updates and other critical bug fixes need to happen as soon as possible. So instead of relying on going out and looking for them, you should have notifications pushed to you.
If you’re on GitHub, it has had basic support for vulnerability alerts for a while, and has acquired Depandabot, which can automatically open PRs against your application with security fixes; it supports Pipenv, Poetry and pip-tools. If you’re on GitHub go to the Security tab of your project and enable Automated Security Fixes (really it’s automated security fix suggestions).
Other updates get pulled once a month
Once a month or two, you should try updating all your dependencies. Most of them won’t have changed at all, or will have only point releases, so this update will be low risk. Because you’re doing updates frequently, only a small number of major changes should have happened, so if there are problems you will be able to pinpoint the cause.
The specific time frame might vary based on your resources and domain—every six months might be fine too. But make sure you don’t put it off for too long or you’re back in the dangerous zone where upgrades will become too risky.
Note: I am presuming that you have either an extensive automated test suite or the resources to do sufficient manual testing. If you can’t do the necessary testing, you need to start working on that right now, a little at a time.
Start updating now
Here’s how you can start applying these policies today:
- If you don’t have pinned dependencies, go implement that first.
- If you don’t already have security notifications enabled for your application, set that up in GitHub or with a third-party service that works with your version control system.
- Finally, consider how you feel about updating your application’s dependencies. If that’s a scary prospect, you need to ensure you have sufficient tests to make it possible, and then start updating it on a regular basis. Waiting is just going to make it that much scarier.
Learn how to build fast, production-ready Docker images—read the rest of the Docker packaging guide for Python.
Production Docker packaging is too complicated to learn from Google searches
With as much as a dozen different intersecting technologies, and an unknown number of details to get right, Docker packaging isn't simple, especially for production.
But you still need fast builds that save you time, and security best practices that keep you safe.
Take the fast path to learning best practices, by using the Python on Docker Production Handbook.
Free ebook: Introduction to Dockerizing for Production
Learn a step-by-step iterative DevOps packaging process in this free mini-ebook. You'll learn what to prioritize, the decisions you need to make, and the ongoing organizational processes you need to start.
Plus, you'll join my newsletter and get weekly articles covering practical tools and techniques, from Docker packaging to Python best practices.