Backbone Antipatterns

Backbone Antipatterns

200px-Skull_and_crossbones

Backbone.js deserves a lot of credit for bringing MVC to mainstream client-side Javascript development. That said, many beginners ask what the ‘right way’ of doing something with Backbone is. The bad news is that there’s not necessarily a ‘right way’ – it all depends on the problem you are trying to solve. The good news is that there are definitely some ‘wrong ways’ that you should avoid on your way to finding the right solution for your particular problem.

In this post I’ll cover some of these anti-patterns, as well as some general advice for the new starter. I’ve ordered the anti-patterns roughly by significance from the major to the more trivial. Don’t be too upset if you’ve done something on this list – I’ve made most of these mistakes myself 😉

Antipattern #1: Building a single page app when you don’t need to

This first one doesn’t really pertain to Backbone specifically, but it’s worth mentioning up-front: If you’ve decided to build a single-page app, make sure you’ve got a pretty good reason to do so.

At first glance, the concept can seem appealing: instead of having to assemble fragments of HTML and Javascript on the server-side, just define a JSON API and do everything on the client-side with Javascript. However, there’s a couple of things you need to be wary of.

Firstly, if your app is using some sort of database as the backend (highly likely), a JSON API is effectively just another layer of indirection between your UI code and that database. From a development perspective, it’s much easier and more flexible for UI code to live on the server and be able to make direct SQL calls to your database rather than have to define and then implement a JSON API. 

Secondly, building a single-page app is just plain hard, especially if your background is in building traditional request-response apps with frameworks like PHP or Rails. SPAs are all asynchronous and event-based, and objects can live for a long time. This is very different from the synchronous, short-lifecycle approach of old-school web apps.

Finally, it’s worth noting that just because you’re using Backbone (or Angular, or many other Javascript MVC frameworks) you don’t have to commit to building a full single-page app. Perhaps you can just use it for those parts of your app where the UX demands it. For example, 37Signals implemented the new Basecamp calendar with Backbone, without making all of Basecamp a single-page app.

Antipattern #2: Using Backbone When You Should Use Another Framework

The Javascript MVC framework you choose for a project is one of the most important decisions you’ll make, and Backbone is by no means the best choice for all projects. For more information check out my prior post on why Backbone Is Not Enough for many Javascript-intensive applications.

Antipattern #3: No View Tests

Backbone Models are reasonably straightforward to unit test. Backbone Views can be a little more tricky – but this doesn’t mean you shouldn’t do it.

In addition to the well-understood benefits of unit testing (i.e., code quality), writing unit tests for your views forces you to think about how your code is structured. Because it is so easy to set up interdependencies between views via global variables and undocumented properties, un-tested code will tend gravitate towards a state where there are no clearly defined units of code. This is a bad thing.

For more information on testing Backbone Views, check out my previous post on Testing Backbone Views with QUnit and Sinon.

Antipattern #4: No Memory Management

A classic Backbone beginners mistake is to not think about memory management. As a consequence, in no time at all an app can be leaking memory like a sieve and triggering a blizzard of events whenever anything happens.

‘How can this be?’, the novice ask, ‘my code is super tight and Javascript is supposed to be garbage-collected’. Well unfortunately there’s one source of memory leaks that Javascript can’t pick up: views that continue to listen to their models for events, even when those views are no longer being displayed. Views stay in memory because models still retain references to them, and this pool of ‘zombie views’ grows over time.

Fortunately this is a well-understood problem, for which there are a variety of solutions. Whatever you do, an important first step is to use Backbone’s listenTo method when subscribing to events rather than using the ‘on/bind’ methods directly. At least that way, the subscriber can keep a list of what its listening to. However, it’s still up to you (or your view-management framework – we often use Backbone LayoutManager) to get your views to stop listening to events when it is no longer being used.

Antipattern #5: Data Attributes in the DOM

The next anti-pattern is probably the most common thing I see those new to Backbone do – particularly if they’re coming from a jQuery background. It’s probably best demonstrated with an example.

Say that we want to display a list of people, and respond when an item in the list is clicked. The models will look something like this:

var Person = Backbone.Model.extend();

var People = Backbone.Collection.extend({
  model: Person
});

Now let’s get to the problematic part. Firstly, the template (assuming we’re using Underscore templates):

<script id="people" type="text/template">
  Here is our list of people:
  <ul>
    <% people.each(function(person) { %>
      <li class='person' data-id='<%= person.id %>'>
        Hi, my name is <%= person.get('name') %>
      </li>
    <% }); %>
  </ul>
</script>

Things starting to look fishy? Now the view:

    var PeopleView = Backbone.View.extend({
      events: {
        'click .person': '_personClicked'
      },
      render: function() {
        var renderTemplate = _.template($('#people').text());
        this.$el.html(renderTemplate({people: this.collection}));
      },
      _personClicked: function(e) {
        var personId = $(e.currentTarget).data('id');
        var person = this.collection.get(personId);
        $(e.currentTarget).text(
          'Nice to meet you, ' + person.get('name')
        );
      }
    });

What’s the problem with this? Well, inserting data into the DOM and then pulling it out again kind of runs against the grain of Backbone and tends to box you in as your UI gets more complex.

Instead, we’d be better-off leveraging everything Backbone gives us for associating portions of our DOM with data. Given that there’s a one-to-one correspondence between each Person model and their <li> row in the DOM, we might as well capitalise on that by having a view that mediates between the two of them.

Let’s change our people template to be:

&lt;script id=&quot;people&quot; type=&quot;text/template&quot;&gt;
    Here is our list of people:
    &lt;ul&gt;&lt;/ul&gt;
&lt;/script&gt;

and create another template called dedicated to displaying a person:

&lt;script id=&quot;person&quot; type=&quot;text/template&quot;&gt;
  Hi, my name is &lt;%= person.get('name') %&gt;
&lt;/script&gt;

Then define a view for the Person model:

    var PersonView = Backbone.View.extend({
      tagName: 'li',
      events: {
        'click': '_clicked'
      },
      render: function() {
        var renderTemplate = _.template($('#person').text());
        this.$el.html(renderTemplate({person: this.model}));
        return this;
      },
      _clicked: function() {
        this.$el.text(
          'Nice to meet you, ' + this.model.get('name')
        );
      }
    });

and have the PeopleView render this into place:

    var PeopleView = Backbone.View.extend({
      render: function() {
        var renderTemplate = _.template($('#people').text());
        this.$el.html(renderTemplate({people: this.collection}));

        this.collection.each(function(person) {
          this.$('ul').append(
            new PersonView({model: person}).render().$el
          );
        }, this);
      }
    });

No more data attributes, no more event targets. This also makes testing easier, as you can now unit test a PersonView in isolation.

Put bluntly, don’t be shy about having lots of views. To be honest, it’s very rare for me to use iterators in templates, unless I’m rendering purely read-only data. I consider data attributes to be a code smell that will cause problems down the track.

Antipattern #6: Rendering Templates Asynchronously

Frameworks like Backbone.LayoutManager make it easy to load templates asynchronously. However, it’s easy to underestimate how much asynchronicity complicates both your code and your tests – it should be avoided unless absolutely necessary. And a lot of the time, asynchronous template loading is unnecessary.

This is because in production environments it’s unlikely that you’ll be loading templates individually across the network – they’ll probably be precompiled and concatenated into your application Javascript file. Consequently, they’ll never actually be be loaded asynchronously.

This leaves your local development environment as the only place that you’d want to load templates asynchronously. But funnily enough, if you switched to synchronous loading for local development, you probably won’t notice much in the way of a performance degradation, as all your synchronous calls will just local.

So this eliminates the only rationale for loading templates asynchronously.  So why do it at all? For many of my projects, I’ve switched to using the ‘async: false’ option in the Ajax calls that fetch templates in dev. My code was able to become much simpler, and things didn’t get noticeably slower during dev.

If you don’t want to compile all your code into a single artefact, you can still break it into chunks and load those separately. However, each chunk should comprise both the code _and_ compiled templates relevant to a section of your app. Loading templates as they are needed is probably too fine-grained an optimisation and will make your app excessively chatty, not to mention harder to code.

Antipattern #7: Undocumented Options

Backbone Views can be initialized with an ‘options’ object, which can contain just about anything. The options object is extremely handy, because it means you can pass anything into a view via options. However, it’s also very easy to abuse, because options don’t have to be declared anywhere (for example, as arguments to the initialize() method).

In earlier versions of Backbone, this object would automatically be bound to this.options. In Backbone 1.1, the situation improved slightly because the automatic binding doesn’t happen anymore. However, it’s still easy for somebody to do the binding themselves.

Either way, the consequence is that if you didn’t write the code, you often only find out about an option when it got used deep within a class. For example:

  var MyView = Backbone.View.extend({
  initialize: function(options) {
    this._options = options;
  },
  // … lots more code
  doStuff: function() {
    // ... do a bunch of stuff
      this._options.obscureBackdoor.doSomethingCrucial();
    // … do a bunch more stuff
  }
  // ... do more stuff
});

Nobody will know that the view has a dependency on an obscure backdoor until they find the options reference deep within the code.

This is unfortunate because each option object provided to a view is effectively a coupling between the view and that object. Put differently, the options that a view takes constitute part of the public interface of that view.

Consequently, I highly recommend that all options that a model or view uses be documented somewhere in the class or initializer declaration (if there is one). It can look as simple as this:

/**
 * options: 
 * - obscureBackdoor: a now slightly-less obscure backdoor
 */
var MyView = Backbone.View.extend({
  ...
});

If you’re feeling particularly diligent, you can document which options are mandatory and which aren’t actually required. Or you can even check for the presence of mandatory options in the initialize() method of your object. Either way, if you don’t document options, there’s a good chance you’re going to end up with spaghetti code that’s full of hidden backdoors.

Antipattern #8: Premature Use of Custom Events

It’s common for new starters to Backbone to get excited about the fact that just about everything can emit custom events. Consequently, they start to add their own custom events.

I have a couple of issues with this.

Firstly, the events that an object emits constitute a part of the public interface for that object. So if you’re going to have something emit custom events, it’s really important that you document that it can do so. Otherwise, you’re effectively adding backdoor coupling that is difficult for those unfamiliar with the code to see.

Secondly, often custom events are added under the pretence that it ‘decouples’ the code, but the fact remains that if some required behaviour is implemented by having two objects interact via custom events, then those objects are still coupled – just with an extra level of indirection.

