Yarn 1 vs Yarn 2 vs NPM

Re-examining Infinite Red’s decision to use Yarn

Jamon Holmgren
Red Shift

--

At Infinite Red, we’ve been using Yarn for several years now — virtually since it was released. We even released an NPM vs Yarn cheat sheet that has been downloaded thousands of times!

Recently, Yarn 2 was announced, with PNP (plug ’n’ play) support, constraints, and many other improvements. The announcement brought about some controversy in the community due to how some of the technical decisions were made, and it also doesn’t (as of this writing) seem to support React Native. And, since we do a lot of React Native at Infinite Red, that raised some pretty important questions. Most notably:

Do we upgrade to Yarn 2, stay on Yarn 1, or move back to NPM?

So this morning I pulled our developers together in a Zoom call and we discussed it.

Totally a picture of us in that meeting

What’s important to us in a package manager?

We thought about what aspects of a package manager were important to us and came up with the following list. I’ve arranged them in a rough approximation of order of importance to us.

  1. Broad support — needs to work with React Native, Node CLIs, web — anything we do. We work with a number of clients over a range of technologies and having a package manager that can be used for all our JavaScript technologies is a must-have
  2. Speed — needs to be fast. Development is arduous and slow enough without having to wait for our tools!
  3. Predictability — will install the same packages every time. Having different versions on different machines makes debugging quite difficult
  4. Reliability — it’s not flaky nor does it fail randomly. Nothing seems to frustrate developers as fast as flaky tools
  5. Caching — local installs wherever possible. We all work remotely and so less network traffic/bandwidth is critical
  6. Community adoption — we are a consultancy and have to use things that are broadly adopted unless we have a clear reason not to (MobX-State-Tree vs Redux being a good example of this — Why Infinite Red uses MobX-State-Tree instead of Redux)
  7. Cost of change — needs to be manageable, both in terms of technical cost and also training the team and our open source contributors
  8. Key value-added features: outdated, interactive-upgrade

Note that we do not use Yarn’s workspaces feature, which is a big focus of Yarn 2. We have used Lerna in the past for monorepos (with varying degrees of success).

Broad support

Both NPM and Yarn have similar wide support for the technologies we work in. Supporting React Native is generally the biggest question mark with any new package manager we would consider.

But Yarn 2 (also called “Berry”, which I’ll call it from now on in this article) does not support React Native (at least yet) out of the box. This makes it a no-go for us. There is a plugin for Berry for opting back into the node_modules strategy, which in theory should support React Native. But this is untested with RN as of publication.

Winner: NPM or Yarn 1

Speed Tests

The biggest question mark of the above list of important aspects was speed. I was curious if NPM has caught up to Yarn 1 in terms of speed.

I wasn’t looking to do an exhaustively scientific speed test. I purposely kept the speed tests “dirty” to simulate real-world conditions— I took an existing app, removed the postInstall hooks, and otherwise left everything running on my 2019 MacBook Pro 16", including a screen share session on Zoom with my engineers. I ran each test at least twice.

I’ve included Berry performance out of curiosity, but again, it isn’t an option we are considering at this point.

In the below grid:

  1. “No cache” means I removed the global cache, node_modules, and lockfile before running the install command.
  2. “Cached” means I removed node_modules but left the global cache before running the install command.
  3. “Reinstall” means I left all of that in place and just ran the install command again and again.

I rounded the average runtime of all tests to the most reasonable significant digit.

                   No cache | Cached | Reinstall
NPM 6.13.4 67s 61s 28s
Yarn 1.21.1 57s 29s 1.2s
Berry 2.0.0-rc.27 145s 14s 6.5s
Node 13.5.0

Yarn 1 and NPM’s no-cache performance was similar enough, although Yarn 1 did beat it by 10 seconds. Yarn 1’s cached (but with no node_modules folder) performance was over twice as fast as NPM’s. But what really jumped off the screen was the reinstall — NPM spent 28 seconds to tell us that nothing needed to be updated, while Yarn’s response was almost immediate.

Berry’s cached performance was very impressive. It’s quite slow when the cache is cold, though.

