5 Reasons to use the Feature pattern in your product

Monday, January 30, 2012 gc 0 Comments

A design pattern is a formal way of documenting a solution to a design problem.

The "Feature" design pattern is a simple yet powerful mechanism that enables you to rollout features based on configuration settings. It provides the ability to turn these high-level features "on" or "off" by changing the configuration parameter values. The basic idea: use a simple mechanism to control the features that are presented to the end user.

5 Reasons to use the "Feature" pattern in your product

1. Reduces the need to branch: The longer your branch remains unmerged, the greater chance that it will never make it. By being able to not turn the feature "on" until ready in production, but continue to work in the main or trunk branch is valuable.

2. Partial rollbacks (isolated): A single new feature will not hold up a production deployment, it can be turned "off" until ready. It keeps progress moving and a feature rollout does not have to necessarily coincide with a code rollout.

3. Different features for different folks: Will make it easier to turn a feature "on" for testing in a particular environment, but "off" for your production environment. It can also help with partial user and A/B testing.

4. Helps reduce risk: With the ability to turn features "off" until ready, it can allow you to increase development velocity without necessarily increasing risk.

5. Used by #winners: Facebook, Amazon, and many others. Today, Facebook has many new features that are not yet turned on yet but deployed to their production environment. Although not an intended reason, Facebook has even turned on features in production to react to new features from Google+. Features ready to be turned "on."

My hope: the "Feature" pattern will help you for the same reasons.

For more details about the Feature pattern, see the somewhat terse pattern description below along with the source code. There is not much to this simple yet powerful pattern.


Pattern Name: Feature

Intent
Provide a simple yet powerful way to turn features "on" and "off" via configuration. This enables certain environments to have certain features turned "on."

Also Known As
Feature toggle.

Motivation (Forces)
Demand for high development velocity and the need to mitigate risk.

Applicability
Web apps, freemium business models, mobile apps, services, etc.

Structure
The pattern structure is simple and consists of the Feature class. 

Participants
Feature: Represents a Feature that can be turned on or off based on an app setting. You can take an action if it is on, off, or both.

Collaboration
Here is an example code fragment that interacts with Feature:

           Feature.BasedOn("Product.Feature.IsOn")
                .IfOn(() =>
                {
                    // do the feature
                })
                .IfOff(() =>
                {
                    // do the alternative or not
                });

If the the configuration parameter "Product.Feature.IsOn" is set to "true," then the IfOn action will be invoked; otherwise, the IfOff action will be invoked.

Consequences
Be sure to get granularity right.  Be wary of adding features at too fine of detail. Complex feature interaction can also complicate testing. Although a more general discussion, make sure that each feature is justified. However, even in a design world driven by YAGNI and KISS principles, you still need some flexibility—just make sure that it is justified.

Implementation
Here is a simple implementation of the Feature pattern in C# below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;

namespace Product.Support
{
    /// <summary>
    /// Represents a Feature that can be turned on or off
    /// based on an app setting. You can take an action if it is 
    /// on, off, or both.
    /// </summary>
    public class Feature
    {
        /// <summary>
        /// Based on this app setting. 
        /// </summary>
        public string AppSettingKey { get; set; }

        /// <summary>
        /// Create a feature based on the given setting.
        /// </summary>
        /// <param name="appSettingKey">The app setting that determines whether the feature is turned on or off.</param>
        /// <returns>Newly created feature.</returns>
        public static Feature BasedOn(string appSettingKey)
        {
            if (string.IsNullOrEmpty(appSettingKey)) throw new Exception("setting key cannot be null or empty.");
            Feature feature = new Feature();
            feature.AppSettingKey = appSettingKey;
            return feature;
        }

        /// <summary>
        /// Whether the feature is turned on; that is, if the app setting
        /// has a value of true; otherwise, false.
        /// </summary>
        /// <returns>Whether the feature is on; that is, if the app setting
        /// has a value of true.</returns>
        public bool IsOn()
        {
            if (AppSettingKey == null) return false;
            string value = ConfigurationManager.AppSettings[AppSettingKey];
            return (value != null && value.ToLower() == "true");
        }

        /// <summary>
        /// Performs the given action if the feature is turned on.
        /// </summary>
        /// <param name="action">The action to take.</param>
        /// <returns>The feature</returns>
        public Feature IfOn(Action action)
        {
            if (IsOn())
                action.Invoke();
            return this;
        }

        /// <summary>
        /// Performs the given action if the feature is turned off.
        /// </summary>
        /// <param name="action">The action to take.</param>
        /// <returns>The feature</returns>
        public Feature IfOff(Action action)
        {
            if (!IsOn())
                action.Invoke();
            return this;
        }
    }
}

Sample Code
As sample code, here are some unit tests that interact with the Feature class:
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Product.Support
{
    [TestClass]
    public class FeatureTests
    {
        [TestMethod]
        public void Feature_IsOff_Success()
        {
            // arrange
            bool isPass = false;

            // act
            Feature.BasedOn("Product.Feature.IsNotOn")
                .IfOn(() =>
                {
                    isPass = false;
                })
                .IfOff(() =>
                {
                    isPass = true;
                });

            // assert
            Assert.IsTrue(isPass);
        }

        [TestMethod]
        public void Feature_IsOn_Success()
        {
            // arrange
            bool isPass = false;

            // act
            Feature.BasedOn("Product.Feature.IsOn")
                .IfOn(() =>
                {
                    isPass = true;
                })
                .IfOff(() =>
                {
                    isPass = false;
                });

            // assert
            Assert.IsTrue(isPass);
        }
    }
}

Known Uses
Facebook, Amazon, and others.

Related Patterns
For a couple more previously unpublished patterns (Instrumentation and Service Protector), check out this article that I wrote for the Microsoft Developer Network last year, WCF Decision Framework:
http://gregcowin.blogspot.com/2011/08/why-use-decision-framework.html


Enjoy the pursuit of great design.

You Might Also Like

0 comments: