Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Graham Siener

Feature hydra: how many heads does your product have?

Graham Siener
Monday, April 29, 2013

Should a web and iOS project have one or two tracks/teams/IPMs?

I posted that question to our internal Q&A forum a few months ago.  We were kicking off a client on a large project, building multiple applications for web (incl. mobile) and iOS. We expected some disparities in functionality between the two but assumed the iOS client would be a subset of the website. The client dedicated a PM for each track and there were many shared resources across both projects (designers, UX, backend, other in-house integrations).  One interesting wrinkle that would be a driving factor: the web would be a backbone app driven by an API that another team was in the process of developing.

I was fortunate to get a variety of viewpoints from Pivots that had been through similar projects (hat tip to Onsi for his great answer that influenced this post!).  My hypothesis was that we should stick to one IPM, one standup, etc. and effectively stay as one team as long as possible.  Three months in, I wanted to share an experience report on what has worked and where we’re still improving.

Clear Roles

Having a PM for each platform meant we could effectively manage the user experience specific to the device.  Mark Pincus pushes for everyone to be the “CEO of Something” and this structure ensured that the PM was not a bottleneck as our development team ramped up to six pairs.  Having one designer meant our UI and UX felt consistent across platforms.  Do not take for granted that a user transitioning from one device to another will be disoriented by an inconsistent experience.  Be careful to watch out for communication issues between PMs, and make sure they’re both describing mutually compatible products. It’s possible to specify a feature in a way that’s subtly incompatible between web and iOS — try to catch these issues early!

Group Discussion

We started off with one IPM but this grew unwieldy (talking through, and pointing out, six pairs worth of work just takes time).  We decided to split into two IPMs but the anchor from iOS joined the Web IPM and vice versa.  We also ensured that these anchors were part of a pre-IPM process that delivered the broad themes and saved the larger IPM audience from unimportant decision points.

Own the API

As I mentioned, we started off with the expectation that we’d develop our front ends while another team worked on an API to expose their backend.  It only took a few weeks to realize this would create a lot of churn and we absorbed a layer of API that let us iterate quickly and optimize endpoints for iOS.

The web is an agile-friendly platform where rapid deployment is possible. iOS, thanks to Apple’s review process, is not. Tying minor tweaks on the web client to a (potentially non-backward compatible) full deploy of the API will almost certainly land you into trouble. You have to think carefully about backward compatibility with the iOS app; imposing a clear separation between the web client and the Rails API really helps.  Have (API-level) integration tests in both your iOS suite and your web suite that hit the API app. These integration tests represent the contract that your API agrees to satisfy.

Parallelization is Hard

Understand that parallelizability will be very difficult at the beginning of the project and at major milestones of each sub-project (iOS and web). Breaking stories into tracks of work helps to highlight this.  One consequence of two backlogs is a tight coupling of stories across two Tracker projects.  iOS features that rely on additions to the API are necessarily blocked until the API team can deliver them, and the PMs must negotiate the priority of these stories in relation to web-centric features.  We opted to separate iOS and Web work, but perhaps a more fluid team could better navigate these dependencies.

Build Product One Screen At a Time

We’re actively trying to improve the story mapping/ideation process for new features.  It’s helpful to develop high-level features for all platforms at the same time, but it’s unusual that a web-focused feature can be copied verbatim to iOS.  Similarly, who breaks the tie when the web and iOS PMs have a different viewpoint on functionality or user experience?  Having one ultimate product owner could potentially address this point, yet it’s unclear how they wouldn’t be a giant bottleneck for both teams.

Closing Thoughts

Building this much (this fast) is fun!  We stood up complete experiences across three platforms while rapidly iterating on functionality.  With a team of this scale it’s critical that you’re re-Incepting your project and including the whole team in roadmap discussions to create a shared product vision.

What did I miss?  What are some best practices you’ve discovered managing products across web and mobile?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Georg Apitz

How to render jade templates on the server

Georg Apitz
Thursday, January 10, 2013

The best way for Google and friends to crawl and index a page is by finding static content. With applications that make heavy use of libraries like Backbone, Sproutcore or ember.js a lot of the content can be rendered in the browser. This can constitute a serious problem since the support methods from Google et. al. for alternative crawling coem with no guarantees.

We recently ran into this problem and came up with a neet solution where the client side templates can also be rendered server side.

There are two parts to the implementation. First, a method that can take a jade partial and its local variables, compile and evaluate it. Second, a view that calls out to that method and provides the template and the necessary local variables.

Part 1, the application_helper.rb


require 'tilt-jade/jade_js'

