NPM packages: they ain’t free, you know
I was driving home from work the other day and saw something that took my breath away. I slammed on the brakes and slid gracefully to a halt, not for a moment taking my eyes of the prize: a box.
A box of foxes.
“Free to a good home.”
10 months later I’m here to tell you that foxes are never free. I’ll admit, a smarter man would have known that 7 was too many for a one-bedroom apartment. But I’m lonely and I like foxes, so here we are.
That very same day, I will have you believe, I had finished building a website. Nothing fancy, just a personal site with a few pages, some transitions, a little photo gallery and a list of all the prime numbers.
Do you remember what the world was like in August 2015? Node 4 hadn’t landed. Babel was version 5. React still started with a 0.
Fast forward to a few weeks ago and there I sat, ready to update my website to Node 6. No problem, I thought, just a version bump for Node and probably a few packages to update. I won’t go into the details, but as each package stopped working with an older version of some dependency and some packages wouldn’t work with the newest version of some other dependency, the list grew to 56 packages that needed a version change, not all to the latest one.
Ironically, the packages that caused me the most pain were the packages that, in hindsight, I didn’t really need. Which got me thinking about the ‘total cost of ownership’ of an npm package.
Below are what, in my opinion, are the hidden costs of packages that should get more attention, or at least form the basis of a discussion when your team is next npm shopping.
A lot of packages will save you time, no doubt about it. But you need to make some attempt to quantify that.
A while back I was looking for a package to handle logging. I spent a few hours to choose the right one, a few hours to read the docs, and a few hours going through github issues when I came across a bug. Then I threw it out and wrote one myself in the remaining hours of the day. And that little logger was exactly what we needed on the project.
As part of my website upgrade, I spent three hours going through the three upgrade guides required to update a 10-month-old react-router*. Looking back, I would have been much better off writing the 50-or-so lines of code required to serve up a few different routes, and have it work forever.
The point: when you’re about to pick a package to do something for you, have a think about how long it would take you to do it yourself. If the package isn’t offering you a significant reduction in time spent, think twice.
Fun fox fact: foxes have 42 teeth. 7 foxes have 294 teeth.
Here I’m talking about npm packages that you’re using because you don’t know how to use a particular technology. Let’s say you want to click-and-drag something. That’s kinda tricky, there’s a right way and a wrong way. But there are packages for that! They iron out browser and device inconsistencies and are generally a good idea.
But do it yourself anyway. Because then you will know how to do it — that has value.
Example two: you want to use the Web Audio API for the first time. If you find yourself typing “web audio npm” instead of “web audio mdn”, I don’t think you’re on the path to greatness.
The point: being good at things is good. If you’re picking a package because it does something you don’t know how to do, maybe today is the day that you learn something new.
Fun fox fact: it’s hard to tell when a fox is pregnant, but really easy to tell that she was pregnant when you now have 12 foxes.
If you’re using someone’s npm package without even looking at their open github issues**, you’re crazy. You want some foxes?
The point: packages can have bugs, they can simply not work, their authors can push updates that disobey semver, and you can’t foresee where or when. So with any package, you are taking on some small, unknown amount of risk that it will break your code base. Humans are bad at assessing risk with very low probabilities, but try anyway.
Fun fox fact: Red foxes scream in sets, with each scream lasting from 3 to ten seconds. Also, when you’re sleeping and a fox screams, it turns your dreams into nightmares.
The learning curve
I wrote a post recently about using icons in React. The end result was a component that took an icon name and returned an SVG icon. Pretty simple stuff. In the comments, someone asked why I wouldn’t use a particular icon library. I looked at the library: it used a higher order component and React’s context to be able to color the icon. If you jumped into a code base that used this, you would need to go and read their readme to see how to color an icon. Whereas if you just write the code yourself, it will be clearer how it works because there won’t be any black-box code that requires docs. Not a big deal, but multiply it by 50 packages each with their own vocabulary and you start to feel the pain.
As someone pointed out on reddit in a remarkably civil comment, this only applies to small things. Don’t go re-writing Angular or React and claiming it’s better because you don’t have docs.
The point: if a package can’t be used without referring to its help, it is hurting the learnability of your code base.
Fun fox fact: fox epilepsy is hereditary.
So… what do I suggest?
As you’re next typing npm install…, stop to think about the total cost of that package, over the lifespan of your project. For you and for your fellow developers. Spend a few minutes to compare that to how much effort it will save you today and maybe, just maybe, that the package is a freebie you can live without.
* I am certainly not knocking react-router. It’s a high quality package and all the updates have been useful improvements. But for my site I didn’t need it.
** Tip for windows users: search the github issues for ‘windows’. Same for ‘webpack’.