Monday, March 25, 2013

Client-side JS MV* Framework Roundup

We at SUSE Studio strive to give our users the best experience we can, using modern techniques and technologies.  To that end, I recently spent some time evaluating client-side Javascript MV* frameworks, trying to find a good fit for both how Studio works, and how we develop it.  After a little discussion, we decided we shouldn't be the only ones to benefit from the research, so here's the findings - I hope you find it useful when you have to make the same decision for your web application!

SUSE Studio has evolved a number of methods for performing dynamic client updates; in some places, we use Rails 2.0-style RJS - sending rendered partials along with JS code to be executed by the client. In other places, we're sending JSON data, and handling it via custom-written interactions. Most recently, we've experimented with client-side rendering of Mustache templates. None of these approaches is particularly robust or maintainable, and as a result we've ended up with a huge amount of fragile client-side code.
Luckily, we're not the only developers in this situation - every major web application faces the same challenges. As a result, a number of client-side frameworks have developed, applying common design patterns to build up a logical, maintainable framework for managing client-state and server interactions for web development. The problem is that everyone seems to have a solution, and that means navigating a number of projects to find a good fit.

Scoping

TodoMVC has evolved into a generic sample; implementing the same application (a todo list) in each framework. As an open project ( https://github.com/addyosmani/todomvc ), framework developers are free to add their own impelemntations, ensuring that the frameworks are represented by someone knowledgable of the framework. As a side-effect though, TodoMVC demonstrates how we’re spoiled for choice: 4256 different implementations are currently included.
Additionally, the Throne of JS conference in July 2012 gathered top developers representing a subset of popular frameworks. Videos of many presentations, as well as discussions among the developers, are available on InfoQ.
Based on the quality of coverage via those two primary sources, as well as critical mass of discussion, I narrowed the field to 13 implementations:
  • agility.js ( https://github.com/arturadib/agility )
  • AngularJS ( https://github.com/angular/angular.js )
  • Backbone.js ( http://github.com/documentcloud/backbone )
  • batman.js ( http://github.com/Shopify/batman )
  • canJS ( http://github.com/bitovi/canjs )
  • closure ( http://code.google.com/p/closure-library/source/browse/#git/closure/goog )
  • dojox/mvc (Dojo) ( http://svn.dojotoolkit.org/src/ )
  • ember.js ( https://github.com/emberjs/ember.js )
  • JavascriptMVC ( https://github.com/bitovi/javascriptmvc )
  • knockback.js ( https://github.com/kmalakoff/knockback )
  • Knockout ( https://github.com/SteveSanderson/knockout )
  • Spine ( https://github.com/spine/spine )
  • YUI/app (YUI) ( https://github.com/yui )

Incompatibility

While each framework has its own merits, and none is without some issue, a number of them were simply incompatible with our project, and were eliminated after a cursory analysis. In order to be considered compatible, a project needed to have a license that is compatible with both our SaaS offering, and our more traditional product. The project must at least tolerate, if not support well our dependency stack; specifically jQuery, server-side HAML or Mustache views, SASS, server-side Coffeescript, and of course Ruby on Rails. From a usability standpoint, a compatible framework is expected to have comprehensive API documentation, some guides and examples, be developed in an open manner where we can effortlessly contribute back, have some example of large public deployments, and be released regularly by and active development community. Beyond that, Studio has a particular style, and we attempt to adhere to a set of good practices; violation of standards or a failure to conform to our existing separation of responsibilities contributes to incompatibility as well.

agility.js

Agility centers around the unique concept of using a single factory to define a container for model, view, and controller objects. In order to accomplish this, all view elements are inlined strings; style is expected to be encapsulated within the view, and the view initialised as a single string of inline HTML, JS, and CSS. Although external view code can be implemented, this is outside the preferred behaviour for the framework.

AngularJS

Angular is a Google-sponsored project, although there is little evidence of its use: Closure, in contrast claims to be used in Google Search, Gmail, Maps, Docs, Sites, Blogger, etc., but this alone is not a reason to abandon the framework. The use of suggestion to use invalid HTML, on the other hand, is. Angular defines its own set of attributes and markup, which are processed by its JS library to provide browser-specific behavior. Fundamentally, though, AngularJS requires suggests you to write invalid HTML; although you can use HTML5 data- attributes instead of the custom attributes, using valid tags requires abandoning some of Angular's functionality.

backbone.js

Backbone, despite its apparent popularity, feels incomplete. Backbone, despite its description as an MVC framework, is generally viewed as muddying the responsibilities of the view and controller, and providing excessive leeway in template rendering and event handlers. This leads to a situation where another large, competing code ecosystem has developed, around building ‘backbone stacks’: collections of cooperating plugins that eventually build a complete MVC-ish framework. Although there is obvious merit in separating the responibilites and creating focus, the consequence is a lack of cohesive code. Being that we want a single solution we can stick with for the long term, and our satisfaction with “Convention over Configuration”, building a maintainable backbone stack seems to be more trouble than its worth. http://ianstormtaylor.com/rendering-views-in-backbonejs-isnt-always-simple/ http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/ http://dev.hasenj.org/post/35572197519 http://destroytoday.com/blog/reasons-for-spinejs/

Closure

Google’s closure, as noted in AngularJS, powers Google’s top apps. Closure, though, is more of a toolkit than a framework, providing widgets, libraries, and external tools. The heart of closure is the closure compiler a JS to JS compiler designed to optimize code performance for SPI (single page interfaces). Like agility.js, closure centers around writing all JS, leaving only a skeleton of HTML code (which I suppose is better than invalid HTML) but this leaves open the first-load performance gap, and ties the complete application to the compatibility of the JS, which is dependent on the compiler’s output. There is no direct correlation between the code a developer crafts, and the what the user runs in their browser.
The following article, although not entireley relevant due to age, demonstrates some of the issues with this abstraction: http://www.sitepoint.com/google-closure-how-not-to-write-javascript/ .

dojox/mvc

With a first release in 2005, Dojo has demonstated it longevity with ease, but it has failed to demonstrate an ability to adapt to the modern expectations of a library. Before diving into the functionality of this toolkit I ran into two blocking issue: Dojo has its own license terms. Although it appears to be a modified BSD license, the modifications are beyond what I can vouch for on behalf of my company, and would require legal review. On the flip side, Dojo is the only member of this framework that is not developed in a public git repository (they use svn), and requires one to sign and submit another license in order to contribute to the codebase.

JavascriptMVC

Bitovi maintains two projects on this list; they’re also maintaining canJS. And they’re working on doneJS “_the next generation of JavaScriptMVC._” DoneJS doesn’t appear to be usable, with the website completely devoid of documentation. Its existence, though, means that JMVC is approaching EOL.

Knockout

Knockout uses a similar view binding style as batman.js, applying bindings using an HTML5 data attribute. Unfortunately, knockout is another example of an excellent half-solution, covering only the view, the model, and bindings. Examples of persistence rely on JQuery Ajax calls, meaning that substantial code writing would be required just to do CRUD via REST. Additionally, Knockout enables JS functions inside its bindings, leading to some potentially ugly views, with poor separation of responsibility.

Suitability

Once the incompatible solutions are weeded out, it comes time to find the most suitable candidate. This is the most challenging part of the roundup, as it requires a deeper level of technical understanding of the frameworks, as well as an evaluation of the framework’s community, and an estimation of its relative opportunity to thrive.
At this point, I had identified some key differences in the frameworks, and was able to hone in on features that are more desirable:
  • Community: Is there a competent development core? Is there a larger contributing community, or does the core seem to develop in isolation. Are the project’s maintainers responsive via social channels?
  • Consistency: Does the framework natively use, or allow configuration of, full URL routing and view templates that are compatible with server- and client-side rendering?
  • Documentation: Are the APIs documented comprehensively? How are the ‘startup’ materials, such as guides, tutorials, or browsable examples?
  • Maturity: How long has the project been in development? Does it release regularly? Are the releases stable and well documented?
  • Publicity: Can the project cite large-scale examples where current releases are in production?
  • Rails: beyond the core requirement to tolerate Rails, how much philosphical and code compatibility was present? Did the framework favor Convention over Configuration? Was there a storage service that aligned well with Rails’ implementation of RESTful Resources?
  • Tolerance: Moving to a framework will be a gradual process. Does the framework coexist nicely with our existing code mix, or will we need to make some global changes in order to implement?
For each of these attributes, I assigned a score of 1 to 5; more points are better.

batman.js

Maintained by Shopify, release 0.14.0 of batman.js is in use throughout their user’s sites as part of the optional Shopify 2 Beta release, but no 3rd-party users are listed.
batman.js is unique among the offerings in having an established solution to server-side rendering. Using pushState() where supported (Chrome >= 5, Firefox >= 4, IE >= 10, Opera >= 11.5, Safari >= 5), along with pure valid HTML templating, the client is more likely to be in sync with the browser than with any other framework. View templates can be served via RESTful URLs, or extracted from a server-rendered page.
According to the API docs, and fleshed out by example “Batman’s API is heavily inspired by Rails and designed to make Rails devs feel right at home.” Rails resources work without additional configuration. Source is organized in parallel to the ‘app’ tree.
Community: 4 Consistency: 5 Documentation: 4 Maturity: 4 Publicity: 3 Rails: 5 Tolerance: 4 Overall: 29

canJS

Maintained by Bitovi, a JS-oriented consultancy, canJS 1.1.4 provides a lightweight MVC framework with Mustache or EJS-based templating, and a hashbang-oriented router. PushState() support is planned for version 1.2. CanJS has an emphasis on balancing size and speed with efficiency, with large sections of documentation dedicated to memory management and performance charting. One of canJS’ most interesting features is its flexibility; it is built to leverage either JQuery, Zepto, Dojo, MooTools or YUI, and a host of optional plugins to extend its core functionality. The tradeoff is more code; unlike its more Railsy counterparts, canJS requires defining routes and functions even for CRUD actions via REST. Conceivably the EJS templates could be used for server-side rendering, although there aren’t any concrete examples I could find of someone doing this with respect to this framework.
Bitovi’s developers are active in project forums, responsive to github issues, and seem to be regularly maintaining their examples and documentation, although the overall quality of the documents are not as robust as many of the other projects.
Although Bitovi lists a number of large-scale public projects, including customers such as Huawei and T-Mobile, they don’t specify if any of their open-source frameworks are in use on these projects.
Community: 4 Consistency: 2 Documentation: 3 Maturity: 4 Publicity: 1 Rails: 1 Tolerance: 5 Overall: 20

ember.js

With some big names developing it and a stable release looming after the current 1.0.0-RC1, ember.js clearly has mindshare. Rails core maintainer and JQuery contributor Yehuda Katz is pushing aggressively to make a JS framework worthy of MERB. Out of the box, ember.js is easily as opinionated as Rails and batman.js. Views are handled via 2-way binding against rendered Mustache templates; although the view code is robust and straightforward, server side rendering is only “something we’re working on.” In order to help mitigate the startup lag associated with all that client-side rendering, we would need to roll our own Mustache template compiler, but apps of any complexity should expect the typical SPI issue of a blank page, followed by the browser chugging on JSON and templates before finally rendering the page.
In response to some well-founded early criticism, both API documentation and entry-level guides are comprehensive and of high quality. Community involvement is equally robust, and maintainer presentations seem to always include github issue statistics.
If anything is of concern, it is the core idea that the framework will release ‘early and often’, so there is no guarantee that any release will be of better than beta quality. The reverse is that bugfixes should follow as rapidly, but it may be difficult to pin down a version for Onsite releases.
Community: 5 Consistency: 1 Documentation: 5 Maturity: 3 Publicity: 5 Rails: 5 Tolerance: 4 Overall: 28

knockback.js

Knockback is an example of a more complete backbone framework, depending on both backbone.js and knockout. Knockback is interesting in that it takes the simplest possible approach to satisfying missing components of both frameworks; its largest drawback is simply that it relies on two independent frameworks, in addition to its overlay library, resulting in a large footprint. Ultimately, neither backbone’s routing, nor knockout’s views justify the bloat.
Because of its duplicitous nature, some of the projects attributes, like the community scope, could be attributed to both underlying projects. But, since the core concept is to provide an overlay that joins disparate parts, that’s the part I used for scoping. As a result, knockback has an understandably small audience.
Community: 2 Consistency: 2 Documentation: 2 Maturity: 3 Publicity: 1 Rails: 2 Tolerance: 4 Overall: 16

Spine

Spine builds on backbone.js’ controller API, but provides a leaner implementation of a full MVC stack. Proudly weighing in under 500 lines of CoffeeScript, Spine is the epitome of minimal JS. What is lacking in the framework, Spine’s developers attempt to make up with Rails integration that includes comprehensive code generation and scaffolding; although each function must be defined in a Spine controller to correspond to CRUD via REST, the complete basis can be generated on the command line in a single shot. There maybe some additional maintenance, but the core core patterns are provided simply, while still providing complete flexibility.
Where Spine really diverges from other platforms is the developer’s insistence on a responsive UI, to the complete ignorance of actual data transactions. UI updates always happen immediately, with server transactions queued asyncronously. The assumption is that the server is highly reliable, and that any necessary validation should me mirrored in both the client and server, so that submissions are always valid. The general design pattern for handling an (expectedly rare) server failure, is to prompt the user to refresh. There’s an interesting presentation from Throne of JS diving further into the concept.
Spine has a subtle leaning towards the obscure Eco templates, which would be ideal on a node.js application, but would not be immediately usable via Rails. Additionally, spine is very opinionated about doing all template rendering client-side with no concept of, nor intent to support, server-side rendering. This is only tolerable because of spine’s rediculously small footprint, but a large data payload is still going to impact startup time on an SPI.
The documentation is comprehensive and clear, as would be expected since the framework was developed in parallel to O’Reilly’s Javascript Web Applications.
Community: 2 Consistency: 1 Documentation: 5 Maturity: 4 Publicity: 3 Rails: 3 Tolerance: 5 Overall: 23

YUI/app

YUI/app provides an ‘MVC-like’ framework by layering the Model, Model List, Router, and View YUI components. The framework builds on the wealth of JS knowledge in YUI, with a strong emphasis on compatibility and progressive enhancement. For example, whereas batman.js uses PushState(), but falls back to hashed URLs, YUI/app uses the YUI PJAX library, which falls back to full-page rendering, maintaining the full URL. YUI/app appears to be the only framework with a legacy support mindset; documentation includes comprehensive explanations of different routing techniques, when they will work, and how to gracefully fallback. Core support for loading HTML partials from a server is a nice middle-ground for both progressive enhancement and performance balancing.
All the frameworks fall into two categories with respect to server interaction: either they are Rails-oriented, and include CRUD via REST by default, in a way that is compatible with Rails resources, or every action must be coded from scratch. YUI incorporates both, providing a REST implementation that can be mixed into a Model, or the ability to override the sync() function for complete control.
Although YUI may not have the mindshare of popular frameworks like ember.js, it certainly has proven its maintainability in the long run, with a strong commmunity and exteremely talented maintainers, such as Douglas Crockford.
Community: 4 Consistency: 4 Documentation: 5 Maturity: 5 Publicity: 3 Rails: 3 Tolerance: 3 Overall: 27

Recommendation

Of course the numbers bear it out, I recommend batman.js as the framework best suited to Studio. It is a stable, forward thinking framwork, including all the expected components while maintaining compatibility with existing standards.
Although the best feature maybe one I haven’t mentioned: we’ve already started using it. AndrĂ©, Cassio, and myself were introduced to the framework during our work on Dashio, which is built with batman.js in the client, and Sinatra on the server.
Ember.js is still a serious project to watch, but in general the team hasn’t been particularly receptive to using Mustache templates, and ember is nothing if not opinionated about such matters.
YUI/app stands out as a great option as well, if we want to start taking progressive enhancement seriously. Long-term viability is definitely not a concern in this project.

22 comments:

  1. "Fundamentally, though, AngularJS requires you to write invalid HTML."

    Incorrect. you may append "data-" to angular directives making them perfectly valid HTML5

    ReplyDelete
  2. Prepending your directives with "data-" should make them valid HTML and properly execute Angular directives. Angular's compiler automatically strips out the "data-", which are valid HTML

    ReplyDelete
  3. AngularJS doesn't require you to write invalid HTML, even though its examples use custom vocabulary. You can use classes or valid data-* attributes instead, therefore allowing it to be valid to the spec and work in older IE.

    Personally I like writing custom elements that Angular handles, it means less reliance on traditional class/id schemes and can allow very clean markup and encapsulates behaviors. Call me crazy, but I'm tired of javascript using classes and ids to operate, its hard to manage on a large project.

    ReplyDelete
  4. "Fundamentally, though, AngularJS requires you to write invalid HTML."

    Not true. If the custom HTML attributes, bother you, you can always write your directives as "data-ng-*" instead, which are valid in HTML5.

    ReplyDelete
  5. Ah... the Angular fans are the first to flame!

    I'm aware you *can* write data- attributes for Angular, but it doesn't seem to be the recommended or suggested method. All the primary examples use *invalid* ng- attributes, and made-up tags.

    I could be more long-winded, about how Angular doesn't play nice with existing jQuery code ( see 'common pitfalls' on http://docs.angularjs.org/misc/faq ) or how Angular, like many others, just fails at server-side rendering and SEO due to the total dependency on #! urls... but why bother? Angular just isn't what we're looking for

    But thanks for clarifying.

    ReplyDelete
    Replies
    1. I'm not trying to 'flame' here, just respond. I use jQuery frequently without issue in my Angular application, I just use it in a way that works well with Angular (not in controllers). Angular also uses the Sizzle engine (from jQuery). Also, #! urls are only for older browsers that don't support HTML5. My application is routed on the client and server side, so its entirely possible for a crawler to navigate the site.

      I'm glad you found your framework, I'll be taking some time to look at it myself. I agree with IZZYOREO, its a major change in mindset that is perhaps the biggest downside.

      Delete
  6. See, those would be far more valid comments to make than something that's actually incorrect.

    ReplyDelete
  7. I think what people were protesting was the implication that you didn't even review Angular because of the "invalid HTML" issue.

    As far as I'm concerned, the main downside to Angular is the major mindshift required to move from a jQuery-based mindset to a directives mindset. Other than that, it's been an absolute pleasure producing highly functional sites with amazingly small codebases.

    PS., As you can see, the Angular community is anything but apathetic.

    ReplyDelete
    Replies
    1. You are quite correct. Coming from a traditional jQuery background, the mindset change is the main hurdle to overcome which I am still trying to do. Whereas Backbone slides into your Rails project without any friction at all. Downside is of course, you are writing a lot of code than you would with Angular. For me, it boils down to code organization and maintainability. How easy is it to track down the unexpected behavior and debug. I was hoping that the author would address those issues in depth.

      Delete
  8. None of the above comments were flaming, they all quoted and corrected the mistake in your article. You should be aware that people new to these frameworks will read your post and take it as gospel. I'm glad you found the right framework "for your use case", but your poorly researched article and your spikey response to the community helping you fill in the gaps has lost me as a reader.

    ReplyDelete
    Replies
    1. The 'flame' reference was meant to be light-hearted. Apologies if anyone was offended. Mostly, it was amusing that the first four comments all said the same thing, about the same framework.

      I have to say, though, my decision was far from "poorly researched": I spent the better part of two weeks reading, trying, digging, googling, watching, and thinking. The fact that every detail isn't here has more to do with what one can actually expect people to read in a blog post that how I came to my conclusion.

      Delete
    2. If you did spend a good two weeks "researching" the various frameworks and did infact invest time with Angular, could you please update the blog with something more pithy than saying "it suggests you use invalid HTML".

      As an aside, if you dont use custom tags in angular you dont lose any functionality. In fact there is no inherent functionality in Angular that requires you to use custom html tags. Attributes - you have to use, but tags - No. This is the second thing you have suggested about Angular that is actually wrong. Really dont mind you not using Angular, but misrepresenting it is probably that the community feels intolerable.

      Delete
    3. > if you dont use custom tags in angular you dont lose any functionality.

      From http://angularjs.org:

      Directives is a unique and powerful feature available only in Angular. Directives let you invent new HTML syntax, specific to your application.

      Delete
    4. I think you are under the assumption that directives == custom tags. A directive can be one of 4 types. An element directive ( ex: ), an attribute directive ( ex: ng-click ), a class directive ( you can use classes as directives as well and finally a comment directive.

      Delete
  9. In the interest of fairness (and not having to hear about data-ng-* attributes ever again) I've updated the article to reflect the fact that AngularJS only *suggests* you write invalid HTML, but does not *require* it.

    If you still think this is a gross misrepresentation of the project, please add some data-ng-* attributes to the samples on http://angularjs.org/ .

    ReplyDelete
    Replies
    1. First, let me thank you for such an AMAZING article. I wish this was the first thing that Google pop-up when searching for Javascript MVC frameworks. It's probably the most important article I've read on the subject so far.

      I'm going to take another look at Batman (though that implies Coffeescript) and I wish EmberJS were little further ahead in their development.

      But I've ended-up settling on AngularJS for my project so I've spent a fair bit of time on their docs. As good as those docs look there are some atrocious holes in them. Most of the examples are quick'n'dirty that mis-lead developers and mis-represent the framework. HTML attributes are just one issue; creating and using services and controllers is another issue.

      I'm still confident in my choice of AngularJS but I know I'm going to struggle through the docs trying to match-up what they say with they do.

      Thanks again for the awesome article.

      Delete
  10. Thanks for the write-up!

    I'll definitely be consulting this next time I start a javascript-heavy project.

    ReplyDelete
  11. I'm tickled pink Angular is working for all of you.

    I have to say, it looks really cool. But, its just not something we'd use in SUSE Studio.

    I'm sure a thousand other people will have a thousand other ways of describing how I've slighted or misrepresented their project... and I totally expected that. It's a blog post, not a term paper - there's just no way to comprehensively describe everything about everything.

    That being said, the effort I put into making a responsible decision for my project wasn't one I took lightly. I'm sorry folks seem to think so, just because I tried (and slightly failed) to write a 3K word summary of 80 hours of JS framework madness.

    ReplyDelete
  12. Small quibble, but Ember.js actually uses Handlebars.js which I find immensely preferable to Mustache because it's a superset that includes simple flow control and provisions for adding new helper's to its vocabulary.

    Also, I believe there is an existing project to handle Handlebars.js templates within Rails too.

    We didn't actually use Ember.js for our last project, we used Backbone.js but we paired it with Handlebars.js and I think everybody liked it. It was probably the one thing for which I heard few or no complaints.

    ReplyDelete
  13. What about Durundal? http://durandaljs.com/ It uses KnockoutJS in more modular way and it is less opinionated than AngularJS.

    ReplyDelete
  14. And if you use Rails, you could try that one http://joosy.ws/

    ReplyDelete
  15. HTML Attributes are property of the elements which may have values and these attribute values are always enclosed in quotes. It’s providing to the browser with some additional information about an elements how the elements should appear or behave. HTML elements can contain one or more attributes, attribute names and attribute values are case-insensitive and separated by an equals (=) sign.

    [HTML Attributes](http://www.willvick.com/HTML/HTMLAttribute.aspx)

    [HTML Attributes Examples](http://www.willvick.com/HTML/HTMLExampleAttribute.aspx)

    [Youtube - HTML Tutorial - Attributes](http://www.youtube.com/watch?v=ucOXvaCEZgg)

    ReplyDelete

 
© 2013 SUSE