module ApplicationHelper
  def html_from_jade_template(partial, options={})
    data = File.open(\
            Rails.root.join("app", "assets", "templates", "#{partial}.js.jst.jade"\
            ))
    compiled = JadeJs.compile(data, locals: {}, client: true, compileDebug: false)

    raw(JadeJs::Source.context.eval("(#{compiled})(#{options[:locals].to_json}, \
        jade.runtime.attrs, jade.runtime.escape, jade.runtime.rethrow, \
        jade.runtime.merge)" \
        ))
  end
end

The method is pretty straight forward, it loads the template from the assets/template directory, compiles it and then evals it with the provided locals. The output is raw html that can be included in your view.

Part 2, a view that requests the compiled and eval’d templates from the server


- body = "This content comes from the server. Remove the param from the url or toggle \
          with the link in the upper right."
#menu
  = html_from_jade_template("home/menu", locals: {items: [{title: "Server"}, \
    {title: "Side"}, {title: "Rendered"}]})
#content
  = html_from_jade_template("home/content", locals: {body: body })

Here the main thing is the call to the method in application helper that will produce the raw html given a template name and the necessary local variables.

That is pretty much it. It may take a little bit to figure out the requirements for the local variables in your template, especially since jade is very strict about them. If it can’t find one it will not compile.

I’ve put an example app on github with the complete source code for this post.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Charles Hansen

Hunting Memory Leaks in Backbone

Charles Hansen
Tuesday, October 23, 2012

Ever notice your backbone app slows down considerably after using it for a few minutes? Or maybe your event callbacks are called more than once each time the event fires? Or my favorite, Chrome runs out of memory if you have a few tabs open that have run your entire Jasmine suite? This means you have backbone leaks.

I just spent the last week hunting these down in a very large app. It turns out the problem is tractable with a few guidelines. None of this is ground-breaking, but I wanted to put it all in one place.

1) The object retaining tree in the Chrome heap profiler is amazing. Use it.

2) Restrict use of views in closures, or as contexts to callbacks in event bindings. This is explained in much more detail here. I’ve also personally found nothing but trouble with _.bind for similar reasons.

More specifically, never use model.bind or model.on in a view without keeping track of that binding and remembering to unbind it when you want to tear down the view. This is the motivation for coccyx

3) ParentViews with childViews need to keep track of their children. When you tear down the parent view, you also tear down the child views. Before implementing this step, the object retaining tree for my zombie views was shockingly large. Afterwards, it was a manageable ~4 retainers for the zombies.

Parents keeping track of their children seems simple enough, but it is easy to lose track of the little devils.

For example:

ParentView = Backbone.View.extend({
    render: {
        //render the rest of the view first

        this.collection.each(function(model){
            var childView = new ChildView({model: model});
            this.$("ul").append(childView.render().$el);
        });
    }
});

loses any reference to the child views, and they will almost certainly leak (assuming the child views bind events to their models that need to be cleaned up).

Using the principles names from coccyx, the same code needed to be rewritten as

ParentView = Backbone.View.extend({
    render: {
        //render the rest of the view first

        this.tearDownRegisteredSubviews();

        this.collection.each(function(model){
            var childView = new ChildView({model: model});
            this.$("ul").append(liView.render().$el);

            this.registerSubView(childView);

        });
    }
});

where tearDownRegisteredSubviews calls tearDown on any registered subviews. This way, the only child views still in memory are those initialized in the most recent call to render. Those remaining views should be torn down when the parent is torn down.

View.tearDown itself can be a bit sticky because you still have to worry about event bindings from global objects and other difficult-to-clean sources, but those solutions are more project-specific.

Hope that points you in the right direction.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mark Rushakoff

Shorthand for searching a View’s DOM in Backbone

Mark Rushakoff
Thursday, August 16, 2012

Sometimes you use a pattern so frequently that you don’t realize that other people might not know about it. Here’s the most recent one for me — I was surprised that this wasn’t being used in the last few codebases I’ve seen:

If you’re using Backbone.js, instead of writing

view.$el.find('span.timestamp')

or worse

$(view.el).find('span.timestamp')

or

$('span.timestamp', view.$el)

Backbone provides a $ method such that you can simply write

view.$('span.timestamp')

It’s less to type and it doesn’t hurt readability.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Onsi Fakhouri

Cocktail: DRY up your backbone code with mixins

Onsi Fakhouri
Tuesday, July 24, 2012

I’ve continued to enjoy using Backbone.js to build single page apps. As I’ve seen more and more real world backbone I’ve started to develop opinions to augment the blissfully unopinionated little framework that could.

One of these opinions has turned into a mini-library: Cocktail adds functionality to Backbone’s extend to facilitate breaking up reusable code into mixins. It’s pretty straightforward:

  1. Define your mixin. Mixins are just plain vanilla JavaScript objects with methods and properties hanging off of them. Here’s a slightly contrived mixin that makes a view selectable:

    window.MyMixins = {};
    MyMixins.SelectMixin = {
      initialize: function() {
        this.model.on('change:selected', this.refreshSelect, this);
      },
    
    
      events: {
        click: 'toggleSelect'
      },
    
    
      render: function() {
        this.refreshSelect();
      },
    
    
      refreshSelect: function() {
        this.$el.toggleClass('selected', this.model.get('selected'));
      },
    
    
      toggleSelect: function() {
        this.model.set('selected', !this.model.get('selected'));
      }
    }
    
  2. Mix your mixin into your views. It’s a one-liner:

    var MyView = Backbone.View.extend({
      mixins: [MyMixins.SelectMixin, MyMixins.SomeOtherMixin],
    
    
      events: {
        'click .myChild': 'myCustomHandler'
      }
    
    
      initialize: function() { ... },
      render: function() { ... },
      etc...
    });
    
  3. That’s it! Instances of MyView will automatically inherit the behaviors and methods defined in SelectMixin.

Cocktail brings two simple things to the table:

  • it adds the special mixins:[...] notation to Backbone’s extend.
  • it automatically detects and handles method collisions. In the example above Cocktail will wrap MyView‘s and SelectMixin‘s implementations of initialize into one method and assign that method to the new, composite, class. The return value of this composite method is the last non-undefined value returned by the methods it wraps. All colliding methods are handled this way, as is the events hash (the events hashes all get merged together).

There are more details and examples at the repo. In particular, there’s an example for testing mixins with Jasmine — it goes over a pattern for writing shared behaviors in Jasmine.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Georg Apitz

Jasmine Testing: Param(s) passed to a method.

Georg Apitz
Thursday, July 19, 2012

Recently, I’ve been using a nice way to test if the correct arguments have been passed to a method. This uses a neat property of jasmine where you set up the method you want to test as a mock and have an expectation inside the mock.

The only caveat is you have to set an expectation that your mock get’s called, otherwise if it never gets executed the test will also never fail.

Step by Step

1) Set up the spy on the method for which you want to test the params.
2) Set up the expectation to compare a param to an expected value.

spyOn(App.Views.MasterView.prototype, 'initialize').andCallFake(function() {
 expect(arguments[0].template).toEqual(JST['my_templates/simple_view']);
});

3) Call something that should call the method under test with the correct params.

var router = new App.Routers.ViewRouter;
router.simpleViewInit();

4) Set up an expecatation that makes sure the method under test get’s actually called.

expect(App.Views.MasterView.prototype.initialize).toHaveBeenCalled();

Here you go, now it is easy to test if a method is called with the expected param(s).

The complete test.

describe('App.Routers.ViewRouter', function() {
  beforeEach(function() {
    Backbone.history.start();
  });

  afterEach(function() {
    Backbone.history.stop();
  });

    describe('#simpleViewInit', function() {
        it('call initialize on the MasterView with the simple_view template', function() {
        spyOn(App.Views.MasterView.prototype, 'initialize').andCallFake(function() {
             expect(arguments[0].template).toEqual(JST['my_templates/simple_view']);
          });
          var router = new App.Routers.ViewRouter;
          router.simpleViewInit();

         expect(App.Views.MasterView.prototype.initialize).toHaveBeenCalled();
        });
    });
});

The code that is under test.

App.Routers.ViewRouter = App.Routers.BaseRouter.extend(
{
    routes: {
        'example1': 'example1'
    },

    initialize: function() {
        ...
    },

    simpleViewInit: function() {
        this.simpleViewRecord = new App.Models.SimpleViewRecord();
        this.simpleView = new App.Views.MasterView({
            el: '#view_element',
            model: this.simpleView,
            template: JST[''my_templates/simple_view''],
            state: 'expired'
        });

        this.simpleViewInit.fetch({
            success: function(model) {
              model.trigger('change');
            }, silent: true
        });
    }
});
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mark Rushakoff

Simplifying View-View Events in Backbone using the Mediator Pattern

Mark Rushakoff
Thursday, June 14, 2012

In most single-page apps, you will inevitably end up having multiple views on one page at a time.
It usually starts out with just one view, and then that view needs to bind to events on a subview, and then the subview gets its own subview who will also trigger events that affect the top-level view…

