Further Learning - Developing Backbone.js Applications (2013)

Developing Backbone.js Applications (2013)

Appendix A. Further Learning

A Simple JavaScript MVC Implementation

A comprehensive discussion of Backbone’s implementation is beyond the scope of this book. I can, however, present a simple MVC library—which we will call Cranium.js—that illustrates how frameworks such as Backbone implement the MVC pattern.

Like Backbone, we will rely on Underscore for inheritance and templating.

Event System

At the heart of our JavaScript MVC implementation is an Event system (object) based on the publisher-subscriber pattern, which makes it possible for MVC components to communicate in an elegant, decoupled manner. Subscribers listen for specific events of interest and react when publishers broadcast these events.

Event is mixed into both the view and model components so that instances of either of these components can publish events of interest.

// cranium.js - Cranium.Events

var Cranium = Cranium || {};

// Set DOM selection utility

var $ = document.querySelector.bind(document) || this.jQuery || this.Zepto;

// Mix in to any object in order to provide it with custom events.

var Events = Cranium.Events = {

// Keeps list of events and associated listeners

channels: {},

// Counter

eventNumber: 0,

// Announce events and passes data to the listeners;

trigger: function (events, data) {

for (var topic in Cranium.Events.channels){

if (Cranium.Events.channels.hasOwnProperty(topic)) {

if (topic.split("-")[0] == events){

Cranium.Events.channels[topic](data) !== false ||

delete Cranium.Events.channels[topic];

}

}

}

},

// Registers an event type and its listener

on: function (events, callback) {

Cranium.Events.channels[events + --Cranium.Events.eventNumber] = callback;

},

// Unregisters an event type and its listener

off: function(topic) {

delete Cranium.Events.channels[topic];

}

};

The Event system makes it possible for:

§ A view to notify its subscribers of user interaction (such as clicks or input in a form), to update/rerender its presentation, etc.

§ A model whose data has changed to notify its subscribers to update themselves (for example, view to rerender to show accurate/updated data) and so on.

Models

Models manage the (domain-specific) data for an application. They are concerned with neither the user interface nor presentation layers, but instead represent structured data that an application may require. When a model changes (such as when it is updated), it will typically notify its observers (subscribers) that a change has occurred so that they may react accordingly.

Let’s see a simple implementation of the model:

// cranium.js - Cranium.Model

// Attributes represents data, model's properties.

// These are to be passed at Model instantiation.

// Also we are creating id for each Model instance

// so that it can identify itself (e.g., on chage

// announcements)

var Model = Cranium.Model = function (attributes) {

this.id = _.uniqueId('model');

this.attributes = attributes || {};

};

// Getter (accessor) method;

// returns named data item

Cranium.Model.prototype.get = function(attrName) {

return this.attributes[attrName];

};

// Setter (mutator) method;

// Set/mix in into model mapped data (e.g.{name: "John"})

// and publishes the change event

Cranium.Model.prototype.set = function(attrs){

if (_.isObject(attrs)) {

_.extend(this.attributes, attrs);

this.change(this.attributes);

}

return this;

};

// Returns clone of the Models data object

// (used for view template rendering)

Cranium.Model.prototype.toJSON = function(options) {

return _.clone(this.attributes);

};

// Helper function that announces changes to the Model

// and passes the new data

Cranium.Model.prototype.change = function(attrs){

this.trigger(this.id + 'update', attrs);

};

// Mix in Event system

_.extend(Cranium.Model.prototype, Cranium.Events);

Views

Views are a visual representation of models that present a filtered view of their current state. A view typically observes a model and is notified when the model changes, allowing the view to update itself accordingly. Design pattern literature commonly refers to views as dumb, given that their knowledge of models and controllers in an application is limited.

Let’s explore views a little further using a simple JavaScript example:

// DOM View

var View = Cranium.View = function (options) {

// Mix in options object (e.g., extending functionality)

_.extend(this, options);

this.id = _.uniqueId('view');

};

