Chapter 9. Feature Toggle

Continuous deployment is the process of testing, integrating, and deploying software in rapid cycles in order to deliver bug fixes and new features to customers as quickly as possible. It gained popular acceptance as a cornerstone of extreme programming and agile development and is very popular among Software as a Service providers.

A feature toggle system allows you to integrate features into your codebase even before they’re finished and ready to release. During development, the features are toggled off by default. In order to turn them on, you must enable them manually. Using this method, you can deploy unfinished or untested changes into your production system without interfering with the user experience.

Feature toggles can allow software integration cycles that run in weeks, days, or even hours, as opposed to months or years. They are an essential component in a broader continuous integration system.

Feature toggles are popular in the startup community but have gained widespread acceptance in the enterprise, including larger companies such as Facebook, Google, Yahoo, and Adobe.

Deciding how to organize features begins with deciding what exactly constitutes a feature. Once you figure that out, you should also decide how you’re going to classify or group features together.

Sometimes it’s handy to group features together. You should definitely do so for features that are designed to interact with each other. For instance, you might have a user ratings feature that allows users to add stars and comments to items in a list. The stars and comments are both subfeatures of the user-ratings parent feature. If ratings are turned off, users can no longer comment or add stars to an item. It’s possible to build a feature-dependency graph to keep track of which features rely on each other to operate correctly, and then toggle them on and off simultaneously.

Groups can also be handy for marketing purposes. Sometimes it’s useful to group several exciting new features together into a newsworthy release. For products that rely on publicity cycles to spur growth, engineering must provide the technical capabilities to generate worthy news events.

It’s essential for the product team to engage the marketing team in a realistic way. The executives and marketing department should not start touting a release date until the features are in the testing stage and nearing completion. Features don’t need to be released the moment they’re complete. You can hold completed features for a scheduled marketing event and flip the toggle switch at the precise moment that marketing has promised delivery. Since the features are already deployed into production, they’ll be live and ready to use immediately.

Features are often grouped in default sets for each environment, as well. A default set commonly includes all of the features that will be toggled on in the next deploy cycle.

The creation of a feature begins with naming and documentation. Before you write a line of code, name and describe the feature, along with any required criteria to qualify it for production activation. “User ratings” might be a good feature name.

On the client side, you can show and hide features based on the presence of CSS classes. Take the following HTML:

  <!DOCTYPE html>
  <html>
    <head>
      <title>Feature Toggle Demo</title>
      <style>
        li {
          display: inline-block;
          margin-left: 1em;
        }

        .new-feature {
          display: none;
        }

        .ft-new-feature .new-feature {
          display: inline-block;
        }
      </style>
    </head>
    <body>
      <p>Add <code>?ft=new-feature</code> to the end
        of the url to see the new feature.</p>
      <div class="menu">
        <ul>
          <li class="old-feature">Boring old feature</li>
          <li class="new-feature">New feature!</li>
        </ul>
      </div>
      <script src="../dist/feature-toggle-client.js">
      </script>
      <script>
        // Activate the feature toggle system.
        var ft = featureToggle();
      </script>
    </body>
  </html>

