Reading Time: 3 minutes
Anyone who has written software for long has in one way or another abstracted over library dependencies. Be it a small function wrapper or a full-fledged API over a dependency we all have noticed pros and cons in doing so. Based on each project’s nature, such a practice can be an overall asset to it, but sometimes it becomes a liability. In this article, I will try to mention the general benefits and problems I’ve found through the years when adopting abstracting practices over library dependencies.
Some libraries are not naturally friendly to testing but still provide important functionality which they sometimes monopolize, making them a necessary asset in a project’s dependency set. To enable unit testing, one has to abstract over the library in order to avoid code dependencies so as to be able to mock the library in order to test an aspect of the software that’s independent of the library’s functionality.
While minor version upgrades of dependencies don’t usually break the code, major version upgrades that include new features and library API restructure will break the build. Abstracting away the library interface and applying an extra layer on top of it, allows one to easily upgrade a library’s version by only changing the code layer that comes directly in contact with it.
There are certain cases, where there’s a need to select a specific library among a family of libraries that provide the same or similar functionality. Reasons for this could be dependency conflicts with other libraries, familiarity with it from previous development iterations and generally, reasons for such a choice always exists. Future requirements may change the project’s dependency needs and a new library of the same type must be included in the project. Abstracting away the library internals from the project’s source code can help swap the old library by only editing the code layer that interacts with it.
In a rapidly changing environment, libraries come and go. Year after year we see more small projects get initiated, get traction and then due to trends changing becoming obsolete and eventually unsupported. Being able to swap those libraries out guarantees the survival of the project.
By providing an abstraction layer between your code and the target library, you boost the team productivity in times of change. If a library needs to be replaced only the developer maintaining the abstraction layer has to learn the new library in order to adopt it into the project. The rest of the team, the developers working on the actual project code, keep using the same API.
You code one more layer which takes time, both to design it, to adapt it and to implement it. Especially if your project includes multiple libraries the work needed to create all the adapting layers is summed.
No universally acceptable API
Since there’s no API you’re implementing that’s globally acceptable, each developer is required to learn the documentation of your layer. That may introduce trouble recruiting since developers specialized on a specific framework or library won’t be able to use their expertise since they are required to code using your abstraction layer.
Extra documentation needed
Which means you will need to write that documentation. And maintain it. For every library. And each time it updates.
Bugs & Risk
An extra layer introduces extra risks as there’s one more point where things can break. Extra effort should be put on testing and debugging the middle layer you’ve created. Whenever new features are needed, those features have to be developed by the maintainer of the abstraction layer before the rest of the team can use the functionallity. This can delay the projects by a significant amount.
An extra layer introduces an extra method call at minimum, sometimes instantiation of extra objects which means a performance hit as more processing and memory resources are needed.
Each time a library upgrades, you have to adjust your API implementation to account for changes that may break your code. Imaging doing that for ten libraries.