Matching Notes

Conditionally roll out features with Ember

To avoid any early confusion, this post is not about Ember's internal feature flagging. That refers to turning on and off experimental features of the framework that are not yet considered ready for production use.

This post refers to a technique I am using on Telescope to conditionally roll out new application functionality that we are developing.

We are using the Rollout gem in our Rails application to conditionally switch on new bits of functionality for groups of users. However, since it is Ember that is in charge of rendering the application UI, we need to make Ember aware of which features are available to a given user.

We are doing this by injecting a features object into the Ember controllers and routes when the application boots.

Ember.Application.initializer({
  name: 'features',
  initialize: function(container, app) {
    var features = Ember.Object.create(data.features);
    // Register the injection
    app.register('features:application', features, { instantiate: false });
    // Inject it where it is needed
    app.inject('controller', 'features', 'features:application');
    app.inject('route', 'features', 'features:application');
  }
});

In this example, data.features refers to an object containing the feature flags for the current user. In Telescope, we just have this serialized on the page when it loads, but you could make an API call for it instead if you preferred.

var data = {
  features: <%= FeaturesSerializer.new(current_user).to_json(root: false).html_safe %>
};

The FeaturesSerializer is an ActiveModel serializer that wraps out rollout object and serializes it to JSON.

class FeaturesSerializer < ActiveModel::Serializer
  attributes :suggestions, :forkable_decks

  def suggestions
    $rollout.active?(:suggestions, object)
  end

  def forkable_decks
    $rollout.active?(:forkable_decks, object)
  end

end

So, in our Ember application, we can easily access the features object in our templates to decide which sections of UI to render.

{{#if features.suggestions}}
  <p>Brand spanking new UI goes here</p>
{{/if}}

Likewise, the features object has been injected into our routes:

App.DeckRoute = Ember.Route.extend({
  setupController: function(controller, model) {
    // this.get('features.suggestions') is available if you need it.
  }
})

Now it's just a case of flipping the switch to turn on new functionality for groups of users. We're using the Rollout UI gem which gives us an easy interface for doing that.