Managing dependencies is not for the faint of heart. For a single project, you may be able to keep up with dependencies on your own. For software codebases with hundreds of modules, however, even the most seasoned developer will quickly descend into dependency hell.
Don’t worry: dependency hell has happened to the best of us! There are some things you can do to keep yourself sane and improve application security. Read on to learn more about challenges, best practices, and good strategies for dependency management, and discover our three favorite tips.
First, let’s define some of the basic terminology.
A software dependency is an external standalone library that can be as small as a single file or as big as multiple files and folders organized into packages to perform a specific task. For example, if you created a messaging app and wanted to encrypt your messages, you could use an external package created by someone else for the encryption. Your messaging app now has a dependency — the encryption package — that it needs to run properly.
There are two types of dependencies:
From: NPM Dependency Network – Graph Commons
Dependency management is a technique for identifying, resolving, and patching dependencies in your application’s codebase.
A dependency manager is a software module that helps integrate external libraries or packages into your larger application stack.
When it comes to dependency management, open source software has made things more complex. Built on the foundation of sharing and reusing code, open source software now accounts for 60-80 percent of all applications’ code base. Sometimes this reaches 90 percent, which means more dependencies to manage.
So why do you need visibility into your dependencies anyway? Outdated dependencies can impact your software application in a number of ways:
Dependency management can be tricky. Perhaps the three most significant problems that you’ll face are:
Sometimes, more than one software package needs to use the same dependency, but each of them requires a different version of that dependency. The versions may not always be compatible, and the risk exists that when you solve the dependency for one piece of software, you’ll break the compatibility of another.
Updated versions of existing software and components are intended to improve the performance of that software and fix bugs, so patching software with new versions is important. Versioning also allows development teams to keep track of changes they make to the project code. However, issues can arise when:
It’s therefore essential that every component and dependency gets scanned to ensure that versions used in each particular instance are secure.
Managing Dependencies Across Multiple Environments
Similar to the versioning issue, dependencies can behave differently when used in different environments and when they are put together with other components. Like cogs, they have to fit together properly, otherwise, the whole mechanism doesn’t work. Mismanaged dependencies can therefore disrupt or disable entire projects. To avoid this, you need to do the following:
There’s a mind-boggling amount of pre-written code you can use, so it’s important to choose the dependencies that work best in your project and pose the least risk. To help you choose, check the following:
Many projects use semantic versioning (SemVer) to keep track of changes in third-party software or packages they are using. The semantic versioning specification was originally authored by Tom Preston-Werner, inventor of Gravatar and cofounder of GitHub.
SemVer gives software or packages a unique name and/or number, thereby helping you identify whether your components and their dependencies are up to date and are using the latest, most secure patch. It follows a relatively simple and logical set of naming and numbering protocols that delineate if a change is major, minor, a patch, in pre-release or build, or is a change for development purposes.
SemVer is popular for several reasons. First, it’s quite simple to help you keep track of changes, and it indicates what kind of change happened and at what point in development. SemVer also gauges update risk based on whether a change is major, minor or patch; most importantly, it helps developers avoid dependency hell by making it easier for them to resolve version conflicts and know what versions are acceptable to use.
For these reasons, we recommend that you apply a versioning protocol such as SemVer to your dependency management practices.
We’re all familiar with the practice of installing software updates to enable software to run more smoothly, ensure that it’s compatible with new platforms or new versions of existing systems, and enable it to run more advanced or refined features. The same applies to components and dependencies that are used in the software development pipeline (SDLC).
It is important to make sure that regular updates to components and dependencies address any flaws and weaknesses that have come to light. Attackers are always seeking vulnerabilities, or ways to exploit vulnerabilities, and you can be sure that if you’re using a popular component or dependency, the bad guys are working on ways to compromise it. Updates constantly counter these efforts to undermine your software, so by applying them regularly when they become available, developers and DevOps teams can ensure that they keep their dependencies secure.
This means it’s necessary to constantly scan for vulnerabilities, then identify and remediate them. It’s simply not enough to implement a round of due diligence either during the development process or at its end before you ship software. Regular maintenance is necessary to ensure that every dependency you use is secured and updated.
Implementing maintenance on an ongoing basis isn’t easy, because the volume of software and applications being developed is continually growing, which results in a rising volume of usage and an increasingly complex network of interrelationships. Manual dependency management in this environment simply can’t cope with the size of the task. For the process to be comprehensive, fast, and effective, dependency management must be automated.
Simple inertia is the main reason companies don’t actively update their dependencies. Your software is working fine, so it feels as if there’s little incentive to update it. With so many other priorities, dependency management often gets ignored.
Another reason outdated dependencies aren’t updated is due to a fear of breaking the build. Dependency management is hard, and applications depend on many libraries. You might have long chains of transitive dependencies or even circular dependencies. Updating one dependency may break another one further down the chain.
There are three primary approaches to dependency management:
Keeping your dependencies up to date is important, but this is far easier said than done. The really hard part of managing your dependencies is understanding which dependencies are vulnerable to security threats and which updates won’t break your code. With so many direct and transitive dependencies, this can be a challenge, but we have some tips to help you manage this process:
1. Prioritize. Some dependencies are more important than others, so it is important to be able to prioritize them, particularly when it comes to vulnerabilities. You need to understand exactly which open source vulnerabilities are being accessed by your code and which vulnerabilities aren’t so that you can update your most critical dependencies first.
2. Automate. Maintaining your dependencies can be extremely time sensitive when it comes to vulnerabilities and bug fixes. You can save time and reduce your exposure by automating dependency updates in your software projects and have your dependencies updated when new versions are released.
3. Establish policies. Establishing a clear policy up front about open source usage and dependency management helps prevent headaches later in development when it is more costly to resolve them. Your policies act as a playbook by telling your development and security teams how to handle these threats in your open source components. Without policies to give clear guidance, managing dependencies efficiently tends to be extremely hard if not close to impossible.
Now that you understand the challenges that come with dependency management, you probably have a lot of questions. The answer you’re searching for is dependency update automation. Dependency update automation helps you stay current and requires less time than an ad-hoc approach. It also gives you a number of advanced features like sorting by dependency or patch type and can even auto-merge pull requests if tests pass.
With data breaches more common and compliance top of mind, developers are more concerned about dependency management than ever before. You can stay out of dependency Hell by creating a clear plan that prioritizes dependencies with vulnerabilities and handles updates automatically.
Developers and DevOps personnel have powerful new tools for dependency management available to them, which will help usher them to dependency Heaven.
Poor dependency management can lead to dependency hell, caused by confusion and conflict between dependencies, circular dependencies, and versioning issues that can adversely affect your software, your applications, and your software development pipeline. Without good dependency management, the performance and quality of your software and applications could degrade, and your development pipeline could be impeded. Furthermore, neglecting to manage and update your dependencies could compromise application security by exposing dependencies to vulnerabilities emerging from the use of outdated versions, and the ingestion of malicious packages that can exploit these vulnerabilities. Finally, poor dependency management could prove costly if your software fails, your security is breached, and if your dependency use becomes no longer compliant.
You could decide to update dependencies before every release, or more often, before every pull request. Most importantly, you should check regularly and frequently. Ideally, automate the process, then you can set the frequency and conditions of updates without impeding the speed of your development pipeline. Consider monthly or even weekly updates to maximize security, assure compliance and optimize performance.
Regularly review and update dependencies. Components change and versions are updated all the time. You need to stay on top of them constantly. Apply a versioning protocol like semantic versioning to your dependency management practices. It will help you keep track of changes. It makes it easier to resolve version conflicts and know what versions are acceptable to use.
You can choose one or more dependency managers for different parts of your project if you prefer, but be mindful of the possibility of anomalies arising, or of findings falling between your dependency management tools.