// Mix in Event system

_.extend(Cranium.View.prototype, Cranium.Events);

Controllers

Controllers are an intermediary between models and views and are classically responsible for two tasks:

§ Updating the view when the model changes

§ Updating the model when the user manipulates the view

// cranium.js - Cranium.Controller

// Controller tying together a model and view

var Controller = Cranium.Controller = function(options){

// Mix in options object (e.g extending functionality)

_.extend(this, options);

this.id = _.uniqueId('controller');

var parts, selector, eventType;

// Parses Events object passed during the definition of the

// controller and maps it to the defined method to handle it;

if(this.events){

_.each(this.events, function(method, eventName){

parts = eventName.split('.');

selector = parts[0];

eventType = parts[1];

$(selector)['on' + eventType] = this[method];

}.bind(this));

}

};

Practical Usage

Here is the HTML template for the primer that follows:

<!doctype html>

<html lang="en">

<head>

<meta charset="utf-8">

<title></title>

<meta name="description" content="">

</head>

<body>

<div id="todo">

</div>

<script type="text/template" class="todo-template">

<div>

<input id="todo_complete" type="checkbox" <%= completed %>>

<%= title %>

</div>

</script>

<script src="underscore-min.js"></script>

<script src="cranium.js"></script>

<script src="example.js"></script>

</body>

</html>

Cranium.js usage:

// example.js - usage of Cranium MVC

// And todo instance

var todo1 = new Cranium.Model({

title: "",

completed: ""

});

console.log("First todo title - nothing set: " + todo1.get('title'));

todo1.set({title: "Do something"});

console.log("Its changed now: " + todo1.get('title'));

''

// View instance

var todoView = new Cranium.View({

// DOM element selector

el: '#todo',

// Todo template; Underscore templating used

template: _.template($('.todo-template').innerHTML),

init: function (model) {

this.render( model.toJSON() );

this.on(model.id + 'update', this.render.bind(this));

},

render: function (data) {

console.log("View about to render.");

$(this.el).innerHTML = this.template( data );

}

});

var todoController = new Cranium.Controller({

// Specify the model to update

model: todo1,

// and the view to observe this model

view: todoView,

events: {

"#todo.click" : "toggleComplete"

},

// Initialize everything

initialize: function () {

this.view.init(this.model);

return this;

},

// Toggles the value of the todo in the Model

toggleComplete: function () {

var completed = todoController.model.get('completed');

console.log("Todo old 'completed' value?", completed);

todoController.model.set({ completed: (!completed) ? 'checked': '' });

console.log("Todo new 'completed' value?",

todoController.model.get('completed'));

return this;

}

});

// Let's start things off

todoController.initialize();