By setting the .ft-new-feature class on the body tag, you can make the feature show up in the menu. Here’s a client-side script that can manage feature toggles in the browser:

  'use strict';

  var union = require('mout/array/union'),
    contains = require('mout/array/contains'),
    EventEmitter = require('events').EventEmitter,
    stampit = require('stampit'),

    /**
     * Grab the url parameters and build a map of
     * parameter names and values.
     * @return {Object} params object
     */
    getParams = function getParams() {
      var params = {};
      if (location.search) {
        var parts = location.search.slice(1).split('&');

        parts.forEach(function (part) {
          var pair = part.split('=');
          pair[0] = decodeURIComponent(pair[0]);
          pair[1] = decodeURIComponent(pair[1]);
          params[pair[0]] = (pair[1] !== 'undefined') ?
            pair[1] : true;
        });
      }
      return params;
    },

    /**
     * Get a list of feature names from the url
     * parameters.
     * @return {Array} Features list
     */
    getParamFeatures = function getParamFeatures() {
      var features = getParams().ft;
      return features ? features.split(',') : undefined;
    },

    /**
     * Combine the list of base features with
     * the features passed in via URL parameters.
     * @type {Array} active features
     */
    getActiveFeatures =
        function getActiveFeatures(baseFeatures,
          paramFeatures) {
      return union(baseFeatures, paramFeatures);
    },

    /**
     * Takes an array of features and creates a class for
     * each of them on the body tag.
     * New features should be hidden in CSS by default
     * and revealed only when the feature toggle is set:
     *
     * .new-feature { display: none; }
     * .ft-new-feature .new-feature { display: block; }
     * 
     * @param {Array} features An array of active features.
     */
    setFlags = function setFlags(features) {
      var featureClasses = features.map(function (feature) {
          return 'ft-' + feature;
        }).join(' '),
        classNames = document.getElementsByTagName('body')[0]
          .className.split(' ').filter(function (className) {
            return !className.match(/^ft/);
          });
      document.getElementsByTagName('body')[0].className = 
        classNames.join(' ') + ' ' + featureClasses;
    },

    /**
     * Take an optional list of features, set the feature
     * classes on the body tag, and return the feature
     * toggle object.
     * @param {Array} baseFeatures List of base features.
     * @return {Object} feature object
     */
    setFeatures = function setFeatures(baseFeatures) {
      var paramFeatures = getParamFeatures(),
        activeFeatures = getActiveFeatures(baseFeatures,
          paramFeatures),

        methods = {
          /**
           * Check to see if a feature is active.
           * @param  {String} feature 
           * @return {Boolean}
           */
          active: function active(feature) {
            var testFeature = feature && feature.trim &&
              feature.trim();
            return contains(activeFeatures, testFeature);
          },

          /**
           * Activate a list of features.
           * @emits activated
           * @param  {Array} features 
           * @return {Object} this (for chaining)
           */
          /**
           * activated event.
           *
           * @event activated
           * @type {Array} activated features
           */
          activate: function activate(features) {
            activeFeatures = union(activeFeatures, features);
            setFlags(activeFeatures);
            this.emit('activated', features);
            return this;
          },

          /**
           * Deactivate a list of features.
           * @emits deactivated
           * @param  {Array} features 
           * @return {Object} this (for chaining)
           */
          /**
           * deactivated event.
           *
           * @event deactivated
           * @type {Array} deactivated features
           */        
          deactivate: function deactivate(features) {
            activeFeatures = 
              activeFeatures.filter(function (feature) {
                return !contains(features, feature);
              });
            setFlags(activeFeatures);
            this.emit('deactivated', features);
            return this;
          }
        },

        // Creates the feature toggle object by
        // composing these methods with an
        // event emitter using the Stampit
        // prototypal inheritance library.
        ft = stampit.compose(
          stampit.convertConstructor(EventEmitter),
          stampit(methods)
        ).create();

      // Kick things off by setting feature classes
      // for the currently active features.
      setFlags(activeFeatures);

      return ft;
    };

  module.exports = setFeatures;

On the server side, you’ll want to check the URL parameters and a features cookie (a saved list of feature overrides), get the list of currently active features from the feature database, and combine that data to calculate the currently active features for the current user.

The list of features sent by the server is often influenced by a number of different criteria. A common example: say you want to roll out a feature to 20% of the users. You can use a clever trick to base the user percentile on the user ID. Of course this won’t produce exactly even distributions, but for user communities numbering in the thousands, it’s usually a close enough approximation:

  userPercentage = function userPercentage(userId, percent) {
    var id = parseInt(userId, 36);
    return (id % 100 < percent);
  };

Other common criteria include whitelisted users and whitelisted user groups, user sign-up date, paid versus unpaid users, andtype of account.

Feature toggles offer tremendous gains in the integration process, allowing companies to ship updates without the nightmare of coordinating a release across many features and teams working on new product features.

They also allow you to test new features in the actual production environment without interfering with the user experience.

Gradual feature rollout allows you to gauge the impact of a change and roll back changes with a minimal impact on user experience and productivity.

As you’ve seen, it’s not hard to integrate feature toggles into your code, and it offers a great deal of value. It’s no wonder that feature toggles have been adopted by so many industry-leading software teams.