What is Dependency Hell and How to Avoid it?
The use of open source, third-party code has become a common practice in today’s software development. No wonder, given that it helps developers avoid coding from scratch, accelerating project work. That being said, the more components or libraries we use, the more software dependencies we also need to account for.
Table of contents
When you find yourself spending more time fixing dependency issues rather than writing code, chances are your code is overloaded and you’re in what is commonly referred to as ‘dependency hell’.
Outdated software dependencies have been shown (source) to cause failure on new builds up to 50% of the time, often requiring developers to start from scratch. Not only that, open source software now makes up 60-80% (source) of all applications’ code base, implying additional dependencies to manage.
This article will guide you through understanding software dependency, explaining what dependency hell is, how it happens, and how you can avoid it.
What is Software Dependency?
Software dependency is when you rely on external libraries to implement certain functionalities, instead of developing them from scratch. Dependency takes place regardless of the external library size – it can be as small as a single document, or as large as a set of packages. For instance, let’s assume that you’ve written an outbound sales platform and want to add a functionality that lets you track email open rates. Instead of coding this module yourself, you can turn to a third-party library. From that moment onward, your tool’s message open functionality depends on the external library’s developers.
Avoiding repetition by adding a dependency is advantageous for designing, writing, testing, examining, and maintaining a small piece of code. A package is the unit of code and is what we refer to in this post.
Software dependencies can be categorized into two types:
- The first type is direct dependencies, which are the libraries that your application directly references.
- The second type is made up of transitive dependencies, which are the libraries that your dependencies use. At the most basic level, they’re a dependency of a dependency.
Software dependencies are naturally a part of software development and are more often than not unavoidable. However, they do come with the risk of dependency hell, so let’s first understand why software dependency should be avoided.
Why Should Your Development Team Avoid Software Dependency?
Simply put, the more dependencies your software has, the more you have to manage. This raises security risks because some of the libraries you employ may be outdated. It can also be more difficult to discover reasons for bugs or other performance issues.
It’s important to keep in mind that most software was traditionally sold on a license basis, whereas nowadays most is rolled out using open-source initiatives. Some might say that there’s also less control over software quality, but this is open to discussion.
Software dependency can have a knock-on effect, negatively affecting your users, and your bottom line. Worst of all, as discussed above, it runs the risk of dependency hell. But what does this mean?
What Is Dependency Hell? Definiton
Dependency hell (otherwise known as JAR hell, or classpath hell) is a phrase used to describe the difficulties encountered by software developers, publishers, and users in general when one piece of software or a software package is dependent on another.
When software from a third party is used in conjunction with other programs or applications, dependency hell occurs if this software works abnormally, producing errors and bugs. Sourcegraph (source) has come up with a rather memorable way to describe the nine circles of dependency hell: limbo, lust, gluttony, greed, wrath, hearsay, violence, fraud, and treachery. It is these contributing factors to JAR hell that we will discuss next.
What Contributes to Dependency Hell?
In order to understand how to lessen the risk of dependency hell, it is vital to understand what causes it. Although the causes of dependency hell are varied, the most common reasons it happens are as follows:
The Library You Use Becomes Abandoned Over Time
This is a common scenario for open-source projects where issues aren’t fixed, and so your projects are affected. This is where code is developed and forgotten about, instead of being updated, or abandoned entirely. This leads to a state of code redundancy, which doesn’t help you or your users, and creates a big security threat.
The Library Code You Use Might be Written Badly
Often, the simplest or most obvious solutions yield the best results. And although excluding poor-quality libraries might sound like the simplest and most obvious answer, as a developer you understand that bad code may be unpleasant in all kinds of devious ways.
Maybe your dependency works well the majority of the time but randomly breaks in certain situations. Or, perhaps it’s a model for a straightforward API design, but its performance falls short when you require it in volume. Or maybe there are security flaws that have yet to be discovered.
While it’s true that libraries with no redeeming features aren’t likely to catch your attention, there are popular packages available with all of the problems listed above. So, before you implement any type of software, it’s worth checking via Google whether any developers have complained about the tool/library. If so, were the reported issues fixed? If so, how quickly?
After their initial zeal and burst of popularity, open-source maintainers may over time become weary. Your project then enters dormancy, where problems are left unresolved, and security vulnerabilities go unaddressed. Event stream processing (source) is just one example of a transfer of authority between maintainers that is rife with difficulties.
Documentation Might Not Exist Or Is Poorly Created
It’s one thing to produce excellent code. Still, telling others about the program’s functionality is a distinct expertise, which many solo or small-scale open-source maintainers lack. Even if they have that knowledge, they may only cover languages that they speak and frequently fall back on community translations to fill in the gaps.
Without proper documentation, it is difficult to improve and adapt these software dependencies. Your developers might be better off starting from scratch rather than trying to sift through and find information and code to make it work optimally.
The Bundle Size is Too Big
This is an issue that can be encountered both in front-end and back-end technologies (where modules that will never be used are uploaded into memory). Libraries that aren’t created with tree shaking in mind can excessively expand your bundle size when developing a front-end web application. You may only utilize a tiny portion of the library’s capabilities, but still pay for all of its code, slowing down your site for users.
For those unfamiliar with tree shaking, consider your application and its dependencies as a tree-like structure. Each dependency in the tree denotes a functionality that contributes to the operation of your app. These dependencies are brought in via static import statements and perform as a sort of dead code elimination in modern applications.
For more information about tree shaking, check out this web fundamental (source). Now, to really give you a feel for classpath hell, let’s take a look at a real-life example.
Real-Life Dependency Hell Example
We stumbled upon dependency issues while working on one of our products.
We used a PHP composer to look for a library that would help us resolve a specific problem.
We found a library that looked promising, but we soon noticed a worrying problem. We couldn’t install any new dependencies and the console was communicating lots of errors without any particular changes to the code.
When we took a look inside the package, we discovered a variety of other dependencies, such as FOSUserBundle, which requires Doctrine ORM locked in a version which has plenty of vulnerabilities. Additionally, it required some other minor packages with very questionable security policies.
The biggest problem came when the last of these minor packages was removed from the package manager. It almost ruined the whole feature and only the fact we had done extensive research a few weeks prior to this incident to define the weak points of our dependencies saved us because we were ready to swap this dependency with a previously prepared workaround.
Now you might be wondering what can be done to avoid dependency hell. Let’s dive right in.
Tips How to Avoid Dependency Hell
The good news is that despite dependencies being unavoidable in most situations, there are ways to mitigate the risk of potential dependency hell. Some actions that you can take include:
Specify Which Dependencies Should Be Prioritized Over Others
The first and most important step to reducing the number of software dependencies is deciding which libraries need to be updated first. One way to do this is to rank your dependencies according to how critical they are for the optimal functioning of your application. The less critical ones are the most important for you to get rid of.
Another way to reduce dependencies is to focus on those that present the highest data security risk. This includes open-source libraries, which expose your application, as well as your users, to a whole bunch of vulnerabilities.
Remove Unused Dependencies
Instead of piling on additional dependencies, try removing unused ones (First Circle) as your starting point. You can then use static code analysis tools to find out where your most important dependencies originate, and whether you can compress them or get rid of them altogether.
Another approach is to only import what you need from the code (e.g., no star (*) imports). This might not reduce your bundle size, but it will almost definitely free up your global namespace, reducing the chance of any collisions or conflicts.
Automate Software Dependency Updates
Software updates are an important part of staying up to date with enhancements that can improve performance, and even add new functionalities. By not staying up to date with these important refinements, you could fall into dependency hell, which could have been otherwise mitigated without much effort on your part.
Automation is a way of ensuring you benefit from crucial updates as soon as they become available.
Create (and Maintain) a Software Dependency Management Policy
To ensure that your developers follow all of the above, as well as any other important measures, make sure to establish clear dependency management guidelines. All of your developers, QA specialists, and security/DevOps personnel must follow the agreed regulations.
After all, the last thing you would want is any rules or methods slipping through the cracks if one of your software developers had to leave the company, for example.
On top of this, having a policy in place is crucial to monitor security, improve app performance, provide quality assurance, and maintain license compliance. Without a policy in place, you may miss out on some of these important software dependency risks. This could create vulnerability not only for your organization but users as well.
Software Dependency Hell – Final Thoughts
We hope this article has helped you understand how your business can become less vulnerable to software dependency, and therefore, mitigate the potential dependency hell that can follow on from that.
Although software dependency is often unavoidable, it’s important to remember that dependency hell is not. Be sure to follow this guide to minimize the risks and keep your software protected, for both your business and your users.
If you’re looking for an experienced tech partner, that will guide you through the process of software development, then reach out to us. We’d love to help.
Share this article: