Implementing Feature Toggles in a .Net MVC Application

This is part 2 of a set of posts on feature toggles (part 1 is here). These are some example some code snippets (C#) based on techniques I have used to implement feature toggle systems in .Net applications. This example assumes that Unity is used for dependency injection. A real-world implementation would likely include a database for feature storage (with admin screens) and caching of feature checks for performance.

All Known Features

First, you need a master list of known features:


public enum FeaturesEnum
{
    CustomiseUserWorkspace,
    CancelSubscription,
    NewAwesomeThing
    //etc
}

Feature-Checking Code

Now you’re going to want a central authority for the current feature toggle state – a FeaturesService, and some kind of FeatureStore (not shown, add your own implementation). The FeatureStore implementation could be a config file or a database (hence the “ToggleFeature” method).


    public interface IFeaturesService
    {
        bool IsEnabled(FeaturesEnum feature);

        void ToggleFeature(FeaturesEnum feature, bool isEnabled);
    }

    public class FeaturesService : IFeaturesService
    {
        private readonly IFeatureStore _featureStore;

        public FeaturesService(IFeatureStore featureStore)
        {
            _featureStore = featureStore;
        }

        public bool IsEnabled(FeaturesEnum feature)
        {
            var featureRecord = _featureStore.GetFeature(feature);
            return (featureRecord == null) ? false : featureRecord.IsEnabled;
        }

        public void ToggleFeature(FeaturesEnum feature, bool isEnabled)
        {
            _featureStore.SetFeature(feature, isEnabled);
        }
    }

    public class FeatureRecord
    {
        public FeaturesEnum FeatureType { get; set; }
        public bool IsEnabled { get; set; }
    }

 

That’s all you need for basic feature toggle infrastructure. Now you need to add code to check those feature toggles.

Checking Feature State

First, to check for a feature and branch the code (eg. in a controller):


    public class HomeController : Controller
    {
        private readonly IFeaturesService _featuresService;

        public HomeController(IFeaturesService featuresService)
        {
            _featuresService = featuresService;
        }

        public ActionResult Index()
        {
            if (_featuresService.IsEnabled(FeaturesEnum.NewAwesomeThing))
            {
                //Do the shiny new thing
            }
            else
            {
                //Do the boring old thing
            }

            return View();
        }
    }

If you want to toggle UI elements (or hide the button that launches your new feature), add some HTML helper code:


    public static class FeaturesHelper
    {
        private static IFeaturesService _featuresService = null;
        private static IFeaturesService FeaturesService
        {
            get
            {
                if (_featuresService == null)
                {
                    _featuresService = UnityConfig.Container.Resolve();
                }
                return _featuresService;
            }
        }

        public static bool IsFeatureEnabled(this HtmlHelper helper, FeaturesEnum feature)
        {
            return FeaturesService.IsEnabled(feature);
        }
    }

And make a single-line check:


@if (Html.IsFeatureEnabled(FeaturesEnum.NewAwesomeThing))
{
<p>New awesome markup</p>
}

And finally, putting security checks on the UI is never enough, so you might want some access control attributes:


    public class FeatureEnabledAttribute : AuthorizeAttribute
    {
        private readonly FeaturesEnum _feature;

        public FeatureEnabledAttribute(FeaturesEnum feature)
        {
            _feature = feature;
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var featuresService = UnityConfig.Container.Resolve();
            return featuresService.IsEnabled(_feature);
        }
    }

Now you have another one-line check you can use to block access to controllers or methods:


    [FeatureEnabled(FeaturesEnum.NewAwesomeThing)]
    public class AwesomeController : Controller

Notes

I favour using enums over strings for my known features, because then I don’t have to worry about run-time typos suddenly checking for a brand new unknown feature. It also makes finding and removing features easier. And depending on language used, it might even be easy to document the features using decoration attributes (eg. adding longer descriptions for an admin system).

Any “master record” of which features exist should live with the code. You’re going to need to provide this to any admin system you build (via reflection, API call, etc).

When it comes to unit testing, it’s probably worth your while to actually implement a “FakeFeatureService” that’s just an in-memory dictionary and use the standard ToggleFeature() methods, rather than going with a mocking framework. Also, if you go with enums for your list of known features, the list is going to change frequently. To prevent brittle tests, don’t use a specific feature when testing your feature toggle framework code, just write a little builder/helper to give you a random valid feature enum from the list of current features.

Notes on Implementing Feature Toggles

If you want to release software frequently, you need to be able to cut releases at almost any time, and your code always needs to be in a releasable state (so no “code freeze” while you test and assemble the quarterly release). This makes dealing with in-progress feature development an issue, assuming you can’t completely build and test a new feature in a matter of hours.

One solution is to rely heavily on code branches for development and release, “cherry picking” the feature-complete-and-tested branches. This can introduce a load of test headaches though, as you try and coordinate which of the many possible “release candidates” you have are viable, and prevents using a simple no-or-minimal-branching “trunk-based” or “github-flow” workflow.

Another solution for regularly releasing constantly-changing software is to use a system of “feature toggles” (AKA “feature flags”). The basic idea is that the new code (which you may or may not be comfortable releasing) is already part of the release, but might not actually be available to use. Basically, you leave both the old (if you’re reworking something) and the new code in the release until you’re happy that the new code is ready to go into production, at which point the new code is “baked in”. With some simple “is feature X switched on” logic, and an admin screen or config file, you can control whether or not the new feature is “on”. And as a bonus, you can easily test old versus new behaviour, or even hit the emergency kill-switch and revert to the old code after you’ve deployed to production.

You could just have a load of bespoke configuration settings and checks, but the aim is to decouple and leverage as much common code as possible, so that you can just make a simple standard check for “is this new feature enabled?” and not have to worry about the underlying feature-toggling implementation.

What follows is some notes and observations based on the experience of implementing a feature toggle system (for a large e-commerce monolith .Net MVC web app).

Where and When to Check Feature Toggles

If you were building an API, you could just create a new version of an API method and deprecate the old one. Feature toggles are a great solution for UIs and other customer-facing apps where you want to hide and control the “old versus new code” decision. You can use feature toggles to control flow in microservices or “back-end” code, but you probably don’t need to.

To implement a feature toggle, you’re going to have to add code that you need to remove later, and to keep things simple you want to make as few checks as possible for each feature. Generally, you want to check a feature and branch to use the new path at the earliest possible opportunity, and you want that check to be a one-liner.

Typical examples of checks you would make:

  1. UI controls – the “should I show this button?” check for whether to show new/old or new/no UI component
  2. Authorisation/security – the “is the user allowed to visit this page?” check to block access to unfinished code
  3. Code branching for new/old behaviour – branch at the earliest opportunity, typically one of the first checks a controller action method would make

A suggestion: work on the basis that features default to “off”. Seriously, the additional code and deployment configuration complexity needed for “default to on” is really not worth it. If you have to ship the code with the feature “on”, then re-think your design.

How to Configure / Control Feature Toggles

To get started, you could have a simple “features” configuration file that you ship with your application. This does make it difficult to turn features on or off, other than when you deploy. A database (with an optional admin screen) gives you more control. If you have a microservices / service-based architecture, you might want a separate “features” microservice, although managing a small set of features for each individual UI app is probably going to work better (the feature toggle is generally used to control the start of the user journey). If you’re building your own admin system, a single shared admin UI calling APIs on your platform of client apps and services probably makes sense.

The Life of a Feature Toggle

Over its short life, the process should be:

  1. Create feature toggle, develop the new code, test locally in “on” and “off” states
  2. Allow the new code to ship to Test and Production in incomplete state, leave the feature “off”
  3. When development is complete, ship code to Test and test the “on” and “off” states
  4. Ship to Production, turn on the toggle
  5. After a few days/weeks without problems, remove the toggle and the “old code path” from the code – the change will work its way through to Production

Creating and Removing Features

Features should be easy to create and remove. You should only need to “create” a feature in one place to use it (eg. a “master feature list” enum). If you go for a database approach, don’t require migrations (or worse, a manual developer process) to add database records – just have the database record automatically created when you first toggle a feature on, and assume that “no matching record” means the feature is “off” (if you’re using config files, assume that “no setting” means feature defaults to “off”).

If you store features in a database, but your code holds the master record of the features that exist, you’re going to end up with a load of orphaned records for the features you removed. If you really care, you can clean these up manually in the database, or just have your application check (eg. on startup) the master list of features against the DB records and purge any old features.

The simplest approach is actually to do nothing to create features and just use a convention of referring to features by name, but if you can reference a “master list” of known features, you guard against any nasty issues with typos later on, and it’s easier to find feature references when you come to remove them later.

Advanced Feature Management

For bonus points, if you have a lot of feature toggles to manage, you might want to build the following into your admin system and testing process:

  1. The ability to export/import a set of toggles, so you can replicate the live setup on development or test environments
  2. The ability to script setting toggle states for doing an automated test run
  3. The ability to compare the toggles on two different environments (eg. live vs test)
  4. An indication of when a feature was toggled on (so you know it’s safe to remove a toggle after a couple of weeks)
  5. An audit trail of who turned the feature on, and when

Performance

If your feature toggles are stored in a database (or features microservice), there’s a cost to each feature check, and every user action could check the same toggle multiple times on the same screen (UI, security, code-branching). Invest in caching, even if it means that some users might have to wait an hour for the feature to become available.

Kill Your Feature Toggles!

It’s cool to be able revert new features to their old state, but every feature toggle in your codebase has a maintenance cost. And if a feature is hard to remove, that might indicate a more fundamental problem (is it really a long-term configuration setting?). When you’re happy with a feature being “on”, then bake it in to the code.

My ultimate aim would be to add an “auto-lock” mechanism to any feature toggle system, automatically locking a feature “on” after it had been enabled in production for a set length of time.

Not Everything is a Feature Toggle – Real World Lessons

I helped build a bespoke feature toggling system into a system that was deployed in two countries (UK and US deployments), each of which had its own test and live environments (in addition to 20 local-developer environments). We often had 30-40 toggles in operation at once. The ability to import/export toggles was very useful, as was the ability to compare UK/US or test/live.

We did end up with a lot of features that were hard to kill, either because they were buried deep in the code (always check features as early in the user request/journey as possible), inter-dependent so you had to remove them in a set order, or because they had been built “for the US only” and were actually configuration settings. To help deal with the long list of features, I ended up adding description attributes to each feature in the “master list” enum with a standard “Jira card number and long description” comment, and using this to provide column-sorting options on the admin screen.

I used some of the same tricks when refactoring the feature toggle system in another application. Putting the “master list” of features in the code helped simplify an app that was initially built to use the database as the source of all feature toggle knowledge (migration-heavy feature toggle creation). The admin screen didn’t get any more complex when it switched to reading known features from an enum instead of a database, and the setup process replaced “create an enum and a migration” with “add an enum”.

Remember, every feature toggle you add should be removed and baked into the code a couple of weeks after it gets turned on in production. If it relates to a “for region X only” feature, it should still be safe to turn it on in all regions.

Feature Toggles and A/B Testing

A simple implementation of feature toggles is to only have “on” and “off” states, but you could if you wanted extend feature toggles to include A/B testing or “canary release” scenarios. Assuming you have a production environment with “A” and “B” deployment slots (one handles the lucky “canary” users), you could instead have three feature states – “off”, “on in B only”, “on for all”. Your feature toggle system becomes a little more complicated, but you don’t need to change a single line of code when checking features. Your code can still ask the question “is feature X enabled?”, and the underlying logic will determine whether you’re running on the A or B environment and whether the feature is “on” for you. You could even expand this approach to any number of “slots”, depending on complexity – a shared development environment might need such an approach to allow individual developers to independently toggle features.