NPM best practices: Stop installing npm packages globally

There is almost no use in installing npm packages globally. Here's why

NPM best practices: Stop installing npm packages globally

A few years back when I started using npm, I didn’t mind installing many packages globally. Nowadays, I see almost no use in installing npm packages globally.

Let me tell you why globally installed packages aren’t that great and what you should do instead.

Issues with globally installed npm packages

In a previous article, I’ve explained why freezing the versions of node and npm is really useful for any project. As a matter of fact, freezing the versions of your project dependencies is even more important.

Often times though, people seem to only think about actual project dependencies as requiring a stable version. Utilities are often forgotten/neglected. Still, the different build/dev tools matter a lot for the stability and to limit the “it works on my machine” syndrome.

If the tools required to build/run your project aren’t part of your package.json file, then it means that you have to maintain separate documentation. First off, this creates friction for anyone joining the team. This alone is a good enough reason to list the build/dev dependencies as well. In addition, it’s easy to forget updating the documentation, leaving it out of sync and potentially inducing future time waste.

Another point is that globally installed packages are like installed applications. After some time, you tend to forget why they’re even there. If some of your projects require them, then you risk not being able to work on those projects anymore. If instead those packages are listed in the project’s dependencies, then it’s alright: do an install and you’re good to go.

Alternatives

For projects relying on npm, the simplest alternative is to add all of the tooling to the devDependencies of the project’s package.json file. That way, whenever you install the project, you also get all the tools that you need. If you do that, then using those tools in npm scripts is as natural as if they were installed globally, since the contents of node_modules/.bin are added to the “path” visible to scripts.

I’ll probably write an article about npm scripts one of these days, since I rely a lot on them. They help me introduce “shared commands” for the whole team. Thanks to devDependencies and npm scripts, every team member has access to the same build/tools/commands, which is really valuable. To me that’s like a more modern/user friendly Makefile ;-)

Another thing that you can do when you think about installing a package globally is to use npx instead. The npx command comes along with npm and allows to easily execute npm binaries.

npx works as follows: it first checks the current path (including the local node_modules/.bin folder) to find the command that you’re trying to invoke. If it finds it in “node_modules/.bin”, then it executes that (that means that if your build tool/cli is part of your dependencies, then npx will find it).

If npx doesn’t find the command in the current path (whether locally or globally / in the npm cache), then it will try to install it before executing it. npx usually does a good job at finding what to install. If it fails though, you can help it using the “--package” flag. Of course you can leverage bash aliases for this ;-)

Note that you can easily get rid of the wasted space generated by npx by clearing your npm cache.

TLDR;

devDependencies + npm scripts have tons of benefits for stability, limiting configuration drift, avoiding needless documentation, removing headaches and helping everyone share the same environment

npx can be used to avoid installing packages globally and you only need to clean the npm caches to remove the clutter

Conclusion

In this article, I’ve shared a few thoughts about why I almost never install npm packages globally. Answers may be different if you’re using yarn, but the general idea should be similar. Globally installed packages are like global variables: pollution! ;-)

That's it for today! ✨