Why and How You Should Automate Dependency Updates
Table of Contents
Dependency management, for most developers and software engineers, entails installing, updating, monitoring, and deleting dependencies that an application depends on. Java, PHP, JavaScript, and several other languages all have their own dependency management tools.
This article discusses dependencies, their importance, and how to perform dependency management.
What are dependencies?
The term “dependency” in the context of software engineering refers to the case in which one piece of software or program needs the assistance of another piece of software to add any functionality to an application or run smoothly. For instance, Program A needs the help of Program B to run correctly. As a result, A is dependent on B. Developers can also use these dependencies to add functionality to the application, like user fingerprinting or logging.
With the increase in demand for sophisticated functionality, applications are becoming more and more complex, resulting in an increase in the amount of code behind them. Instead of starting from scratch each time, developers use pre-written and tested code. These existing, tested services, or ‘libraries’ help save time and allow developers to create something new and better from the old.
According to the Linux Foundation, 70-90% of modern applications contain free and open source software (FOSS). However, these open source software dependencies may introduce security flaws (often unbeknownst to their authors) into the applications depending on them. The good news is that once a security vulnerability is patched, a newer, less vulnerable version of the dependency is usually released. Regularly updating your dependencies is a best practice to avoid vulnerabilities and the accumulation of technical debt, even (and some would say, even more so) when updates are minor.
In the event of an urgent security patch (zero-day vulnerability), developers that use the most recent version of the vulnerable dependency face a drastically lower risk of breaking their build. The risk of breaking the build comes as a result of the need to ‘skip’ multiple versions when updating to the dependency version that includes the security patch.
Dependencies are generally added to projects by “declaring” them. For example, NPM project dependencies are listed in a package.json file. Similarly, Java developers will have a pom.xml and Python developers may have requirements.txt. Any Node project’s package.json file is crucial. It saves critical metadata about a project before publishing it to NPM and defines functional properties that NPM uses to install dependencies, run scripts, and identify our package’s entry point.
Let’s say you designed a messaging app. To encrypt all the messages, you can depend on external packages coded by someone else for encryption rather than starting from scratch. This way, your messaging app has a dependency (the encryption package).
Why automate dependency updates?
The average Java library contains over 100 individual open source libraries. No matter how good your team is at keeping everything up to date or streamlining processes, manually updating every dependency is unfeasible. Many of them will have updates every day or week.
Using outdated dependencies or outputting code that has known vulnerabilities, exposes your application to threats. To run the application smoothly and with minimal vulnerabilities, keeping all your libraries up-to-date is vital. However, it needs to be done in a smart fashion. For example, when dependencies are declared as they are in the package.json file below, they may be upgraded automatically without running tests.
"dependencies": {
"dependency-name": "^version",
"dependency-name": "^version"
}
This isn’t good practice as it can easily break your build as soon as a new dependency version is released, as you haven’t had the chance to test it. To avoid breaking your code, dependencies should be pinned to a specific version and updated explicitly. Development teams often look for different tools to manage and update dependencies safely and efficiently.
Renovate for dependency management
Many different tools are available in the market for dependency updates, but most are hard to configure and sometimes don’t work as intended. Renovate is a popular open-source tool for dependency update automation. It is highly configurable, easy to install, and has many state-of-the-art features. For example, Renovate crowdsources tests and package adoption data which are used to create a ‘Merge Confidence’ score, making it easy to understand whether an upgrade is potentially dangerous or not. Also, Renovate includes the release notes and commit history in the PRs it creates so you know exactly what has changed.
Let’s discuss Renovate in detail.
Renovate enables automatic dependency updates and comes packed with a wide variety of capabilities. If an update is available for a dependency used within your project, it will create a pull request (PR) for developers to evaluate and verify the update. Each PR contains all the information required to make an easy update decision. If the PR meets a set of user-defined (including custom) conditions, Renovate can even automatically merge the PR without any human interaction. This makes managing dependencies much simpler for developers. For example, this code in the Renovate configuration file (renovate.json) will enable auto-merging for PRs that aim to update any dev dependencies.
{ "packageRules": [ { "matchDepTypes": ["devDependencies"], "automerge": true } ] }
You can also determine when the pull request for a dependency update should auto-merge by specifying conditions such as “if there is a minor update, then auto-merge”. You can quickly use this configuration for that:
{ "packageRules": [ { "matchUpdateTypes": ["minor"], "automerge": true } ] }
Many organizations like to review the commit before they are merged; hence you can also define that in the configuration file.
{ "reviewers": ["team:foo"] }
This will assign the PR to a team for review before the merge.
Renovate is pretty flexible, including the ability to create schedules for your PRs. You can set a schedule for the time during which Renovate will either execute or create pull requests. You can accomplish it by utilizing this configuration code.
{ "schedule": ["after 8pm and before 7am every weekday", "every weekend"] }
You can install Renovate as a Github App, or as a CLI tool via an NPM package or a Docker image. In addition, you can choose to Mend Renovate Community. There is no fee associated with either of these options, they are available to you free of charge.
Risk of using vulnerable or outdated dependencies
Open source dependencies increase your application’s functionality. However, the older a dependency is, the more likely it is to be vulnerable.
Our research found that over 90 percent of new vulnerabilities in NPM packages have security patches before the vulnerability is publicly disclosed. These newly disclosed vulnerabilities can be prevented by maintaining up-to-date dependencies.
Admittedly, some projects and websites use outdated versions of different libraries as the functionality that their application needs is only available in those. Nevertheless, the risks associated with using outdated and vulnerable dependencies generally outweigh the benefits.
Conclusion
Many applications rely on open-source dependencies. These dependencies contribute functionality to the application, but also have the potential to compromise not only the application itself but also the entire organization’s underlying infrastructure if they are not kept up to date. As a result, they need to be managed at a high priority level. Therefore, organizations must implement effective dependency management solutions like Renovate to ensure dependencies are always regularly updated.