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.
To begin, here’s an overview of how we demonstrated using npm in conjunction with Bower to build a web application:
postinstall
npm
script to install Bower dependencies
automatically following the successful installation of npm modulesnode_modules/
directory was ignored by the project’s
version control system. While building the application, we would update
this configuration to also include the bower_components/
directory.npm
command-line tool and how to use the
bower
command-line toolpackage.json
file for the project’s Node.js
dependencies and a bower.json
file for the project’s
browser dependenciesThis 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.
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.
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:
node_modules/
when
initializing the projectpackage.json
for all third-party JavaScript
dependenciesWe think this represents a much stronger workflow, and we hope it will make the content even more approachable.
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.
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!