Yarn versus npm, what's the big deal?

By: (plus.google.com) +David Herron; Date: Tue Sep 19 2017 17:00:00 GMT-0700 (Pacific Daylight Time)

Tags: Node.JS

A couple years ago Yarn came onto the Node.js scene promising to be a better npm than npm. Apparently a lot of people have taken to Yarn, given the frequency with which I see instructions to install packages using Yarn. Since npm is installed along with Node.js, and since Yarn just uses the npm package repository, I'm left wondering what all the fuss is about Yarn. If it's just doing the same thing as npm then what is its reason to exist? What advantage does Yarn have over npm? Let's take a look.

Let's start by looking at the promises made on the Yarn website -- (yarnpkg.com) https://yarnpkg.com/en/

  • Ultra Fast - Yarn accomplishes this by caching every package it installs locally.
  • Mega Secure - Checksums verify the integrity of packages before installation, and before its code is executed.
  • Super Reliable - The yarn.lock file records precise exact package versions so that everyone on your team is in lock step and you don't have bugs from version creep.

At the time Yarn was introduced, back in 2016, npm was rather slow. I think the speed factor is the major selling point for Yarn. However the npm team have not taken the competition lightly. Over the last couple years npm's performance has improved.

According to npm, npm@5 (a.k.a. npm@latest) they've spent the last year and a half making some major improvements to npm. That time-frame predates Yarn's existence (a year-and-a-half before May 2017 is in late 2015), so npm did not make those improvements as a response to Yarn. Maybe. (blog.npmjs.org) http://blog.npmjs.org/post/161276872334/npm5-is-now-npmlatest

In any case the high level points of the npm@5 announcement is: a) It's fast, b) It's consistent (due to a lock file)

Further, npm@5 is now the default with Node.js v8 and beyond. This latest version of npm may be improved enough to warrant ignoring Yarn.

Yarn uses the same package.json format as used by npm. Yarn doesn't have its own package repository. Instead Yarn gets package information from the npm repository, and when a user publishes a package using Yarn the package is published to the npm package repository. (yarnpkg.com) https://yarnpkg.com/en/docs/publishing-a-package

Yarn is so similar to npm that they've published a migration guide showing the command line options are nearly identical: (yarnpkg.com) https://yarnpkg.com/en/docs/migrating-from-npm

Kicking the tires

Installation for me - a Mac OS X user who prefers MacPorts - is:

$ sudo port install yarn
Warning: port definitions are more than two weeks old, consider updating them by running 'port selfupdate'.
--->  Computing dependencies for yarn
--->  Fetching archive for yarn
--->  Attempting to fetch yarn-0.24.4_0.darwin_15.noarch.tbz2 from https://packages.macports.org/yarn
--->  Attempting to fetch yarn-0.24.4_0.darwin_15.noarch.tbz2.rmd160 from https://packages.macports.org/yarn
--->  Installing yarn @0.24.4_0
--->  Activating yarn @0.24.4_0

It installs an old version, Yarn 1.0 has been released. But that might be because my port definitions are more than two weeks old.

I'm also told this:

  yarn has the following notes:
    yarn stores data in:
    Should you choose to install packages globally with yarn (yarn global add), these files will not be removed if you deactivate or uninstall yarn. By default, these packages will
    be installed in ~/.config/yarn/global, and soft-linked to /opt/local/bin. To uninstall them all:
    $ ls -1 $HOME/.config/yarn/global/node_modules/  | xargs sudo yarn global remove
    You may then remove the directories listed above and uninstall yarn.
    yarn is meant to replace NPM, and may cause conflicts if you use both.
    You may override the default global installation directory by setting the PREFIX environment variable, e.g.
    mkdir -p $HOME/.config/yarn/bin # Or wherever you want
    export PREFIX=$HOME/.config/yarn/bin
    export PATH=$PREFIX:$PATH

That's easily accomplished with:

$ vi ~/.profile

Followed by starting a new shell, or else running those commands by hand.

I ran this in my home directory expecting to see a USAGE message:

$ yarn
yarn install v0.24.4
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
✨  Done in 0.18s.

But, instead, as you see, it did some installation. Not finding a lockfile it helpfully created a yarn.lock, and it also created an empty node_modules directory. Not exactly what I expected since my home directory is not a working project directory. That meant removing those two files to sanitize my home directory.

The command yarn help is the correct way to get the USAGE message. Sigh.

Running yarn in an actual project directory:

$ time yarn
yarn install v0.24.4
info No lockfile found.
warning green-transportation-info: No license field
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
warning Your current version of Yarn is out of date. The latest version is "1.0.2" while you're on "0.24.4".
✨  Done in 29.73s.

real	0m30.488s
user	0m17.598s
sys	0m8.736s

The yarn.lock is a YAML file listing precise version numbers as well as URL's from which to retrieve packages. Here's an example from the file:

akasharender@>=0.5, akasharender@>=0.5.14, akasharender@>=0.6:
  version "0.6.10"
  resolved "https://registry.yarnpkg.com/akasharender/-/akasharender-0.6.10.tgz#85e87d47e01fe9a471efc3e7975696fabf49c741"
    asciidoctor.js "1.5.6-preview.2"
    async "^1.5.2"
    cmnd "0.0.6"
    co "*"
    debug "^2.2.0"
    ejs "^2.4.1"
    flat-cache "^1.2.2"
    fs-extra ">=4.0.1"
    globfs ">=0.2"
    gray-matter "^2.x"
    less ">=2.7"
    markdown-it "^5.1.0"
    oembetter "*"
    rss "^1.2.1"

It's interesting that the package URL is via registry.yarnpkg.com. It would appear the Yarn team maintains a mirror of the main npm repository.

To compare:

$ time npm install
... lots of output

real	0m54.560s
user	0m31.978s
sys	0m9.448s
$ npm -v
$ which npm

Hm, that's quite a bit slower. This is an out-of-date version of Yarn versus an out-of-date version of npm, and npm was twice as slow (approximately).

Updating to the latest npm is (because I'm using nvm to manage Node.js versions) this simple:

$ nvm install 8
v8.5.0 is already installed.
Now using node v8.5.0 (npm v5.3.0)

Then rerun the test

$ rm -rf node_modules/
$ time npm install
... lots of output
added 402 packages in 359.632s

real	6m0.999s
user	0m25.469s
sys	0m9.469s

Um, really? Now it's 18x slower. There is a new file, package-lock.json, that is in JSON format and whose format is rather similar to the yarn.lock file.

There's also a new directory containing a bunch of packages:

$ ls -ld ~/.npm
drwxr-xr-x  1794 david  staff  60996 Sep 21 15:20 /Users/david/.npm

So let's re-run the test to see if the local cache will speed things up.

$ rm -rf node_modules/
$ time npm install
... lots of output
added 402 packages in 24.33s

real	0m25.170s
user	0m15.065s
sys	0m5.630s

Yup, that performance is on-par with Yarn's performance.


I see no compelling reason to switch to Yarn. Bottom line is that Yarn's primary purpose for existence was to be faster than npm, and npm has improved their performance considerably. I don't see any feature of Yarn that screams to me that I must use Yarn.

When Yarn was announced, the npm team responded by welcoming the competition. You'd think that since Yarn is piggybacking off the hard work npm, Inc. performs to set up and maintain and monitor and police the npm Registry, that npm, Inc. would be upset. You could see Yarn as a freeloader organization. But that would be to misunderstand the ethos of the open source ecosystem.

Instead, npm, Inc., took this as a challenge and took it upon themselves to improve their product. As a result npm is much better than it had been.