In What the Web Platform can learn from Node.js, we explored the benefits of narrowly scoped abstractions created by developers for developers. Let’s learn how and why you should bring this same style of development to your own web frontend.
Choose your own adventure
As a user of small modules, if a dependency takes a turn you don’t agree with, you can simply use another. Or maybe your app uses a newer version of a module (let’s say 2.x), while another dependency of your app uses another (call this 1.x). In Node, since dependencies are looked up in neighboring
node_modules directories on up the filesystem, each can be satisfied even if they require the same module with differing version requirements. If the version requirements align, only one copy will be used.
npm modules in the browser? Isn’t that a Node thing?
You might be wondering how one could maintain so many dependencies without thousands of script tags, or even thousands of entries in a RequireJS config file. Or, maybe you might be wondering how it’s even possible to use a module from npm in the browser to easily create SVG elements. Modern tooling like Browserify and Webpack have made this possible by tracing the dependency graph of your app through the same CommonJS
require statement that Node uses. They make each module available to one another in a large bundle file that you can include with a single script tag in your page.
Most folks I know are horrified by the idea of shipping multiple copies of jQuery to the browser. And yes, that would be horrible, because even minified and gzipped, jQuery is still about 30 kilobytes. But shipping two, three or even four copies of a 2KB library is negligible in comparison, especially if it saves you from manually resolving dependencies and upgrading jQuery along with your installed plugins in lockstep. Even so, this would only occur if your app includes modules that depend on many incompatible versions of such dependencies, since npm version 3 deduplicates and flattens the modules directory as much as possible by default. Oh, and it also puts the majority of the npm registry’s 100,000+ modules at your disposal with a simple
Where to draw the line
But first, some terminology: a package is a unit that can be published, for example, to the npm registry or consumable through a git dependency. In CommonJS, however, modules map one-to-one with files. Therefore, a package can contain many modules, but often an npm package is itself a single module.
Deciding a package’s responsibility is arguably the most difficult part of authoring one. If a package’s scope is too large, breaking changes become far too common and their consequences can ripple throughout the ecosystem. Likewise, if a package has many dependents, breaking changes and bugs can lead to a cascading series of updates throughout the system, whether open source or internal. An excellent principle to fall back on when designing the scope of a package is that of software component cohesion). Essentially, if components change together, they belong together in the same package. If not, extract away!
Yes, you can try this at home
Building your app from small modules is a lot easier than you might think. Your app probably already has a collection of abstractions, and it’s often deciding which deserve their own packages that is the difficult part. Firstly, if you only abstract the platform and provide a general-purpose facade, you’re best off providing an open source package. Services like GitHub and Bitbucket are great for this, and if you’re using Node or the browser, you should certainly publish your work to the public npm registry. Of course, other language ecosystems have their own package management solutions.
npm install bitbucket:user/repo and you’re all set.
Once your small modules have found a home, you’re free to iterate on their designs and compose together others to build higher and higher levels of abstraction. Oh, and you can fearlessly (but not carelessly!) break APIs since modern tooling and Semantic Versioning ensure that consumers opt into these kinds of changes, all while rapidly improving upon what you do best. That’s what change is all about.