Update: You don't need to think about manually managing dependencies. Automate it with Dependabot.
In this post, I'll explain how I approach this issue both at work, and in my open source software.
For me, I find it helpful to split upgrades into two categores: upgrades that
address security vulnerabilities, and upgrades that address deprecations, pull
in new useful features, or are just for my own sense of "being up to date".
There are several reasons that these are my two categories, but none are more
compelling than that they map to two useful npm/yarn commands:
security auditing of your dependencies, and checking if your dependencies are
out of date. Since my categories are based on these two commands, they work as
great tools to help me address both of these concerns independently.
npm audit and
yarn audit are commands that can be run inside any package
directory. They function by finding all the versions of all the packages that
your project uses, and checking with a server that stores security advisories
(descriptions of vulnerabilites, their severities, and which packages they
affect) to see if any are vulnerable. They will then show you a list of
publicly disclosed vulnerabilities of any packages you depend on (or any
packages that your dependencies depend on, all the way down), if any
vulnerabilites exist. Both also have flags to let you automatically fix some of
your vulnerabilities by having the package manager upgrade the non-breaking
changes for you.
Audits are run automatically when you install run
npm install or
that you're aware of any vulnerabilities as soon as possible and can push a fix
straight away. It's important to pay attention to the output of those commands,
so that you don't miss a critical dependency.
Another great thing about these features in the npm and yarn registry clients is that they show the severity of the vulnerabilities in your dependencies. You can use this feature to define your own process such that if a critical vulnerability pops up you deal with it immediately, but you can batch your low severity vulnerabilities together so that you don't find yourself constantly fixing low impact issues.
One critical hole in these commands is that they require a developer to manually run an installation, or an audit, to be seen. If you have a project that lays dormant because it's stable, and no feature work has been prioritized on it this quarter, you might not happen to run an audit for quite a while. You can address this issue with interval builds. Interval builds are scheduled jobs on your continuous integration/continuous delivery (CI/CD) infrastructure that build your project on a regular basis - hourly, daily, weekly, monthly, etc. If you configure your CI/CD to alert you when npm or yarn find a critical or high priority vulnerability in your code, and schedule regular builds to fit your schedule, you can always be on top of emergent vulnerabilities in your dependencies. Both Travis CI and Circle CI support scheduling interval builds, so make sure to investigate whether this is an appropriate solution for you and your team.
GitHub also provides security scanning for your projects. You can turn on security scanning in the settings tab of each of your projects. npm/yarn and GitHub seem to use different data sources for security advisories, and it's always advisable to get as much advice as possible!
It's important to keep on top of security vulnerabilities in your dependencies, but remember that this is not all you could and possibly should do to keep your code and your users safe and secure. Dependency auditing does not find security vulnerabilities in your code itself, so you should review your process for code security. Some suggestions include:
- Making sure to keep security in your code review checklist
- Static analysis solutions, like WhiteSource, or Snyk may help you automatically find vulnerabilities in your own code.
- If you're lucky enough to work at a company with a security team, you may be able to view their security resources, access training, request code reviews, or ask questions when you're unsure.
The second category of updates does not involve security. There are after all some other reasons to want to keep your packages up to date. They include:
- Wanting to use a new feature in a library that you depend on.
- Pulling in a bug fix.
- Complying with a company policy around dependency age.
- Upgrading from a deprecated or unsupported version of a package.
- Appeasing your own general sense of up-to-date-edness.
- Any other reason you can think of!
Both npm and yarn provide commands to list all outdated dependencies in your
npm outdated and
yarn outdated. These commands will show you
whether the change was a major, minor or patch change using semantic
versioning, so that you can know whether to expect an update to break your
build. You can then go through one by one updating each of these dependencies,
running your test suite to confirm that the changes didn't break anything, and
committing the change. I'd recommend running this every couple of days while
you're working on a specific codebase anyway. This way, you're always working
with reasonably up-to-date copies of your dependencies. If it takes me 30
minutes to address a breaking change in a dependency, I will save my commits in
a branch and open a ticket to address later, just to avoid having my day-to-day
work be derailed by dependency fixes.
That can be a tedious process, particularly if you have accrued responsibility for many projects with lots of dependencies. Luckily, there is a service called Greenkeeper, that when enabled on your repositories will continuously monitor releases on npm and automatically open a pull request on your project to update the version of the dependency that you need. If the build passes, Greenkeeper can even automatically merge the change, so that you don't even have to think about your dependency versions until Greenkeeper encounters a failing build. In that case, you will already have a branch on which to start addressing the issue.
What does my ideal process look like?
If you have access to dependency automation tools like Greenkeeper, you can forget about manually managing dependencies, and focus on shipping features. If a dependency change breaks your build, your tooling will let you know, and you can address it as part of your planned work.
If for some reason you do not have access to a dependency automation tool, here's an alternative manual approach that should reduce the amount of work you have to do.
If I were setting up an ideal system for tracking security upgrades and package updates in general, I'd have a daily CI run check for security vulnerabilities before I get in to work. If there are any high priority or critical vulnerabilities, I'd address them that day, but it can be ok to leave low or medium priority changes for a while.
For the outdated but not vulnerable packages, I'd have that job run about monthly, just to keep things up to date, and update the things that I need to update for the new features as I go. I would distribute the runs for each project around the month, so I don't end up having to spend an entire day each month running upgrades for all the projects.
Hope that helps!