An argument against abstraction

Harsh S Kulshrestha
Debuide

--

Photo by Fakurian Design on Unsplash

Don’t get me wrong there. But I recently came across a situation where I concluded that the very principle for which I appreciated a library, was the sole reason it gave me a lot of trouble.

I love pre-made components. Things like an npm or a python package. They save time, have been tested thoroughly by thousands of developers, and are always open for extension. Not to forget, most of them, rather almost all are open source or licensed under MIT. Love the community!

But it’s all good as long as the package works in our favour. The moment you want to make a change to the existing implementation, you’re bound to dive into the bundle which is fun in its own sense. While the use case is legit, sometimes it’s not really necessary to deep dive into the package, had things been implemented differently.

One such example is a package that injects a third party script on its own. Let’s assume the intent of the package is to show the most trending places by genre. As is obvious, it’s likely that the package uses google places api to show such results. Now assume you use a totally different component package to show these places on a map. Easy, right? All we have to do is pass in the result from the first component to the second one.

Yep! It should be that easy. Except, I would not be writing this post if it was the case. There’s one catch. Both these components require a common third party script/dependency to work. In this particular case it would be a google script/library that allows both the components to leverage the power of google’s SDK.

A good component is one that works out of the box. I, as a developer would bundle this library as part of my component. So might any other developer. But here comes the main issue — when you inject the same script multiple times in a page, it might introduce unexpected errors. Remember, it might, not always. And it’s expected. There might be a clash of functions, classes, variables, or everything might simply be overridden. It’s too complex to even worry about. As a safety net, one must always ensure to inject such scripts once and only once!

But the limitation is that we have leverage external components. And each component is bundled with the logic to inject the required library. Hence one would expect errors at any place these components are used in conjunction with one another. Hence this introduces issues at the cost of abstracting the components or creating “intelligent” components.

What’s the way out in such cases? Multiple:
1. We patch the component to not inject the library on its own. This is not so straightforward as in such cases you never know how many wires are attached to the injection.
2. We use our own custom component. This gives immense flexibility but eats up a lot of valuable time and effort.
3. We use hooks to un-inject the libraries injected by these components and then re-inject it once on our own. This is super hacky! NOT recommended.
4. We raise a feature request to the author, or create a PR. Recommended! But you never know the timeline.

I feel a good way to design such components would be to expose the callback that gets called upon when the library required by such components is loaded. This way we can inject the library on our own without relying on these components and call their callbacks as soon as its loaded.

This post isn’t meant to question the implementation of the component. To be honest, even I would have designed the components in a similar way that the authors have. And I’m quite grateful to them for releasing such amazing components. I just feel that abstracting the component too much may create barriers that we might not see upfront.
Side note: We could use DI to inject the dependent library rather than having the components load it on their own.

Cheers!

--

--