In the case of view interaction, I personally have no issues at all with two views having references to each other – as long as those references are well-documented (see anti-pattern #7). This is simpler and easier to test.

The most useful events are those that are well-defined, globally understood, and potentially of interest in more than just one or two special-cases. The existing Backbone built-in events (http://backbonejs.org/#Events-catalog) meet these criteria. They can take you a long way if you fully leverage them.

For example, one strategy that maximises the use of Backbone’s built-in events is to coordinate changes between several views via the underlying models that are shared by those views. One view could trigger a change to a model, which makes an update to another model, and that change is then picked up by another view that is listening for change events.  For complex views, introducing a tailor-made view-model can also be handy, and also make testing easier. Neither of these strategies require the introduction of custom events in the first instance.

The only place I’ve found custom events to be useful is in views that you want to reuse through your apps – for example, UI components like tabs or accordions. Custom events are the more flexible way for people to detect what a component is doing so that they can customise its behaviour for a particular scenario. Just make sure that these custom events are well-documented!

Antipattern #9: Building a Relationship Mapper

On non-trivial apps it’s not uncommon to want to deserialize nested JSON data structures from your server into a tree of Backbone models and collections, then potentially re-serialize that tree (or portions of it) for display in templates or for sending back to the server.

One way to do this is to override the parse and toJSON methods on your models and do it yourself manually. However, next you want to lazily load certain portions of the tree, so you put in special logic for that. Then you get sick of duplicated code in parse and toJSON, so you try to write a mechanism for specifying your relationships declaratively instead. Then you find that an object with the same ID and type is appearing in different parts of your JSON, and you want it to be represented by the same model instead, so you need to implement some sort of identity map.

Before you know it, things are getting out of hand and you’ve found yourself writing a pseudo-object/relational mapper. This is something your should avoid at all costs – object/relational mapping is something that seems straightforward at first but is littered with nasty edge-cases.

Fortunately, there are a number of Backbone frameworks out there that do this sort of stuff for you already. Backbone-Relational is probably the most heavyweight. It’s very powerful and includes an identity map, but – like traditional O/R mappers – you can easily shoot yourself in the foot if you don’t make yourself familiar with the subtleties of how it works. Backbone-Associations and Backbone-Nested are more lightweight and may be sufficient for your needs.

Whatever you do, make sure you check them out before you do it yourself.

Antipattern #10: Redundant Divs

This is a minor one, and opinions can differ on it. However, I think it’s worth mentioning because a lot people don’t even realise it happens.

When rendering templates, remember that by default Backbone creates an empty div element for each Backbone view. This can lead to a lot of redundant divs in your generated HTML.

Here’s an example. Consider a templates like this:

&lt;script type=&quot;text/template&quot; id=&quot;section&quot;&gt;
  &lt;section class='myClass'&gt;
    This is a section
  &lt;/section&gt;
&lt;/script&gt;

With a view like this:

var View = Backbone.View.extend({
  render: function() {
    this.$el.html(_.template($('#section').text())());
  }
});

So when you render the view, it looks like this:

&lt;div&gt;
  &lt;section class=&quot;myClass&quot;&gt;
    This is a section
  &lt;/section&gt;
&lt;/div&gt;

Those redundant divs really add up as your DOM gets more complex.

The thing is that Backbone lets you specify details about the root element of a view, including what the element type should be. So instead, you could do this for the template:

&lt;script type=&quot;text/template&quot; id=&quot;section&quot;&gt;
    This is a section
&lt;/script&gt;

And provide more details about the root element in the view:

var View = Backbone.View.extend({
  tagName: 'section',
  className: 'myClass',
  render: function() {
    this.$el.html(_.template($('#section').text())());
  }
});

When rendered, the view will look like this:

  &lt;section class=&quot;myClass&quot;&gt;
    This is a section
  &lt;/section&gt;

Much better. I used to not really understand why they’re called ‘Views’ rather than ‘Controllers’, but now I think I understand. I consider it a code smell if I see a template with a single root element. Use everything that Backbone gives you, and don’t balk at putting a little bit of view-related stuff in your View objects – that’s why they’re called ‘Views’, after all.

Some Final Advice

That sums up my top ten Backbone anti-patterns, ranging from the fundamental to the finicky. However, I appreciate that being told what not to do still leaves a very large set of possible things you could do.

So if I had to give one last piece of guiding advice to the fledgeling Backbone developer, it would be this: always start with the UX and work backwards from there. By extension, this means working backwards from your templates and Backbone Views before getting too bogged down trying to use fancy across-the-board architectural approaches. Don’t be afraid to have lots of view classes and – where necessary – link between them.

That said, you should also unit test and refactor extensively as you go. Duplicated business logic in your views? Push it down into your models. Views still having to know to much about when models are being updated? Start using event listeners on the models so the views don’t have to know in advance when they’re going to change. Views getting too big? Break them down. Views still too complicated? Introduce an intermediate layer of view-models.

A continual code/test/refactor cycle (or test/code/refactor cycle if you’re that way inclined) will usually leave you with just the right degree of design. If possible, code reviews also help a lot too. Finally, make sure that you make yourself familiar with everything that Backbone and its plugin ecosystem gives you, rather than accidentally reinventing the wheel yourself.

If you’ve got some of your own Backbone Antipatterns, I’d love to hear about them!

Tags:
ben.teese@shinesolutions.com

I'm a Senior Consultant at Shine Solutions.

17 Comments
  • Jonathan Kemp (@jonkemp)
    Posted at 01:05h, 28 November Reply

    How do you test views then if templates are not in separate files? Otherwise, you have to copy the templates into the test code, so if any updates are required, you have to update them in 2 places.

    • Ben Teese
      Posted at 08:40h, 28 November Reply

      Hi Jonathan,

      I’m not 100% sure which antipattern you’re referring to: I’m going to assume it’s the one about loading templates asynchronously.

      In unit tests, templates still get loaded from separate files, it’s just that those files get loaded synchronously. However, because the unit tests are (hopefully) running on the same machine as the templates, the synchronous template loading doesn’t cause any noticeable slowdown in the speed of the tests. It’s only for production builds that the templates get precompiled and bundled up with the rest of the Javascript.

      Let me know if this isn’t what you were referring to.

      Cheers,

      Ben

  • Jeff Roberts
    Posted at 06:08h, 14 December Reply

    Hi Ben, my question is regarding javascript code management when not going the SPA route. I have a a pretty large php app that I have been adding more and more javascript to. At the moment it has a dozen backbone models and a few more views than that. Not all models and views appear in every page but many of them are shared. You mention not to go SPA when it is not necessary but I am not finding a lot of resources on organizing and optimizing javascript code when it’s not an SPA. Could you share your thoughts on this? Many thanks, this was a great article. -Jeff Roberts

    • Ben Teese
      Posted at 11:15h, 16 December Reply

      Hi Jeff,

      For an app that isn’t an SPA, the server-side framework that you are using may give you some sort of way to automatically concatenate and minify JS both during development and for production builds.

      For example, in Rails you get the Asset Pipeline; in Java-land you’ve got things like wro4j. I don’t know much about the PHP space but I’d hope there’d be something similar there too. Many of these frameworks work just fine with a codebase organized using a namespace rather than anything fancy like AMD modules.

      If your PHP framework isn’t helping you could probably start using Grunt to assemble and concatenate all your Javascript yourself. Tasks like grunt-watch can help during development by automatically packaging everything up whenever it changes.

      Hope that helps,

      Ben

  • mikermcneil
    Posted at 07:00h, 14 December Reply

    This is an awesome overview. What a lot of folks just getting into Backbone don’t realize is that it’s more of a utility belt than a framework (much like jQuery).

    @ghernandez345 and I recently rewrote the library we call “Mast” (if you’re curious, google ‘mast backbone’). It sits on top of backbone and focuses mainly on style conventions, view/memory management, and HTML data bindings. Curious to hear your thoughts on the approach. This time, we shot for building features as layers on top of the core Backbone internals, so that there’s never a huge “magic” jump.

    Anyways, thanks for writing this 🙂

  • bruno-c
    Posted at 08:14h, 14 December Reply

    One thing that I would consider an anti-pattern and that I have seen a lot in novice Backbone users is creating a new instance of the model inside the view (usually in `initialize`), rather than specifying it as an option when creating the view instance.

  • Sergiy Lytvynenko
    Posted at 18:19h, 14 December Reply

    RE: Antipattern #5: Data Attributes in the DOM
    In your example it looks like we have no reference to PersonView, so when PeopleView will be removed all PersonView instances become zombie-views. Or i missed something?

    • Ben Teese
      Posted at 11:43h, 16 December Reply

      Hi Sergiy,

      Generally you only get zombie views when something reachable retains a reference to a view that you assumed was unreachable.

      As far as I can tell, the only things that are retaining references to the PersonViews (and thus causing them to be kept in memory) are the click event handlers on each li. If you were to pull the root element for the PeopleView out of the DOM and make it unreachable (ie, retain no other references to it), then all of the li tags would also become unreachable, which in turn means that the PersonViews are no longer reachable. Consequently, everything will be garbage-collected.

      In my experience the most common cause of zombie views is when a view subscribes to events from a model, and never unsubscribes. Even if you remove the view’s root element from the DOM, the model object still retains a reference to the view (so that it can send it events). Consequently, if the model remains reachable (which is not uncommon – models often have a longer lifespan than a view) then the view also remains reachable and will not get garbage-collected.

      Cheers,

      Ben

      • Sergiy Lytvynenko
        Posted at 19:10h, 16 December

        But i’m pretty sure that you’ll want to bind to some models or collections sooner or later. As i understand listenTo is useless if you not calling View.remove() end just remove view element from DOM.

      • Ben Teese
        Posted at 08:23h, 17 December

        I agree.

        In my example, I wasn’t calling remove on the PeopleView or any of the PersonViews because they constitute the entire app – they are never having to be swapped-out.

        If they were having to be swapped-out, I’d call remove on them as a matter of habit (even if they weren’t actually listening to any models) to avoid any memory leaks in the future.

  • Mr Steve
    Posted at 10:19h, 16 December Reply

    Is it reasonable to assume that Antipattern #5 depends on the amount of data being rendered? For example, would it not be better to use data-* attributes when rendering 1000 records as opposed to a new view for each of those records?

    • Ben Teese
      Posted at 11:52h, 16 December Reply

      Hi Steve,

      If the preferred approach I outlined in Antipattern #5 led to performance issues, I’d definitely be open to alternatives – including the introduction of data-* attributes. However, I’d be very reluctant to speculate what the threshold should be in advance, or what the exact cause of a possible problem could be.

      Cheers,

      Ben

  • andrewzmh
    Posted at 22:29h, 17 December Reply

    I have a doubt about Antipattern #10: Redundant Divs . why don’t backbone just use the root of the user’s root html element as the view’s root. wrap a div outside the element has any benefit?

    • Ben Teese
      Posted at 08:28h, 18 December Reply

      Not fully sure if this will answer your question, but to be clear: when I talk about the ‘root element’ for a particular view, I mean the top-level element for the portion of the DOM that the view is attached to, not the root element for the entire DOM.

      That said, it is indeed possible to specify a root element in advance for a view rather than having the view create its own root element:

      var $someElement = $(‘…’); // Select some element
      new Backbone.View({el: $someElement}); // Make it that root element for a view

      However, in my experience this is not common. More commonly, views create their own root elements.

  • bigmac
    Posted at 09:23h, 19 December Reply

    Fantastic writeup. I whole-heartedly agree with all points, although maybe in a different priority order. The only other major one that comes to my mind that I see very frequently is building a stateful application around a Router. If your application needs to manage state, do it with a designated model. Routers should simply be another I/O channel, much like buttons in the display or keys on the keyboard. A router should simply pass-through route data, and update the URL to reflect internal state… However, you should still have a fully functional app without in stanching a router.

    • Ben Teese
      Posted at 08:08h, 20 December Reply

      I agree with your concerns about routers, however I didn’t make an antipattern because I couldn’t clearly articulate my reservations at the time.

      I have seen problems where people make parts of their app route-driven for no good reason. This causes issues because the router is effectively a global object, with all of the attendant issues that come with globally accessible objects – hard to test, gets bloated, ‘god-class’ tendencies. Doing routes properly is hard. By extension, doing everything with routes makes everything hard.

      I think it’s important to have a clearly defined policy as to what’s going to have a route and what’s not. In many ways what’s routeable is a UX question first – what needs to be bookmarkable, what needs to be navigable using the back-buttons, what needs SEO – and only secondarily an architectural issue. In my opinion, many people get this around the wrong way.

  • JT
    Posted at 02:15h, 13 January Reply

    I think most of these are really good points, but I can’t agree that it is ever OK for two views to know about each other. That’s a code smell to me. Parents should know about children, children should not know anything about siblings or parents.

Leave a Reply

Discover more from Shine Solutions Group

Subscribe now to keep reading and get access to the full archive.

Continue reading