When you have one view that ties to one model, the standard way to set up the binding in Backbone looks something like

MyView = Backbone.View.extend({
  initialize: function() {
    this.model.bind("change:someProp", this.render, this);
  }
  // ...
});

You might later find yourself binding view events in the same way:

MySubView = Backbone.View.extend({
  events: {
    "click li.foo": "fooSelected"
  },

  fooSelected: function(e) {
    this.trigger("foo:selected", new Foo({id: $(e.target).data("fooId") }));
  }
  // ...
});

MyView = Backbone.View.extend({
  initialize: function() {
    this.model.bind("change:someProp", this.render, this);
    this.subview = new MySubView();
    this.subview.bind("foo:selected", this.addFoo, this);
  },

  addFoo: function(foo) {
    var fooView = new FooView({model: foo});
    fooView.bind("someOtherEvent", this.render, this);
    this.$(".foos").append(fooView.render().$el);
  }
  // ...
});

This can get out of hand rather quickly in several ways:

  • You have to add a lot of code to your view to just to wire up the events properly, and your view has to be very aware of specific subviews
  • If you have three or more levels of views, then the views in the middle may need to “forward” events between subviews and parent views
  • You can end up binding the same method to the same event on subviews of the same class
  • To test that the events are configured correctly, you will need to instantiate all of the necessary subviews in your tests

In my experience, one of the cleanest solutions to this problem is to apply the mediator pattern.

(To be fair: it would be better if we could avoid needing a mediator altogether, but that is often not a realistic goal.)

The Mediator Pattern

Concept

The mediator is a centralized object (often a singleton) whose public API looks roughly like:

  • subscribe(string channel, function callback)
  • publish(string channel, arguments)
  • unsubscribe(string channel, function callback)

Even though your views are still coupled together through their layout hierarchy, your event logic is coupled completely around the mediator.
The good news is that your view events just became enormously easier to test.

Testing

The naive way to test view events before introducing a mediator may look something like this, in Jasmine:

describe("the subview", function() {
  describe("the foo:selected event", function() {
    var spy;
    beforeEach(function() {
      spy = jasmine.createSpy();
      subview.bind("foo:selected", spy);
    });

    it("is triggered when clicking on a li.foo", function() {
      subview.$("li.foo:eq(0)").click();
      expect(spy).toHaveBeenCalled();
    });
  });
});

describe("the view", function() {
  describe("the foo:selected event", function() {
    beforeEach(function() {
      spyOn(view, "fooSelected");
      view.subview.configure();
      view.render();
    });

    it("calls fooSelected", function() {
      view.subview.trigger("foo:selected");
      expect(view.fooSelected).toHaveBeenCalled();
    });
  });
});

It looks fairly simple when written this way, but in the real world, getting your views all set up and rendered may be significantly more complicated.

After introducing a mediator, you only need to test the interaction between your view and the mediator.
If you write some custom matchers, your tests can look as clean as this:

describe("the subview", function() {
  describe("the foo:selected event", function() {
    it("is triggered when clicking on a li.foo", function() {
      expect(function() {
        subview.$("li.foo:eq(0)").click();
      }).toPublish("foo:selected");
    });
  });
});

describe("the view", function() {
  describe("the foo:selected event", function() {
    it("calls fooSelected", function() {
      spyOn(view, "fooSelected");
      Mediator.publish("foo:selected");
      expect(view.fooSelected).toHaveBeenCalled();
    });
  });
});

Existing libraries

There are a decent number of libraries out there that do mediation and only mediation.
Surveying some of the libraries listed on microjs turns up several:

  • Mediator.js
  • smokesignals
  • Radio.js
  • callbacks.js
  • MinPubSub
  • js-signals

A quick Google search will turn up even more libraries:

  • Postman

But we’re already using Backbone…

Exactly!
You don’t need any external libraries because Backbone already provides everything you need to implement the mediator pattern.
Backbone.Events is what provides the existing bind/unbind/trigger functionality, which is everything you need to make your own mediator.
In fact, they even briefly mention this concept in the Backbone docs:

For example, to make a handy event dispatcher that can coordinate events among different areas of your application: var dispatcher = _.clone(Backbone.Events).