It’s worth noting why Yarn 1 is so much faster at reinstalls. Yarn 1 “cheats” by not actually reinstalling at all if the “mtime” of the package.json and the node_modules/.yarn-metadata files are equal. If, for example, your node_modules contents got corrupted or removed (other than the .yarn-metadata file), Yarn 1 wouldn’t notice. Berry validates the cache integrity every time and NPM runs a full install comparison, so that’s why they’re slower.

However, in practice this hasn’t really been something that has bitten us. After all, if we seem to be having problems, removing the entire node_modules folder and reinstalling works fine.

Speed winner: Yarn 1 by a long shot.

Predictability, Reliability

I’ve seen some discussions online (here’s one example) that NPM still struggles with both of these things. Yarn 1 (anecdotally) does seem more reliable and predictable among our developers’ experience. However, we didn’t do any extensive testing on this.

Winner: maybe Yarn 1?

Caching

Both NPM and Yarn 1 have an offline cache. One of our developers is a digital nomad and said that he’s had much better luck with Yarn’s offline cache in terms of reducing bandwidth. I didn’t do any extensive testing here either.

Winner: maybe Yarn 1?

Community Adoption

NPM is the default for Node (generally installed alongside Node). However, within the React and React Native communities specifically, Yarn 1 seems to be the favorite. Facebook uses Yarn 1, so this influences the community a lot.

Winner: Yarn for React/RN, NPM for others

Cost of Change

We’re already on Yarn 1, so changing (for us) isn’t much either way. NPM is pretty straightforward to switch to, and Yarn 1 is close enough. One place this impacts is our open source libraries — for example, Ignite CLI will intelligently use Yarn or NPM depending on what you have installed, but it currently prefers Yarn. Additionally, our extensive Red Shift publication articles use Yarn at this point, and updating those would be non-trivial.

Berry, on the other hand, seems to have enough significant changes from Yarn 1 that it’s a non-trivial task to migrate to.

Winner: for us, Yarn 1. For non-Yarn 1 users, NPM. But not by much either way.

What about PNPM?

A notable omission from this article is PNPM. This is a super fast NPM alternative that uses hardlinks and symlinks to link one version of a package and then use it in multiple projects, which saves gigantic amounts of disk space and increases speed.

It does support React Native now, but it lacks in some key parts: community adoption being key among them. Because of this, we decided that we wouldn’t consider PNPM at this time. Perhaps we’ll reconsider that in the future.

So, what is Infinite Red going to choose?

Infinite Red is going to stay with Yarn 1 for the foreseeable future.

Here are the reasons:

  • It’s still much faster than NPM
  • It’s what the React & React Native community is mostly using
  • It’s what Facebook is using (and we use a lot of FB tech)
  • We haven’t had any major issues with it

We will not be moving to Yarn 2 (“Berry”) for the following reasons:

  • It doesn’t (currently?) support React Native
  • Facebook is not going to migrate to it and some of their engineers have been publicly critical of it, including the creator of Yarn 1
  • I have personal concerns about how decisions have been made so far. For example, while Prolog is a fine technical choice for the constraints feature (Prolog is exceptionally good at that type of task), it’s not a language that most JavaScript developers are familiar with. The community doesn’t seem to be supportive of this decision and should have had some input earlier in the process

With that said, I’m a firm believer that most decisions are temporary, and I’m happy to re-examine my assumptions in the future. After all, I wasn’t afraid to re-examine our earlier decision to move to Yarn. It’s part of my philosophy.

If you have any questions, comments, or just want to say hi, hit me up on Twitter!

A special thank you to Maël (creator of Berry) for reviewing this article for accuracy, as well as Daniel Madalitso Phiri, Gant Laborde, Bryan Stearns, Antonín J., Anthony Humphreys, Derek Greenberg, and Eddie Naff. Their help was invaluable. Any errors or omissions are my responsibility.

Jamon Holmgren is co-founder and CTO of Infinite Red, a mobile app/web design and dev company based in the Portland area & distributed across the USA. He lives in southwest Washington State with his wife and four kids. Follow him on Twitter for more JavaScript discussions, dad jokes, and random pictures of him in goalie gear.

  1. Video call image source: https://imgur.com/N9my3c1
  2. NPM’s official name is lowercase, as in npm, but I’ve used upper case NPM in this article for visual distinction

--

--

Co-founder & CTO @infinite_red. Lutheran, husband, dad to 4, React Native Radio podcast host, Twitch streamer, hockey goalie. Talking shop!