Making sense of the difference between AMD, CommonJS, RequireJS and Browserify

A colleague starting on javascript frameworks asked me what’s the difference between AMD, CommonJS, RequireJS and Browserify. He was under the impression they are 4 separate technologies so here’s a quick overview for those who might be confused. Take note this article is not meant as an in-depth discussion but more of an overview for people unfamiliar with the terms and not sure where to begin.

For a start, we need to understand they are not from the same category. Asynchronous module definition (AMD) and CommonJS are styles/specifications of writing modular code in javascript, while RequireJS and Browserify are 2 popular implementation/helper for them respectively. RequireJS allows you to work with modules the AMD way in the browser, and Browserify for the CommonJS way.

AMD

Asynchronous module definition is designed for the browser from the start. It allows your frontend application to load the starting point of your app, which in turn loads it’s dependencies on the fly. When using the requireJS implementation, it’s syntax looks like:

Defining the module in somethingAwesome.js
[code lang=”js”]
define(function {
console.log(‘Awesome’);
});
[/code]

Consuming the module, note that you do not necessary need to follow the same name of the module.
[code lang=”js”]
define([‘somethingAwesome’], function (thatAwesomeSomething) {
thatAwesomeSomething();
});
[/code]

The mapping of module name to script file path can be done via calling requirejs.config({..});, and requireJS intelligently requests for the needed file (and dependencies) if not already loaded on the browser. Take note that this would generate a large amount of requests for a fairly complex app, and the latency adds up especially on mobile.

CommonJS

CommonJS was designed for an environment where latency of accessing external scripts is negligible, such as on a server running node.js. It’s syntax would be familiar for developers from other languages used in the backend such as java, PHP or C#, where modules are include/imported/required at the start of the code file:

Defining the module in somethingAwesome.js
[code lang=”js”]
module.exports = function(){
console.log(‘Awesome’);
}
[/code]

When consuming it somewhere else.
[code lang=”js”]
// Import our awesome module, you can call it anything you want
var thatAwesomeSomething = require(‘somethingAwesome’);
// Use it
thatAwesomeSomething();
[/code]

As you can see, require('somethingAwesome'); assumes the runtime has access to

  1. the definition file (package.json) of the module, which defines the path of the script file (somethingAwesome.js), and
  2. the actual script file to be loaded.

Our browser runtime has access to neither, without some help from 3rd party tools. That’s where Browserify comes in.

Browserify

Browserify allows us to use the CommonJS style in the browser (hence the name) by compiling the entire dependency tree into one or more script files which is then served along your page. That’s right, every last dependency would be served in one go instead of one-by-one in the case of AMD. This significantly reduces the number of requests made to the server for a large app (imagine how many round trips that saves!), but adds an additional build step during development. Your browser now needs to download more code before it starts to execute the script, though a single large request over CDN can still be faster compared to several small requests.

But wait, you can compile RequireJS too! Using r.js, one can do the same with AMD style modules, and you will end up with the same pros and cons as CommonJS. The details are out-of-scope for this article though, so please refer to http://requirejs.org/docs/optimization.html#wholeproject for more information

Conclusion

Since both styles can be compiled, the decision between the 2 styles really boils down to

  1. which syntax format you prefer,
  2. whether you need the ability to asynchronously load modules on the fly with AMD (with the impact of higher total latency), and
  3. whether the 3rd party modules your app depends on are available in the style of choice.

Many major libraries nowadays have both options, and there are ways to work around (shim it) even if the authors do not provide them.

Personally, I went with CommonJS + Browserify because I do not need to load modules on the fly, and because it is more similar to the ECMAScript 6 (ES6) syntax which adopts best of both worlds and would be more of a standard in the future. Read more about module support in ES6 at http://www.2ality.com/2014/09/es6-modules-final.html

So that’s it for a brief overview! I have not covered all the intricacies of each approach so there are bound to be lots more reading involved, but hope it points you in the right direction in your search. For an excellant article covering history, background, and working with the future ES6 syntax on ES5 browsers, check out https://medium.com/@brianleroux/es6-modules-amd-and-commonjs-c1acefbe6fc0. If you need any help feel free to reach out to me.

Leave a Reply

Your email address will not be published. Required fields are marked *