Doubling Down on npm

Posted by Mike Pennisi on July 1, 2015

In the beginning, there were no package managers for JavaScript. When developers needed third-party code, they would use a search engine to find websites that published code. They would “Right click” and “Save as…” or copy-and-paste raw text. Those were dark days.

Since that time, there has been an explosion of package managers (tools custom-built to distribute code) for JavaScript. From Bower to Component, Jam to npm and Volo, it seemed like everyone was trying to solve this problem.

Things were a little confusing during this transition, but eventually, two solutions started to “bubble up.” With initial support from Twitter, Bower’s repository of available packages grew to host a majority of the packages that front-end developers would want to use. npm, through its tight integration with the Node.js platform, maintained a consistent edge when it came to JavaScript code written to run in that environment.

For a while, it seemed as though the industry would standardize on using npm exclusively for Node.js packages and Bower for packages written for the browser. We initially designed Roost to reflect this trend.

The npm team recently voiced their interest in supporting both use cases, and through guides on publishing jQuery plugins to last week’s beta release of npm 3, they’ve since been making strides toward that goal. I’d like to talk about why we decided to embrace this change and use npm in isolation for Roost.

Running with Two Package Managers

To begin, here’s an overview of how we demonstrated using npm in conjunction with Bower to build a web application:

This mimicked the setup we had been using for consulting projects, and it was certainly serviceable. I say “serviceable” because we recognized some pain points about that approach.

Breakdowns

Every tool we use introduces a new concept in the workflow. As the number of concepts grows, so too does the cognitive load placed on the developer. While we could optimize our workflow for “smallest number of tools,” we hold a conflicting belief in the Unix philosophy (that is: every tool should do one thing and do it well). This value normally trumps our concerns for cognitive load, but it doesn’t exactly apply to using both Bower and npm. These tools do the same thing (albeit in slightly different ways). That makes the extra conceptual overhead hard to justify, especially in the context of an educational experience like Roost.

This cognitive load is most apparent in those cases where the core semantics of the tools differ. For example npm saves packages using the ^ operator while Bower saves packages using the ~ operator. In order to use both tools concurrently, users need to understand this subtle distinction and keep it in mind whenever they install new dependencies.

Besides being harder to conceptualize, a “dual package manager” workflow is just plain awkward. Initial installation requires steps for both tools, and this of course increases the amount of documentation needed. (We experimented with alternate installation procedures to avoid this, but we’re not comfortable recommending approaches that the projects don’t themselves endorse.)

Difficulties persist beyond the project’s initial set up. Packaging information must be managed across two distinct files. This makes it harder to get an overview of a project’s third-party dependencies, and it forces developers to maintain duplication in common fields like author and version. When the time comes to update, developers have to run two commands to get the latest version of their dependencies (or wire the tools together in non-obvious ways).

As you might expect, no one walked away from Roost San Diego or Roost Chicago saying, “I was inspired by the serviceable approach to package management!” So when the npm team announced its plans to shoulder more responsibilities, we were all ears.

Using npm Alone

As a testament to both tools, the change itself couldn’t have been easier. We simply moved some dependencies from one manifest file to another, updated a few paths in our RequireJS configuration, and we were done! As we hoped, this allowed us to streamline the course content that dealt with package management. Here’s an overview:

We think this represents a much stronger workflow, and we hope it will make the content even more approachable.

Caveats

This approach allows us to address the important aspects of third-party dependency management without getting weighed down by implementation details. That said, there are some realities that may make it inappropriate for wider use today.

  1. The dependency conflict resolution facilities currently offered by Bower are only proposed for npm. (We’re lucky in that the application we build does not have deeply-nested dependencies, so we can do without. This may not hold for all applications.)
  2. As the long-standing de-facto “standard” repository for browser-based JavaScript, Bower offers a better selection. This need not be a deal-breaker, though. As demonstrated by our coworker Tim, fixing this for any given package can be a painless process. (All of the packages we need happen to be available on npm.)

Even if current-day realities make this issue a little less cut-and-dry, we’re convinced this approach to be both more progressive and more conducive to an educational setting. We hope you agree!