The Hidden Fees of Software Dependencies—and Smart Ways to Pay Them

For decades, code reuse was heralded as a holy grail of software engineering. It was seen as a key productivity enabler, making developers able to focus their time on novel problems rather than reinventing wheels. But it comes at a cost, and especially so when relying on the code of others. You will have to give up simplicity, quality and control—to varying extents. Depending on your goals and circumstances, this price will not always be worth paying.

This post is the fourth and last in a series on dependency hell. To learn more about handling existing dependencies that cease to be useful—please have a look at the third post.

The Cost of a Dependency

There is no way of getting something for nothing. Adding code produced by other people to your code has plenty of implications on the shape and capabilities of the system you are developing. These implication can be summarized as giving up:

  1. Simplicity
    Most dependencies are created with a larger group of uses cases in mind than just yours. This means that there are features you don’t need, failure routines your code can never trigger, configuration options with no relevance to you, and so on. Bringing in such a dependency makes your application _more complicated_ than it would have been if you would have implemented the functionality yourself. This complexity will spill over to your system in many ways, such as by making it more complicated to develop, review, configure or use. While there often are ways of reducing the impact of this increase in complexity, such as by creating abstractions, having to manage complexity is also a kind of cost.
  2. Quality
    The additional features, options and routines of a dependency typically affects the quality of your system in multiple ways. It may take longer to build, respond slower when used, or take up more disk space. Dependencies can also negatively impact the security and robustness of your system. The more code there is, the more room there is for security holes and bugs to hide, which may lead to your users being hacked, having to deal with untimely crashes, or other system issues.
  3. Control
    As I have carefully pointed out in the previous posts of this series on dependency hell, software dependencies are often a reliance on _other people_ more than it is on other code. By taking on a dependency, you start to depend on these people making choices that keep it useful to you. You get the functionality you need, but you sacrifice direct influence over how it is realized. An important and common consequence of this is that you will not have in-depth knowledge about how the dependency is implemented, or how it affects the rest of your system. This lack of knowledge can make it harder to implement your system correctly, making bugs harder to anticipate and address.

Ways of Paying the Devil His Due

While the obvious way of avoiding a dependency is to implement it yourself, this is not always feasible. You might not have the competence, time, or other resources required. But, there is a middleground between rolling your own and relying on others:

  1. Prune an existing solution
    You take an existing solution, most likely open source, and then remove all parts of it not immediately useful to you. If the solution is not open source, or is written in a language you cannot use, you identify what parts of it matter to you and reimplement these. This saves you time because it saves you from technical decision-making, which often takes as much or more time than it does to write the code.
  2. Fork an existing solution
    If the issue isn’t necessarily about the current features of the dependency, but it being unmaintained or there being concerns about its future direction, you can take its source code and start maintaining it yourself. This assumes that the dependency source code is available to you, of course.

Both of these approaches leaves you with having to maintain the code, but gives you the freedom to make changes to it as you see fit. They trade a slower speed of development for a more simplicity, quality and control.

Also, be wary of the fine print of the licenses in any open source code you prune or fork. Nowadays, most open source projects allow you to make your prune or fork without open sourcing it, but there might be other restrictions. You may have to acknowledge the origins of your software in a way that your users can see, for example.

Making The Cost/Benefit Trade-Off

In the end, what strategy to use for taking on dependencies should be a matter of what you believe will strike the best balance between costs and benefits. Do you need to be done with a proof-of-concept as soon as physically possible? Take on dependencies like there is no tomorrow! Are you making a surgical robot? Perhaps it is best to implement everything from scratch. But your system is propably neither a throw-away prototype or a medical device.

While this should sound kind of obvious to you, allowing there to be a cost/benefit trade-off is far from the norm. Many companies have cultures thay weigh heavily towards rolling your own or depending on others, expressed via mantras such as “not invented here” or “move fast and break things”. I believe that we, as designers, architects and engineers, have a duty to guide our peers towards what will produce the best outcomes, which may mean that we have to challenge these kinds of norms.

Don’t let your dependencies hold you back. Stay out of dependency hell!

Posted in ,

Discover more from Systems and Society

Subscribe to get the latest posts sent to your email.

Leave a comment