If you do choose to use the mediator pattern, here are some other extensions to that basic pattern that may be helpful:

  • Consider what you want your mediator to do when the same function is bound twice to the same event name.
    In some cases this is correct and it should call that function twice, but in other situations that is a programming error.
    Consider also when the function is the same but the context differs.
  • Sometimes a bindOnce function is handy – unsubscribe immediately after invoking the callback.
  • Make sure that both in the app (”between” pages) and in your tests (between specs) you unsubscribe all existing subscriptions so that you don’t leak references to objects that you are no longer using.
  • Depending on how you implement your mediator, you’ll probably want to write a custom matcher for your test library as well.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Onsi Fakhouri

Coccyx: plug up those backbone leaks

Onsi Fakhouri
Wednesday, June 13, 2012

A number of projects at Pivotal have been using Backbone.js to build single page web apps. I’ve enjoyed using Backbone: it’s lightweight, unopinionated, helps encourage good separation of concerns between models and views, and reduces a fair bit of JavaScript boilerplate by bringing just enough framework to the table.

Unfortunately, it’s very easy to write Backbone code that leaks – especially in the view layer. A common backbone pattern is to set up some event bindings for a view:

var MyView = Backbone.View.extend({
    initialize: function() {
        this.model.on('change', this.update, this);
        this.someOtherModel.on('change', this.update, this);
        this.boundResizeHandler = _.bind(this.resizeHandler, this);
        $(window).on('resize', this.boundResizeHandler);
    },
    ...etc..
});

If your app needs to switch between several such views it is not enough to simply remove the view’s DOM and null out any references to the view. You must also unbind these event bindings in order for the view to be garbage collected. Moreover, if this view contains any subviews, you must also tear down all event bindings for all its subviews. If you do not succesfully clear out all bindings the view (and/or its subviews) will leak.

What’s worse: while there are some great tools out there to identify leaking objects, Backbone’s default constructor lists all objects as type child. This makes finding the leaky Backbone objects that are instances of MyView nearly impossible in Chrome’s heap propfiler.

Coccyx attempts to adress these problems by doing two things:

  1. Coccyx adds named constructors to Backbone. You no longer need to wonder which child is yours. By adding constructorName when you extend a Backbone class you’ll be able to easily tell which object is which in the console and the heap profiler.

  2. Coccyx implements teardown-able view hierarchies. You can easily build view hierarchies in which parents are aware of their children and can tear the entire structure down by calling tearDown() on a root node. tearDown() automatically unbinds any Backbone event bindings, cleans up DOM events and gives your view a chance to perform any custom teardown via a callback.

There are many more details at https://github.com/onsi/coccyx. Bug reports and pull requests are encouraged!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Max Brunsfeld

A convenient ‘super’ method for Backbone.js

Max Brunsfeld
Monday, January 30, 2012

Inheritance in Backbone

Backbone.js comes with a minimalist OO inheritance framework similar to the one employed by CoffeeScript. Each base class has a static method called extend that is used to create a subclass, like this:

User = Backbone.Model.extend({
  // instance methods
},
{
  // class methods
});

extend returns a constructor whose prototype inherits from Backbone.Model.prototype. References to all of Backbone.Model’s static methods and properties (including extend) are copied to the new constructor.

Calling ‘super’

The constructor also receives a __super__ property, which references its superclass.

User.__super__ === Backbone.Model.prototype

This makes it possible to call super inside of a class or and instance method:

User.prototype.save = function(attrs) {
    this.beforeSave(attrs);
    User.__super__.save.apply(this, arguments);
};

CoffeeScript has a super keyword that compiles to the line above, but when using Backbone with plain javascript, its a little grating to have to type that out.

A small layer of convenience

I wrote this little super method (test-driven using jasmine) which saves me having to repeat the constructor’s name all over the place. You call it like this:

User.prototype.save = function(attrs) {
    this.beforeSave(attrs);
    this._super("save", arguments);
};

The second parameter to _super is the array of arguments to pass to the overridden method. This is to optimize for the common case of passing the arguments object straight through.

There’s no way to avoid having to repeat the method name like that, unless you wrap every method definition with a helper function that either passes the overridden method as a parameter (a la Prototype.js) or reassigns a hidden super property behind the scenes (like JS.Class or John Resig’s approach). These approaches won’t work with Backbone’s ultra-minimalist inheritance system.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (780)
  • rails (113)
  • testing (88)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (55)
  • techtalk (44)
  • rspec (38)
  • ironblogger (32)
  • productivity (30)
  • activerecord (29)
  • gogaruco (29)
  • git (28)
  • nyc (27)
  • rubymine (26)
  • bloggerdome (23)
  • mobile (22)
  • process (21)
  • pivotal tracker (20)
  • cucumber (20)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • css (13)
  • tdd (13)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to backbone Feed
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >