Modularity

This document contains some basic advice for how to achieve modularity, then it goes into more advanced ideas as you scroll. At the bottom lie some information on arbiters.

Welcome to Italy

It here is one thing you learn quickly in programming, it is this:

It is awful to read, but it is even worse to modify or maintain.

What is Bad Code

Without rehashing the entire internet: tightly coupled code is bad code. Because

What is Good Code

What it is not: Bad Code

If tightly coupled code is bad code, then good code is loosely coupled. A ⇒ B ∴ ¬B ⇒ ¬A.

In other words, if you factor out your behaviour into small separate units of behaviour, you will have gained maintainability and readibility properties for free, and your code will inevitably have less unknown side-effects, leading to more secure code as well. It does, however, take certain disipline to constantly police your files for multiple types of behaviour.

You may shrug and say, well, I'm only going to write this once anyway..

..and you will be right. You will write it once and probably wish you wrote it zero times.

The biggest mistake you can make as a learning programmer is to not factor out behaviour as early as possible.

Relation to JavaScript

JavaScript has no module system.

Shit.

We have, on the other hand, got functions. Functions with closures.

(function(){
  var private = 5;
  window.publicFn = function(){
    console.log(private);
  }
})();

This is the commonly employed method of encapsulating and exposing objects and functions that can reference private variable through a closure. This works; private is inaccessible outside this anonymous function.

Unfortunately, this just exposes publicFn to the global window object. This is not ideal, as anything, anywhere can just reference it, leaving us not much wiser. True modularity is clearly impossible when things are just lying around freely like this for everyone. It is fragile, and it is error prone as conflicting exports will actually just favour the last script to execute - as JavaScript simply runs top to bottom, attaching its exports to window as we go along. Clearly we need something better than this.

CommonJS

There is a way to fix this, but first of all it assumes all modules need to support a stadardised format for exporting of modules. CommonJS is a such a standardization. It has very large traction at the moment, particularly driven by server side environments such as NodeJS.

Its ideas are simple. Each module avoids the above safety-wrapper, must assume it has a working require(), and instead of attaching its exports to a global object, it attaches them to an opaque exports object. Alternatively, it can replace the module.exports object to define all your exports at once.

By making sure each module is written this way, CommonJS parsers can implement clever trickery on top of it to make this behaviour work. I.e. having each module's exports objects stored somewhere for require() and every module will export a singleton. For more information on this goto the CommonJS section describing how a module system works - and what we have done.

Best Practices

One of the hardest areas to modularize web applications is the client application domain. If you are using jQuery, you should be particularly familiar with this. $ selector calls are spread around, DOM insertion & manipulation code exists all over the place, identical behaviour modifying functions written for every URL. If this is familiar to you, then you should consider looking at a MVC/MVVM framework such as Spine/Backbone/Knockout (although this by no means is this an exhaustive list).

However, for jQuery applications, some things transcends the framework you use to manage your events.

Decoupling jQuery code

It is always important to think about the behaviour you are defining. If it is for

This way if something breaks, you should be easily able to narrow down the problem to a UI error, a signaling error, or a calculation error. => Debugging becomes up to 3 times easier.

General

modul8 just tries to facilitate the building of maintainable code. To actually do so, you need to always stay vigilant and remember to:

Decouple your code this way and you will save yourself the trouble of later having to learn from your mistakes the hard way.

Going Further

Killing Globals

Global variable are evil, and should be kept to a minimum. We know this, and this is were a require system really shines, but you are generally going to depend on a few global variables. Not all libraries are CommonJS compliant, and having jQuery plugins in showing up in your dependency tree under every branch that requires jQuery might just make things more confusing than to load them classically.

Besides, you may want to load it in from a separate CDN anyway.

Even in such an environment, it is possible rid yourself of the global $ and jQuery symbols without breaking everything.

We will demonstrate such a solution. Begin by constructing a jQuery.js file on your application domain containing:

module.exports = window.jQuery;
delete window.jQuery;
delete window.$

This means you can use $ = require('jQuery') so everything will be explicitly defined on the application domain, you've deleted the global shortcuts so that you will know when you forgot to require, and jQuery (but none of its dependencies) show up in the dependency tree. I.e. you will quickly identify what code is actually DOM dependent, and what isn't or shouldn't be. Clearly this is advantageous.

Having found this pattern very useful, but also noticing how repeating this pattern on several libraries pollutes our application code folder with meaningless files, a modul8 extension has been made in 0.3.0 to allow automatic creation of these arbiters in the internal module system by using the arbiters() call. This example could be automated by chaining on arbiters().add('jQuery', ['jQuery', '$']). See the API docs for more details.

Note that modul8 only allows one domain to be DOM dependent (the application domain), so with correct usage - i.e. not just using that domain - you might not have any big revelations there anyway. You are likely going to have require('jQuery') in most places. But if you just find some areas that do not use it, and as a result move those files to an environment agnostic domain, then this has in my book, been a success.

If you can efficiently separate code on the domain level, try to keep above advice in mind (always aim to factor out behavior into small loosely coupled modules), then you are already good on your way to resolving spaghetti hell. The rest is tackling the correct signaling model for your events. And for that there are MVC/MVVM frameworms of varying sizes.

Good luck. Hopefully this is useful on some level.