Skip to content
valdisiljuconoks edited this page Dec 13, 2014 · 13 revisions

Feature Toggling Framework for Various Types of Applications

Have you ever wrote the code like following to verify that either you have to disable or enable some functionality base on set of conditions (usually different sources):

  if(ConfigurationManager.AppSettings["MyKey"] == "true")
      return ...;

or something like this:

  if(HttpContext.Current != null && HttpContext.Current.Session != null && HttpContext.Current.Session["MyKey"] == "true")
      return ...;

FeatureSwitch library should reduce amount of time and code needed to implement feature toggle in unified way. FeatureSwitch library is easily adoptable and extendable.

Overview

FeatureSwitch library is based on two basic aspects features and strategies. In combination they provide enough input data for feature set builder to construct feature context to be used later to check whether particular feature is enabled or disabled.

Features

Feature is main aspect of FeatureSwitch library it's your logical feature representation - either it's enabled or disabled. You will need to define your features to work with FeatureSwitch library. To define your feature you have to create class that inherits from FeatureSwitch.BaseFeature class:

  public class MySampleFeature : FeatureSwitch.BaseFeature
  {
  }

Keep in mind that you will need to add at least one strategy for the feature in order to enable it. By default every feature is disabled.

Setup (FeatureSet builder)

To get started with FeatureSwitch you need to kick-off FeatureSetBuilder by calling Build() instance method:

  var builder = new FeatureSetBuilder();
  builder.Build();

By calling Build method you are triggering auto-discovery of features in current application domain loaded assemblies. Auto-discovery will look for classes inheriting from FeatureSwitch.BaseFeature class. Those are assumed to be features.

Is Feature Enabled?

After features have been discovered and set has been built you are able to check whether feature is enabled or not:

  var isEnabled = FeatureContext.IsEnabled<MySampleFeature>();

You can also use some of the IsEnabled overloads for other usages.

Strategies

Whereas strategies are aspect in the library that is controlling either feature is enabled or disabled in the current circumstances. Strategy is an attribute you decorate your feature with:

  [FeatureSwitch.Strategies.AppSettings(Key = "MySampleFeatureKey")]
  public class MySampleFeature : BaseFeature
  {
  }

Currently FeatureSwitch release contains following built-in strategies:

  • AlwaysFalse - by using this strategy your feature will be always disabled
  • AlwaysTrue - this strategy will always make your feature shine
  • AppSettings - key under <appSettings> element either in web.config or app.config will control this feature state
  • HttpSession - if you need more permanent storage for your feature's state you can make use of Http session
  • QueryString - if you will provide magic key in query string, feature may enable or disable.

Multiple Strategies

Single feature can support more that one strategy. For instance:

  [AppSettings(Key = "StyleOptimizationDisabled")]
  [QueryString(Key = "DisableStyleOptimization", Order = 1)]
  public class StyleOptimizationDisabled : BaseFeature
  {
  }

this feature is controlled by <appSettings> element key in web.config file and then also by query string. It means that feature is disabled by AppSettings strategy but enabled by QueryString strategy (providing proper query string) - feature is eventually enabled. In order to attach multiple strategies to single feature you need to define order for the strategy using Order property. FetureSetBuilder will fail if it founds multiple strategies with undefined or equal order. When feature is asked for enabled state all strategies in ascending order are used to query for feature enabled state. If any of those return true feature is enabled.

Writable Strategies

Sometimes it's required to store feature enabled state in more permanent storage (session, cache, etc). For this reason you can use any of FeatureSwitch.Strategies.BaseStrategyImpl implementations. Currently available writeable strategies:

  • HttpSession - using this strategy you can enable or disable feature and it will remain in this state as long as session is alive

Advanced Context Construction Cases

At moment of current release of FeatureSwitch library we do have dependency on structuremap in order to keep track of strategies and dependencies and also to support constructor injection for custom strategies if needed (this is subject for refactoring). Using dependency injection you are able to instruct FeatureSetBuilder how to construct context and which strategies to use and how to initialize them.

Disable Auto-Discovery

Sometimes it's required to disable auto-discovery and use only predefined list of features. This can be accomplished using action parameter of Build() method:

  var builder = new FeatureSetBuilder();
  builder.Build(ctx => ctx.AddFeature<MySampleFeature>());

This will add only MySampleFeature to the context and will not perform auto-discovery of features.

If it's still needed to add arbitrary features and continue with auto-discovery of features this can be controlled via context AutoDiscoverFeatures property:

  var builder = new FeatureSetBuilder();
  builder.Build(ctx =>
                {
                    ctx.AddFeature<MySampleFeature>();
                    ctx.AutoDiscoverFeatures = true;
                });

Strategy Swap

Let's assume that for any reason you are required to change / swap built-in strategy with some other implementation. This can be used by using configuration expression while building feature set (MySampleFeature is configured to use AppSettings strategy):

  [FeatureSwitch.Strategies.AppSettings(Key = "MySampleFeatureKey")]
  public class MySampleFeature : BaseFeature
  {
  }

  ...

  var builder = new FeatureSetBuilder();
  var container = builder.Build(ctx =>
                  {
                      ctx.AddFeature<MySampleFeature>();
                      ctx.ForStrategy<AppSettings>().Use<SomeFancyStrategyImplementation>();
                  });

Custom Strategy With Dependency

Let's assume that we have a fancy strategy that is depended on even more fancier service to work properly:

  public class StrategyWithConstructorParameterReader : BaseStrategyImpl
  {
      public StrategyWithConstructorParameterReader(ISampleInjectedInterface sample)
      {
          ...

So to properly configure dependency composition root we need to configure FeatureSetBuilder to inject ISampleInjectedInterface properly. To do so you can use dependencyConfiguration parameter of Build() method:

  builder.Build(dependencyConfiguration: ex => ex.For<ISampleInjectedInterface>().Use<SampleInjectedInterface>());

More Information

More information on extending FeatureSwitch library.