todo1.set({ title: "Due to this change Model will notify View and

it will rerender"});

Samuel Clay, one of the authors of the first version of Backbone.js, says of Cranium.js: “Unsurprisingly, [it] looks a whole lot like the beginnings of Backbone. Views are dumb, so they get very little boilerplate and setup. Models are responsible for their attributes and announcing changes to those models.”

I hope you’ve found this implementation helpful in explaining how you would go about writing your own library like Backbone from scratch, but more so that it encourages you to take advantage of mature existing solutions where possible but never be afraid to explore deeper down into what makes them tick.

MVP

Model-View-Presenter (MVP) is a derivative of the MVC design pattern that focuses on improving presentation logic. It originated at a company named Taligent in the early 1990s while the company was working on a model for a C++ CommonPoint environment. While both MVC and MVP target the separation of concerns across multiple components, there are some fundamental differences between them.

For the purposes of this summary, we will focus on the version of MVP most suitable for web-based architectures.

Models, Views, and Presenters

The P in MVP stands for presenter. It’s a component that contains the user interface business logic for the view. Unlike MVC, invocations from the view are delegated to the presenter, which are decoupled from the view and instead talk to it through an interface. This allows for all kinds of useful things such as being able to mock views in unit tests.

The most common implementation of MVP is one that uses a passive view (a view that, for all intents and purposes, is dumb), containing little to no logic. MVP models are almost identical to MVC models and handle application data. The presenter acts as a mediator that talks to both the view and model; however, the view and model are isolated from each other. Presenters effectively bind models to views, a responsibility held by controllers in MVC. Presenters are at the heart of the MVP pattern, and as you can guess, incorporate the presentation logic behind views.

Solicited by a view, presenters perform any work having to do with user requests and pass data back to them. In this respect, they retrieve data, manipulate it, and determine how the data should be displayed in the view. In some implementations, the presenter also interacts with a service layer to persist data (models). Models may trigger events but it’s the presenter’s role to subscribe to them so that the presenter can update the view. In this passive architecture, we have no concept of direct data binding. Views expose setters that presenters can use to set data.

The benefit of this change from MVC is that it increases the testability of your application and provides a cleaner separation between the view and the model. This isn’t without its costs, as the lack of data binding support in the pattern can often mean having to take care of this task separately.

Although a common implementation of a passive view is for the view to implement an interface, there are variations on it, including the use of events that can decouple the view from the presenter a little more. As we don’t have the interface construct in JavaScript, we’re using it more and more as a protocol than an explicit interface here. It’s technically still an API, and it’s probably fair for us to refer to it as an interface from that perspective.

There is also a supervising controller variation of MVP, which is closer to the MVC and MVVM—Model-View-ViewModel—patterns, as it provides data binding from the model directly from the view. Key/value observing (KVO) plug-ins (such as Derick Bailey’sBackbone.ModelBinding plug-in) introduce this idea of a supervising controller to Backbone.

MVP or MVC?

MVP is generally used most often in enterprise-level applications where it’s necessary to reuse as much presentation logic as possible. Applications with very complex views and a great deal of user interaction may find that MVC doesn’t quite fit the bill here, as solving this problem may mean heavily relying on multiple controllers. In MVP, all of this complex logic can be encapsulated in a presenter, which can simplify maintenance greatly.

Because MVP views are defined through an interface and the interface is technically the only point of contact between the system and the view (other than a presenter), this pattern also allows developers to write presentation logic without needing to wait for designers to produce layouts and graphics for the application.

Depending on the implementation, MVP may be more easy to automatically unit-test than MVC. The reason often cited for this is that the presenter can be used as a complete mock of the user interface and so it can be unit-tested independent of other components. In my experience, this really depends on the languages in which you are implementing MVP (there’s quite a difference between opting for MVP for a JavaScript project over one for, say, ASP.NET).

At the end of the day, the underlying concerns you may have with MVC will likely hold true for MVP given that the differences between them are mainly semantic. As long as you are cleanly separating concerns into models, views, and controllers (or presenters), you should be achieving most of the same benefits regardless of the pattern you opt for.

MVC, MVP, and Backbone.js

There are very few, if any, architectural JavaScript frameworks that claim to implement the MVC or MVP patterns in their classical form, as many JavaScript developers don’t view MVC and MVP as being mutually exclusive (we are actually more likely to see MVP strictly implemented when looking at web frameworks such as ASP.NET or GWT). This is because it’s possible to have additional presenter/view logic in your application and yet still consider it a flavor of MVC.

Backbone contributor Irene Ros subscribes to this way of thinking, as when she separates Backbone views out into their own distinct components, she needs something to actually assemble them for her. This could either be a controller route (such as a Backbone.Router) or a callback in response to data being fetched.

That said, some developers do feel that Backbone.js better fits the description of MVP than it does MVC . Their view is that:

§ The presenter in MVP better describes the Backbone.View (the layer between view templates and the data bound to it) than a controller does.

§ The model fits Backbone.Model (it isn’t that different from the classical MVC model).

§ The views best represent templates (such as Handlebars/Mustache markup templates).

A response to this could be that the view can also just be a view (as per MVC) because Backbone is flexible enough to let it be used for multiple purposes. The V in MVC and the P in MVP can both be accomplished by Backbone.View because they’re able to achieve two purposes: both rendering atomic components and assembling those components rendered by other views.

We’ve also seen that in Backbone the responsibility of a controller is shared with both the Backbone.View and Backbone.Router, and in the following example we can actually see that aspects of that are certainly true.

Here, our Backbone TodoView uses the Observer pattern to subscribe to changes to a view’s model in the line this.model.on('change',...). It also handles templating in the render() method, but unlike some other implementations, user interaction is also handled in the view (seeevents).

// The DOM element for a todo item...

app.TodoView = Backbone.View.extend({

//... is a list tag.

tagName: 'li',

// Pass the contents of the todo template through a templating

// function, cache it for a single todo

template: _.template( $('#item-template').html() ),

// The DOM events specific to an item.

events: {

'click .toggle': 'togglecompleted'

},

// The TodoView listens for changes to its model, rerendering. Since there's

// a one-to-one correspondence between a **Todo** and a **TodoView** in this

// app, we set a direct reference on the model for convenience.

initialize: function() {

this.model.on( 'change', this.render, this );

this.model.on( 'destroy', this.remove, this );

},

// Rerender the titles of the todo item.

render: function() {

this.$el.html( this.template( this.model.toJSON() ) );

return this;

},

// Toggle the `"completed"` state of the model.

togglecompleted: function() {

this.model.toggle();

},

});

Another (quite different) opinion is that Backbone more closely resembles Smalltalk-80 MVC, which we covered earlier.

As MarionetteJS author Derick Bailey has written, it’s ultimately best not to force Backbone to fit any specific design patterns. Design patterns should be considered flexible guides to how applications may be structured, and in this respect, Backbone doesn’t fit either MVC nor MVP perfectly. Instead, it borrows some of the best concepts from multiple architectural patterns and creates a flexible framework that just works well. Call it the Backbone way, MV*, or whatever helps reference its flavor of application architecture.

It is, however, worth understanding where and why these concepts originated, so I hope that my explanations of MVC and MVP have been of help. Most structural JavaScript frameworks will adopt their own take on classical patterns, either intentionally or by accident, but the important thing is that they help us develop applications that are organized, clean, and easy to maintain.

Namespacing

When learning how to use Backbone, you’ll find that an important and commonly overlooked area by tutorials is namespacing. If you already have experience with namespacing in JavaScript, the following section will provide some advice on how to specifically apply concepts you know to Backbone; however, I will also be covering explanations for beginners to ensure that everyone is on the same page.

What Is Namespacing?

Namespacing is a way to avoid collisions with other objects or variables in the global namespace. Using namespacing reduces the potential of your code breaking because another script on the page is using the same variable names that you are. As a good citizen of the global namespace, you must do your best to minimize the possibility of your code breaking another developer’s scripts.

JavaScript doesn’t really have built-in support for namespaces like other languages, but it does have closures, which can be used to achieve a similar effect.

In this section we’ll be taking a look at some examples of how you can namespace your models, views, routers, and other components. The patterns we’ll be examining are:

§ Single global variables

§ Object literals

§ Nested namespacing

Single global variables

One popular pattern for namespacing in JavaScript is opting for a single global variable as your primary object of reference. Here’s a skeleton implementation of this, where we return an object with functions and properties:

var myApplication = (function(){

function(){

// ...

},

return {

// ...

}

})();

You’ve probably seen this technique before. A Backbone-specific example might look like this:

var myViews = (function(){

return {

TodoView: Backbone.View.extend({ .. }),

TodosView: Backbone.View.extend({ .. }),

AboutView: Backbone.View.extend({ .. });

//etc.

};

})();

Here we can return a set of views, but the same technique could return an entire collection of models, views, and routers depending on how you decide to structure your application. Although this works for certain situations, the biggest challenge with the single global variable pattern is ensuring that no one else has used the same global variable name as you have in the page.

One solution to this problem, as mentioned by Peter Michaux, is to use prefix namespacing. It’s a simple concept at heart, but the idea is that you select a common prefix name (in this example, myApplication_) and then define any methods, variables, or other objects after the prefix.

var myApplication_todoView = Backbone.View.extend({}),

myApplication_todosView = Backbone.View.extend({});

This is effective from the perspective of trying to lower the chances of a particular variable existing in the global scope, but remember that a uniquely named object can have the same effect. This aside, the biggest issue with the pattern is that it can result in a large number of global objects once your application starts to grow.

For more on Peter’s views about the single global variable pattern, read his excellent post on them.

NOTE

There are several other variations on the single global variable pattern out in the wild; however, having reviewed quite a few, I felt the prefixing approach applied best to Backbone.

Object literals

Object literals have the advantage of not polluting the global namespace but assist in organizing code and parameters logically. They’re beneficial if you wish to create easily readable structures that can be expanded to support deep nesting. Unlike simple global variables, object literals often also take into account tests for the existence of a variable by the same name, which helps reduce the chances of collision.

This example demonstrates two ways you can check to see if a namespace already exists before defining it. I commonly use option 2.

/* Doesn't check for existence of myApplication */

var myApplication = {};

/*

Does check for existence. If already defined, we use that instance.

Option 1: if(!myApplication) myApplication = {};

Option 2: var myApplication = myApplication || {};

We can then populate our object literal to support models, views, and collections

(or any data, really):

*/

var myApplication = {

models : {},

views : {

pages : {}

},

collections : {}

};

You can also opt for adding properties directly to the namespace (such as your views, in the following example):

var myTodosViews = myTodosViews || {};

myTodosViews.todoView = Backbone.View.extend({});

myTodosViews.todosView = Backbone.View.extend({});

The benefit of this pattern is that you’re able to easily encapsulate all of your models, views, routers, and so on in a way that clearly separates them and provides a solid foundation for extending your code.

This pattern has a number of benefits. It’s often a good idea to decouple the default configuration for your application into a single area that can be easily modified without the need to search through your entire codebase just to alter it. Here’s an example of a hypothetical object literal that stores application configuration settings:

var myConfig = {

language: 'english',

defaults: {

enableDelegation: true,

maxTodos: 40

},

theme: {

skin: 'a',

toolbars: {

index: 'ui-navigation-toolbar',

pages: 'ui-custom-toolbar'

}

}

}

Note that there are really only minor syntactical differences between the object literal pattern and a standard JSON data set. If, for any reason, you wish to use JSON for storing your configurations instead (for example, for simpler storage when sending to the backend), feel free to.

For more on the object literal pattern, I recommend reading Rebecca Murphey’s excellent article on the topic.

Nested namespacing

An extension of the object literal pattern is nested namespacing. It’s another commonly used pattern that offers a lower risk of collision due to the fact that even if a top-level namespace already exists, it’s unlikely the same nested children do. For example, Yahoo’s YUI uses the nested object namespacing pattern extensively:

YAHOO.util.Dom.getElementsByClassName('test');

Even DocumentCloud (the creators of Backbone) uses the nested namespacing pattern in its main applications. A sample implementation of nested namespacing with Backbone may look like this:

var todoApp = todoApp || {};

// perform similar check for nested children

todoApp.routers = todoApp.routers || {};

todoApp.model = todoApp.model || {};

todoApp.model.special = todoApp.model.special || {};

// routers

todoApp.routers.Workspace = Backbone.Router.extend({});

todoApp.routers.TodoSearch = Backbone.Router.extend({});

// models

todoApp.model.Todo = Backbone.Model.extend({});

todoApp.model.Notes = Backbone.Model.extend({});

// special models

todoApp.model.special.Admin = Backbone.Model.extend({});

This is readable, clearly organized, and a relatively safe way of namespacing your Backbone application. The only real caveat is that it requires your browser’s JavaScript engine to first locate the todoApp object, then dig down until it gets to the function you’re calling. However, developers such as Juriy Zaytsev (kangax) have tested and found the performance differences between single object namespacing and the nested approach to be quite negligible.

What Does DocumentCloud Use?

In case you were wondering, here is the original DocumentCloud (remember those guys who created Backbone?) workspace that uses namespacing in a necessary way. This approach makes sense, as the company’s documents (and annotations and document lists) are embedded on third-party news sites.

// Provide top-level namespaces for our javascript.

(function() {

window.dc = {};

dc.controllers = {};

dc.model = {};

dc.app = {};

dc.ui = {};

})();

As you can see, DocumentCloud opts for declaring a top-level namespace on the window called dc, a short-form name of the app, followed by nested namespaces for the controllers, models, UI, and other pieces of the application.

Recommendation

Of the preceding namespace patterns, the option that I prefer when writing Backbone applications is nested object namespacing with the object literal pattern.

Single global variables may work fine for applications that are relatively trivial. However, larger codebases requiring both namespaces and deep subnamespaces require a succinct solution that’s both readable and scalable. I feel this pattern achieves both of these objectives and is a good choice for most Backbone development.

Backbone Dependency Details

The following sections provide insight into how Backbone uses jQuery/Zepto and Underscore.js.

DOM Manipulation

Although most developers won’t need it, Backbone does support setting a custom DOM library to be used instead of these options. From the source:

// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns

// the `$` variable.

Backbone.$ = root.jQuery || root.Zepto || root.ender || root.$;

So, setting Backbone.$ = myLibrary; will allow you to use any custom DOM-manipulation library in place of the jQuery default.

Utilities

Underscore.js is heavily used in Backbone behind the scenes for everything from object extension to event binding. As the entire library is generally included, we get free access to a number of useful utilities we can use on collections, such as filtering _.filter(), sorting _.sortBy(), mapping _.map(), and so on.

From the source:

// Underscore methods that we want to implement on the Collection.

// 90% of the core usefulness of Backbone Collections is actually implemented

// right here:

var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',

'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',

'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',

'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',

'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf',

'isEmpty', 'chain'];

// Mix in each Underscore method as a proxy to `Collection#models`.

_.each(methods, function(method) {

Collection.prototype[method] = function() {

var args = slice.call(arguments);

args.unshift(this.models);

return _[method].apply(_, args);

};

});

However, for a complete linked list of methods supported, see the official documentation.

RESTful Persistence

We can sync models and collections in Backbone with the server using the fetch, save, and destroy methods. All of these methods delegate back to the Backbone.sync function, which actually wraps jQuery/Zepto’s $.ajax function, calling GET, POST, and DELETE for the respective persistence methods on Backbone models.

From the source for Backbone.sync:

var methodMap = {

'create': 'POST',

'update': 'PUT',

'patch': 'PATCH',

'delete': 'DELETE',

'read': 'GET'

};

Backbone.sync = function(method, model, options) {

var type = methodMap[method];

// ... Followed by lots of Backbone.js configuration, then..

// Make the request, allowing the user to override any Ajax options.

var xhr = options.xhr = Backbone.ajax(_.extend(params, options));

model.trigger('request', model, xhr, options);

return xhr;

Routing

Calls to Backbone.History.start rely on jQuery/Zepto binding popState or hashchange event listeners back to the window object.

From the source for Backbone.history.start:

// Depending on whether we're using pushState or hashes, and whether

// 'onhashchange' is supported, determine how we check the URL state.

if (this._hasPushState) {

Backbone.$(window)

.on('popstate', this.checkUrl);

} else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {

Backbone.$(window)

.on('hashchange', this.checkUrl);

} else if (this._wantsHashChange) {

this._checkUrlInterval = setInterval(this.checkUrl, this.interval);

}

...

Backbone.History.stop similarly uses your DOM manipulation library to unbind these event listeners.

Backbone Versus Other Libraries and Frameworks

Backbone is just one of many different solutions available for structuring your application, and we’re by no means advocating it as the be-all and end-all. It’s served the contributors to this book well in building many simple and complex web applications, and I hope that it can serve you equally well. The answer to the question “Is Backbone better than X?” generally has a lot more to do with what kind of application you’re building.

AngularJS and Ember.js are examples of powerful alternatives but differ from Backbone in that they are more opinionated. For some projects, this can be useful; for others, perhaps not. The important thing to remember is that there is no library or framework that’s going to be the best solution for every use case, so it’s important to learn about the tools at your disposal and decide which one is best on a project-by-project basis.

Choose the right tool for the right job. This is why I recommend spending some time doing a little due diligence. Consider productivity, ease of use, testability, community, and documentation. If you’re looking for more concrete comparisons between frameworks, read:

§ “Journey Through the JavaScript MVC Jungle”

§ “Rich JavaScript Applications—The Seven Frameworks”

The authors behind Backbone.js, AngularJS, and Ember have also discussed some of the strengths and weaknesses of their solutions on Quora, StackOverflow, and so on:

§ Jeremy Ashkenas on why Backbone

§ Tom Dale on Ember.js versus AngularJS

§ Brian Ford and Jeremy Ashkenas on Backbone versus Angular (discussion)

The solution you opt for may need to support building nontrivial features and could end up being used to maintain the app for years to come, so think about things like:

What is the library/framework really capable of?

Spend time reviewing both the source code of the framework and official list of features to see how well they fit with your requirements. There will be projects that may require modifying or extending the underlying source, so make sure that if this might be the case, you’ve performed due diligence on the code. Has the framework been proven in production? Have developers actually built and deployed large applications with it that are publicly accessible? Backbone has a strong portfolio of these (SoundCloud, LinkedIn, Walmart), but not all frameworks do. Ember is used in a number of large apps, including the new version of ZenDesk. AngularJS has been used to build the YouTube app for PS3, among other places. It’s not only important to know that a framework works in production, but also to be able to look at real-world code and be inspired by what can be built with it.

Is the framework mature?

I generally recommend that developers don’t simply pick one and go with it. New projects often come with a lot of buzz surrounding their releases, but remember to take care when selecting them for use on a production-level app. You don’t want to risk the project being canned, going through major periods of refactoring, or other breaking changes that tend to be more carefully planned out when a framework is mature. Mature projects also tend to have more detailed documentation available, either as a part of their official or community-driven docs.

Is the framework flexible or opinionated?

Know what flavor you’re after, because there are plenty of frameworks available that provide one or the other. Opinionated frameworks lock you into doing things in a specific way (theirs). By design they are limiting, but place less emphasis on developers having to figure out how things should work on their own. Have you really played with the framework?

Write a small application without using frameworks and then attempt to refactor your code with a framework to confirm whether it’s easy to work with or not. As much as researching and reading up on code will influence your decision, it’s equally important to write actual code using the framework to make sure you’re comfortable with the concepts it enforces.

Does the framework have a comprehensive set of documentation?

Although demo applications can be useful for reference, you’ll almost always find yourself consulting the official framework docs to find out what its API supports, how common tasks or components can be created with it, and what the gotchas worth noting are. Any framework worth its salt should have a detailed set of documentation that will help guide developers using it. Without this, you can find yourself heavily relying on IRC channels, groups, and self-discovery, which can be fine, but are often overly time-consuming when compared to a great set of docs provided upfront.

What is the total size of the framework, factoring in minification, gzipping, and any modular building that it supports?

What dependencies does the framework have? Frameworks tend to list only the total file size of the base library itself, not the sizes of the library’s dependencies. This can mean the difference between opting for a library that initially looks quite small, but could be relatively large if it, say, depends on jQuery and other libraries.

Have you reviewed the community around the framework?

Is there an active community of project contributors and users who would be able to assist if you run into issues? Have enough developers been using the framework that there are existing reference applications, tutorials, and maybe even screencasts that you can use to learn more about it?