Design: Dependencies

When third-party should be a second choice?

Jérôme Beau
4 min readJun 25, 2020
A pile of big “modern” dependencies depend itself on a small module written years ago by random person.
Beware of the dependencies of your dependencies (XKCD drawing).

More and more reusable packages are emphasizing that they are “lightweight”, often because they don’t embed other reusable packages themselves.

This seems paradoxical, as promoting reusability (“use my package”) and refusing to apply it (“I don’t use other packages”) at the same time. So is it some sort of “Not Invented Here” (NIH) syndrome or just a more rational, sparse choice of dependencies?

When to use them

There are several reasons to use third-party software:

  • lack of competency: you don’t know how to do it ;
  • lack of resources: you don’t have the time or the workforce to do it ;
  • reusability common sense: somebody seems to have the solution to your problem, so why not using it? This will save you time (which is critical in professional environments) and will probably warrant some good quality provided it has already been tested by many users.
  • adoption: if a library or framework is widely used (and provided it brings real and substantial added value) it might save you training time to choose it, as other developers of your team will more likely know it already (or will be less reluctant to invest in learning it, conversely): APIs and patterns will be well-known, so your team will speak the same language quite quickly.
  • reliability: Even if publishing a public package is (hopefully) allowed to anyone, packages authors use to be experimented developers who take the responsibility of embedding their software in others’ code as serious as it should. As a corollary of adoption, the more a package is used, the more it is battlefield-tested and, if a majority of its issues are solved, you usually can bet on its quality.

When not to use them

There are also contexts where you should think twice before using third-parties:

  • over-constraining: Sometimes using some libraries or frameworks implies things you don’t want. Sometimes this can be workarounded, but the cost of the workaround must be considered. And this have to be summed up with all the workarounds of all other libraries you use. For instance, if you spent most code to redefine/customize Bootstrap styles that writing your own style, it might be more profitable to learn CSS.
  • monolithism: The share of the library you’re using is only a tiny part of the library itself. Maybe you would benefit from using only the code you need. For instance, don’t use Moment.js to get a pretty print of the current date.
  • fast evolution pace: If the library or framework you choose has strong competitors, or if the addressed domain is nascent, it is more likely that the third-party of your choice might be superseded by another one. So either you will keep on using outdated software, or your investment in any proprietary APIs might be lost.
  • specific critical requirements: It may sound obvious that nobody is likely to use a third-party software that doesn’t meet its project requirements, but this is more a matter of mid-term and long-term projection.
    Some software can meet your first requirements but not others that you know will occur. So building a custom solution might be redundant at the beginning, but might pay off on the long run. For instance you might be satisfied by Matomo/Piwik for your current analytics, but this might not be a good basis for the extended ones you know will come in the coming year.

How to achieve flexibility

Different jack plugs
Each dependency implies coupling with a specific contract. How to limit such coupling?

Now how to avoid being slave of dependencies? As David Wheeler said:

All problems in computer science can be solved by another level of indirection.

A clue that this is a good option is that it is the way standards work: they define common (standardized) APIs, and allow several implementations of it. For instance you may want to issue your logs using vendor-neutral OpenTracing API.

Standards may not be available for all your needs though, and you may find risky to make your code depend on one single third-party product. To avoid this, just adapt that third-party API to your own API.

That will allow you to:

  • change the product behind your API. All you’ll have to do would be to write a new adapter implementation, but none of the zillions calls to your API.
  • know better your needs as your API will contain only what your app needs and probably not the whole offering of the third-party product. Thus you might be able to better know what is underused or not and if that product could be easily replaced or not by a better or lighter solution.
    For instance by wrapping a Moment.js behind your TimeUtils.js API, you might discover that you could replace that product by a simpler and lighter one, possibly even your own custom code.
  • de-couple your need from the product offering: When you’re using a product, you also might be limited by this product. What if you need a service that is not provided by your logging product? By hiding that product behind a facade, you are free to aggregate a set of products (or custom solution) to provide the facade contract. For instance you might want your logging API to send PagerDuty messages in case of critical errors, as a supplementary task to the usual logging operation.

Conclusion

Integrating a third-party product might be a good or bad choice depending on the coupling it induces and of how the product offering (over)fits you needs.

In any case, to avoid being the slave of your dependencies, it is always a good idea to wrap them through facades that express your needs.

--

--

Jérôme Beau

Software engineer for three decades, I would like to share my memory. https://javarome.com