Keeping clean

A week or so ago James Coglan tweeted this:

It reflects perfectly why I didn't get too much into Python other than writing isolated scripts that worked well with themselves and didn't require extra packages, because installing them was a pain and different in each system (compiling, package managers, other package managers, eggs, pip, bla, bla). And then there was the virtualenv solution, but that adds another layer of managing that I have to deal with. I just want to get things done and be able to distribute them in a manner that makes it easy for other people to use my software.

This reminded me that I had to spend a whole afternoon a while ago trying to make some brew-installed packages operate in harmony with other libraries/binaries in my system. It was so tedious and fuzzy I can't even remember what the problem was actually. Just the notion that installing all the software and making it all available into a global scope === bad, because different versions will require other different versions. And either everything works in harmony and life is beautiful, or you spent a rainy afternoon feeling miserable because of the weather and the incompatible binaries.

So it drives me crazy when I see the instructions for so many npm modules calling for npm install -g whatever. I will try to do all that is possible to avoid the dreaded -g switch, because bad things will happen at some point.

Let's start with the totally subjective reason that I don't want Random CLI Tool popping up in my bash autocompletion, and once the subjectiveness has been established...

These tools are a pain to install in many systems because sometimes the places where npm installs global packages are protected directories (e.g. in Linux). Other times they are protected due to having been wrongly installed from the beginning, so for example the /usr/local directory becomes "tainted" with the need for sudo. You can fix this by changing npm's global directory to somewhere else, or by changing the permissions of those folders to just be your permissions--specially if you're the only user, which happens to be the case for 99.99% of the cases.

These tools are also a danger because people mindlessly advocate for The Sudo Way, or what I call the chmod -R 777 * way. If you ignored my above suggestion to properly configure your system to avoid using sudo when installing packages globally because you trust some tutorial on the Internet more than you trust me, you're granting admin access to those node modules. You're installing software that can do very powerful things including all you can do PLUS executing other software with admin privileges, and you're in short, giving it the keys to your kingdom.

And that's for the being-a-user side of things, i.e. for defending yourself against globally installed packages.

When it comes to the developer of those packages, I have some advice too:

First consider splitting your tool in two parts: cli side and library side. Let people use your tool from their build scripts (as the tools I often find that need installing globally are build tools) instead of having to call shell.exec or something to launch your tool--or even worse, asking people to run it manually. What is this, the 60s? Automate everything!

You should also inform people they can run your tool locally, and perhaps you should encourage it too. Once you install a module locally with npm install, its binaries can be in ./node_modules/.bin. So for example you could run


Brian Brennan gave a fantastic suggestion: adding ./node_modules/.bin to your $PATH variable. Then you can run locally installed utilities as long as you're in the top directory of a project. For example, the above mentioned nichetool utility could be just called as this:


This will allow people to run different versions of utilities with all their particular dependencies simultaneously. SIMULTANEOUSLY. No need to set up virtualenvs, just cd'ing to a different directory. I think it is a truly beautiful feature of npm, despiste all the criticism that it "wastes space" because of all the copies of libraries in the node_modules folder. Honestly I'd rather waste space than time sorting out dependency clashes. I can always buy more space, but I can't buy more time.

In general, just try to introduce the minimum friction and disruption to existing toolsets and environments with your software. I consider globally installed packages as gross as global variables. They pollute your namespace and will be bringing bad news at any point. So as with global variables, don't. Just don't.