Yarn: Lock It in for Deterministic Dependency Resolution

Listen to this article

Choices are an important part of a healthy open source software community. That’s why we’re excited about Yarn, a new package manager that addresses many of the problems with Node’s default package manager, npm. While npm has done a fantastic job creating a large and vibrant JavaScript ecosystem, I want to share why Yarn is an important addition to the Node.js ecosystem, how it will improve your Node.js development experience, and how Heroku has incorporated it into the build process for your Heroku apps.

We began testing Yarn almost immediately after it was released, and began fully supporting it on December 16.

Yarn was released in October 2016 and made a big splash immediately. And while it came out of Facebook, Yarn is a true open source project: it has a BSD license, clear contribution guidelines and code of conduct. Big changes to Yarn are managed through a public RFC process.

A few popular projects have switched to Yarn. Ruby on Rails in version 5.1 switched to using Yarn by default. JHipster, a Java web app generator that incorporates Spring Boot, Angular, and Yeoman, has switched to Yarn too.

Yarn improves several aspects of dependency management for Node developers. It pulls packages from the npm registry (via a proxy), which gives you access to the same huge selection of packages available via npm. But Yarn gives you three big benefits:

Predictable installs

Security as a core value

Performance

Let’s look at each in more detail.

Adding a new dependency to a Node project can sometimes be a torturous task. It has become far too common for Node developers to resort to rm -rf node_modules && npm install after encountering errors adding a new dependency to a project. You can kiss those days goodbye with Yarn. An explicit design goal of Yarn is deterministic dependency resolution.

What is deterministic dependency resolution? Think of a pure function. If the same inputs go into a pure function, the same outputs always come out. Dependency resolution should work the same way. If the same dependency list goes in, the same node_modules folder should come out.

We want to make every deploy on Heroku a low risk event. It should be a fast and common part of your everyday development workflow. We also want to encourage dev / prod parity—the code that runs in your development environment should be exactly what runs in production.

Running npm install on two different machines can result in different dependencies being installed on each machine. These two machines could be two developer machines. Or, much more problematic, they could be a development machine and a production machine. The crux of this is that the dependency tree npm generates is determined by the order in which packages are installed. For more detail on this, I suggest you read this page from npm’s documentation.

Yarn’s lockfile is the key component making its dependency resolution deterministic. Using the lockfile, Yarn will generate the same dependency tree regardless of install order. This means it’s important that you commit your yarn.lock file to source control.

How do you know every time you or your deploy process runs npm install you’re getting the same code you got the first time? With npm, you don’t. For example, you might have a faulty cache or there might be an issue with the proxy you’re using. npm will install that different code and not provide a warning or error.

Yarn inspects the integrity of each package every time it is installed. It calculates a checksum to ensure you always get the same bits for a specified version of a package. If one line of code changes in left-pad v1.1.2, Yarn’s next install of left-pad will fail.

For most Node projects, Yarn will download and install dependencies faster than npm. Note that I said most projects, not all projects. You can see Yarn’s own measurements here, or you can check out the various 3rd party comparisons or do some tests yourself.

While build predictability and security are most important to us, fast dependency installation provides some great benefits. We want your deploys on Heroku to be fast -- regardless of whether you’re deploying a Review App, staging app, or production app. But even on your local development machine, Yarn will get you faster initial project setup and faster addition or upgrade of dependencies. It means you can more easily stay undistracted in your flow state.

Whereas npm downloads dependencies sequentially, Yarn downloads, compiles if necessary, and installs multiple dependencies in parallel. Most computers and network connections these days are capable of installing more than one dependency at a time.

So are you excited about Yarn yet? If not, here are a few more Yarn features that have been useful to me:

yarn why left-pad Identifies why the left-pad package is installed, showing which other packages depend upon it.

Identifies why the package is installed, showing which other packages depend upon it. yarn licenses generate-disclaimer generates a license disclaimer from installed dependencies.

generates a license disclaimer from installed dependencies. yarn upgrade-interactive interactively upgrades specific dependencies.

Heroku has full support for Yarn. Any Node.js app with a yarn.lock file will be built using Yarn instead of npm. Without any additional work, using Yarn on Heroku will get you predictable, secure, and, possibly, faster deploys. Of course, if you want or need to continue using npm, you can.

If you haven’t used Yarn yet, I encourage you to check out the getting started guide. It took me just a few minutes to swap out npm for Yarn on several existing projects, and just a few more minutes to figure out Yarn’s simple commands.