Eventual Consistency

Structuring Client-side JavaScript code

JavaScript is a powerful language, but like any other tool it has its ugly parts and its good parts.

One of its "features", is that it doesn't really enforce any particular packaging practices. Modules or namespaces are not first class citizens. This means that as developers we are pretty much free to do whatever we want, and with this great power, comes great responsibility.

This is my set of best practices, detailing how to better organize projects, create structure and enforce design paradigms in a way that makes it easy to read and maintain. It's definitely not the only way to go, and might not fit all use cases, but I found it to be useful in real world scenarios for most of the projects I've done. That being said, I'm sure there is room for improvement, so I'd be more than happy to hear how your way is better and why I'm wrong and stupid.

Breaking things down into files

One of the first things I've learned, is that JavaScript makes it really easy to start off with a single file, listening to when the DOM is ready, and create a whole mess of AJAX calls, event listeners, application logic and templating all mangled into each other.

Our intentions were good. We didn't plan for this to happen, but the project was so simple when it started, so breaking it up into files and modules was just over-complicating things, it seemed.

But as time went by, and Feature Creep started settling in, we added more and more functionality to our monolithic code base. It is now impossible to test, thus also impossible to refactor. The only option is to start over and rewrite the whole damn thing.

So how can we avoid this? simple. Before I start writing even a single line of code, I create the following file structure (assuming the project is called awesomeApp):

js/
    awesomeApp/
        common.js
        models.js
        views.js
        handlers.js
        app.js

The common.js file is used to create a namespace and provide general utilities and classes that are, well, common to the entire project.

models.js doesn't have to hold actual classes representing models (although if using some MV* framework such as Backbone.js this would be the place to put them). For simpler projects I would just put my AJAX functions here.

views.js holds the presentational logic. This could be Backbone.js views, your own template handling code, or just manual creation of DOM elements. It all happens here.

handlers.js is used to hold callbacks that are fired when the user does something. they are not yet tied to their respective events, but only defined here. This might seem counter-intuitive or difficult at first, but it has proven itself to be really helpful at fighting and preventing callback hell.

The last file is app.js. This is where we kick things off, connecting events to their handlers, and running any startup logic our app might have.

As you can see, this isn't strictly MVC/MV*, but I found it to make sense and provide a good structure to build on top of. In real life, I might even break this down even further, or add more files as I go.

Side note: Don't worry about browser performance or bloat, since we can always concatenate and minify these into a single file when deploying.

Namespaces

Namespaces are awesome (or, are one honking great idea, as some Python developers might say). They help separate concerns, create modularity, and make it easy to understand where components are found, and what exactly they do.

JavaScript, unfortunately, doesn't natively support them. But it's easy enough to implement ourselves. We can just use an arbitrary object.

I like building my namespace as a tree. I start off with a only one object defined in the global scope. This would serve as our "root element".

var awesomeApp = {};

This would generally be defined in common.js, as it is the first file loaded.

Then, for each file, I would create its own namespace inside our root object. All variables, functions and classes would then go inside that namespace. Here's an example:

// Assuming this is models.js
awesomeApp.models = {
    getItems: function(onSuccess, onError) {
        $.getJSON('/items', onSuccess).error(onError);
    },

    getItem: function(itemId, onSuccess, onError) {
        $.getJSON('/item', {id: itemId}, onSuccess).error(onError);
    },

    // ...more stuff goes here.
};

This way, when we call awesomeApp.models.getItems() from anywhere else in the code, it's immediately apparent where that function came from and what it does. Structure and clarity are enforced by convention.

Configuration and settings

I usually create a settings object in common.js. this object is accessible to all other modules, and lives under the awesomeApp.settings namespace. I would generally place the following information inside it:

  • URLs and API endpoints
  • HTML class names and IDs
  • Boolean feature flags, to easily enable or disable a feature
  • App related settings that alter or tune the behavior of the application

Basically, anything that might change over time should be placed here. Just be careful not to change the value of these settings in runtime. Treat them as immutable or you'll suffer some nasty bugs as a result.


This is my basic setup for most projects. As always, I'd love to hear your feedback and your methods in the comments!

comments powered by Disqus

Hi, I'm Oz Katz

I am a co-founder and CTO over at Swayy.

I usually write about software development using Python, JavaScript and other awesome, open source tools.

Feel free to reach out on Twitter, or contact me using the links at the bottom of the page.