We’ve all been there. Its time to introduce a new package / dependency to our code base, be it a HTTP request library, a logger or something else, and the question we ask ourselves “Should I wrap it?”
The main reason we ask this question is a hidden fear that maybe in the future we would like to change the underlying implementation to a different package. We, humans, like to keep our options open.
But in reality, by creating wrappers around your dependencies, you are actually narrowing down your options and limiting your self.
Lets say you use axios package and instead of using it directly, you decide to wrap it. Your wrapper will depend on axios and will expose some generic API that uses the internal functionality of axios.
You need to find the exact balance between what you want to expose and what you want to abstract. You don’t want to create a dumb proxy in front of axios because you want to keep your options open in case you decide to switch underlying functionality, so you try to create something abstract. And by doing so you encounter the first problem of wrappers:
Wrappers take a feature rich library and dumb it down a set of simple functions
You don’t have time to create a proper wrapper. You don’t know what functionality you might need from it, so you focus on wrapping what you need now. In the end, you are making a product for your clients, not developing a library. You don’t have time to correctly wrap the underlying dependency. So you expose a few methods, those you need right now, in some abstract way, type git commit and go to drink your coffee with a smile on your face.
Later on, when one of your team mates needs to develop a new feature, he will happily look at your axios wrapper, expecting it to contain a method he needs and will be disappointed to find out that this method is not there. Now instead of focusing on delivering value to your customers, your team mate will have to focus on creating a useless abstraction for a greater cause of “we might replace it in the future”, and this brings me to the second and biggest problem of wrappers:
Wrappers create the illusion of control. The main argument for creating a wrapper is that “we might change to a different underlying dependency in the future”, but guess what? No body, ever, changed from one logger to another; changed from postgres to mysql. And if they did, their wrapper probably caused them more troubles than help.
In programming there is a nice principle called YAGNI — You Aren’t Gonna Need That. YAGNI suggest that instead of implementing a bunch of abstractions with the hope that “one day I might change the underlying implementation” you actually focus on implementing what you need now. Don’t be afraid of refactoring — its a healthy process in software development. Focus on implementing real safety nets such as continuous automated unit and integration tests and look at type checking, either via statically typed languages or by using tools like TypeScript to help you with refactoring. Remember — wrappers create the illusion of safety, as they rarely help you with transition from one underlying library to another.
Donald Knuth once said
The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.
Wrapping your dependencies is a premature optimization for a dooms day when you decide to switch to a different logger. This day will either never come or if it does, your wrapper will not save you from tons of refactoring and fixing broken tests anyway. And in the mean time, you’ve wasted time on creating your useless wrapper but instead you’ve needed to focus on proper refactoring and tools to help you with them (such as type checking), proper automated unit and integration testing while in same type enjoying the full potential of a library that’s been co-created by 260 different contributors (https://github.com/axios/axios/graphs/contributors), but hey! It is sure better to roll your own, dull wrapper, ain’t it?
So stop creating wrappers. Instead focus on refactoring (by prioritizing it as tech debt in your development life cycle), focus on tests. Focus on creating middlewares to common functionality that can be quickly plugged to existing libraries, instead of wrapping those libraries. Focus on bringing value to your customers instead of reinventing the wheel over and over again.