Why I still don't use Yarn

By Evan Sangaline | June 12, 2017
Follow @sangaline Star

But Isn’t Yarn the Best Node Package Manager?

If you’re only comparing it to npm, then the answer is unequivocally yes. Yarn is generally much faster than npm and gives you deterministic builds by default, built-in integrity checking, license management tools, and a host of other goodies. Despite all of that, I still usually don’t use yarn.

I avoid yarn for one simple reason: disk space usage. I feel like a bit of a curmudgeon here, but I find it a little absurd that it can easily take 100 MB, or more, to store a project consisting of a couple hundred lines of JavaScript if you want to use modern tooling (e.g. Babel, Webpack, React). 100 MB here. 100 MB there. It really starts to add up when you’re creating a lot of small projects over a long period of time. Between the egregious use of disk space and nvm thinking that it’s OK to add a 10 second delay every time you open a terminal, working in the JavaScript ecosystem sometimes feels like having a friend crash at your house and then proceed to trash the place.

Maybe I’m a little extra-sensitive to this because… well, let’s just say that being low on disk space isn’t just a choice for me, it’s a lifestyle.

This is my life

Even if you don’t struggle with this issue as much as I do, wouldn’t you rather not throw away tens of gigabytes if you could help it?

It turns out that there’s another node package manager out there, called pnpm, which solves the disk space issue while simultaneously offering many of the same benefits that yarn does (e.g. integrity checking, first-class caching and offline installations, and being way faster than npm). Unlike yarn or npm, pnpm uses a clever combination of hard and symbolic links within the node_modules directory which point to a global package cache. This means that 1) a simple JavaScript project can occupy mere kilobytes- like it bloody well should- instead of hundreds of megabytes, and 2) that there’s no need to flatten the node_modules directory structure. I won’t get into that second point in more detail, but it has some serious advantages over yarn or npm.

It’s interesting to note that early incarnations of Yarn actually also used symbolic links to a central cache. The behavior was only changed because it caused issues with internal tooling at Facebook. The possibility of adding symbolic, or hard, links back into the project has been discussed at length and there are some valid reasons why not to. These issues don’t apply to everyone though. If you don’t use FAT32, don’t need tooling that watches inside of node_modules, and trust yourself to understand the implications of modifying the shared files inside node_modules, then pnpm should work fine for you (give or take a few edge cases).

The Intoli Monthly Newsletter

Enjoying this article? Sign up to get monthly updates on new content!

Node Package Manager Benchmarks

There are a handful of node package manager benchmarks floating around, but none of the ones that I could find really give a full comparison between npm, yarn, and pnpm. The official pnpm benchmarks don’t include a warm cache scenario and, realistically, that’s probably the most common use case. The official yarn benchmarks, on the other hand, cover a comprehensive set of scenarios, but they don’t include pnpm. Additionally, the yarn benchmarks are a bit outdated and don’t compare against the newer npm@5.

I was curious how yarn and pnpm really compared, so I put together a node-package-manager-benchmarks repository which covers the same scenarios as the yarn benchmarks, but also include pnpm and tests several additional project types. I’m including only the React Native project results here because they correspond most closely to the official yarn benchmarks. You can find a number of additional benchmarks over on GitHub.

The project was scaffolded using react-native-cli and the dependencies installed using each of the three package managers under eight distinct scenarios. The eight scenarios correspond to each permutation of these three binary options.

  1. Warm Cache - The package manager’s local cache already includes all of the project’s dependencies.
  2. node_modules - The node_modules subdirectory is already present and populated with all of the project’s dependencies.
  3. Lockfile/Shrinkwrap - The package-lock.json (npm), yarn.lock (yarn), or shrinkwrap.yaml (pnpm) file is present.

Each combination of scenario and package manager was benchmarked five times and the results were averaged to give more consistent numbers. Again, these different scenarios and the formatting of the results are closely modeled after the official yarn benchmarks.

React Native Benchmark Results

React Native dependency installation benchmarks for npm v5.0.3, yarn v0.24.5, and pnpm 0.69.3.

✘ Warm Cache ✘ node_modules ✘ Lockfile/Shrinkwrap
npm (51 seconds 389 milliseconds)
yarn (46 seconds 658 milliseconds)
pnpm (40 seconds 333 milliseconds)
✔ Warm Cache ✘ node_modules ✘ Lockfile/Shrinkwrap
npm (3 seconds 567 milliseconds)
yarn (928 milliseconds)
pnpm (1 second 276 milliseconds)
✘ Warm Cache ✘ node_modules ✔ Lockfile/Shrinkwrap
npm (4 seconds 665 milliseconds)
yarn (924 milliseconds)
pnpm (1 second 779 milliseconds)
✔ Warm Cache ✘ node_modules ✔ Lockfile/Shrinkwrap
npm (3 seconds 471 milliseconds)
yarn (922 milliseconds)
pnpm (1 second 233 milliseconds)
✘ Warm Cache ✔ node_modules ✘ Lockfile/Shrinkwrap
npm (3 seconds 855 milliseconds)
yarn (37 seconds 838 milliseconds)
pnpm (36 seconds 510 milliseconds)
✔ Warm Cache ✔ node_modules ✘ Lockfile/Shrinkwrap
npm (3 seconds 499 milliseconds)
yarn (926 milliseconds)
pnpm (1 second 260 milliseconds)
✘ Warm Cache ✔ node_modules ✔ Lockfile/Shrinkwrap
npm (3 seconds 475 milliseconds)
yarn (930 milliseconds)
pnpm (1 second 707 milliseconds)
✔ Warm Cache ✔ node_modules ✔ Lockfile/Shrinkwrap
npm (3 seconds 479 milliseconds)
yarn (939 milliseconds)
pnpm (1 second 214 milliseconds)

NPM vs Yarn vs PNPM Roundup

From the benchmarking results, we can see that pnpm is significantly faster than npm and yarn for the completely cold cache scenario. My guess is that this is because it doesn’t have to deal with flattening the directory structure within node_modules. It’s a tad bit slower than yarn in most of the other scenarios, but still much faster than npm. It is also notable that gap between npm and yarn is quite a bit narrower than it was in the official yarn benchmarks, apparently npm@5 has made a lot of progress compared to previous versions.

If I were just looking at the benchmarks, I would probably call yarn the winner because generally people will be working with a warm cache. For me personally, however, the drastically reduced disk space usage with pnpm is way more important than saving a few hundred milliseconds here and there. I’ll probably go with a copy-on-write filesystem on my next computer, at which point this distinction becomes far less important, but I’ll be sticking with pnpm until then.