Andrew Lock | .NET Escapades

Andrew Lock

in ASP.NET Core Feature Flags Configuration ~ 6 min read.

Alternatives to Microsoft.FeatureManagement
Adding feature flags to an ASP.NET Core app - Part 6

In this series I've been looking at the Microsoft.FeatureManagement library (which is now open source on GitHub 🎉). This provides a thin layer over the .NET Core configuration system for adding feature flags to your application. But this library is new, and the feature flag concept is not - so what were people using previously?

I have a strong suspicion that for most people, the answer was one of the following:

  • LaunchDarkly
  • Some in-house, hand-rolled, feature-toggle system

Searching for "feature toggles" on NuGet gives 686 results, and searching for "feature flags" gives 886 results. I confess I've used none of them. I have always fallen squarely in the "roll-your-own" category.

On the face of it, that's not surprising. Basic feature flag functionality is pretty easy to implement, and as virtually every app has either external configuration values or a database connection of some sort, another dependency never felt necessary. That said, there's a big difference between basic on/off functionality, and staged, stable, gradual rollouts of features across your applications.

In this post I take a brief at a few of the alternatives to Microsoft.FeatureManagement, and describe their differences:

LaunchDarkly

LaunchDarkly is the real incumbent when it comes to feature toggles. They're a SaaS product that provide an API and UI for managing your feature flags, and SDKs in multiple languages (there's an open source SDK for .NET which supports .NET Standard 1.4+).

At the heart of LaunchDarkly are a set of feature flags, and a UI for managing them:

Screenshot of LaunchDarkly

However the simplicity of that screenshot belies the multitude of configuration options available to you. Each feature flag:

  • Can be a Boolean value (on/off), or a complex value like an int, string, or JSON, though I'd argue with the latter you're getting more into "general configuration" territory.
  • Can be marked "temporary" or "permanent" to make it easy to filter and remove old temporary flags.
  • Can have rich names and descriptions.
  • Can vary between different environments (dev/staging/production).
  • Can depend on other feature flags being enabled, so feature A is only enabled if feature B is enabled.

The values of the feature flags are all managed in the LaunchDarkly UI, which the SDK contacts to check flag values (caching where appropriate of course). The LaunchDarkly server typically pushes any changes to feature flags from their server to your app, rather than the SDK periodically polling, so you get updates quickly. Your code for checking a Boolean feature flag with the SDK would look something like this:

// Create a LaunchDarkly user for segmentation
var user = User.WithKey(username);

// Use the singleton instance of the LaunchDarkly client _ldClient,
// providing the feature flag name, the user, and a default value incase there's an error
bool showFeature = _ldClient.BoolVariation("your.feature.key", user, false);
if (showFeature) {
  // application code to show the feature 
}
else {
  // the code to run if the feature is off
}

This snippet highlights the user-segment feature - this ensures that feature flags are stable per-user, a problem I described in the previous post of this series. They also provide features for doing A/B/n testing, metrics for which features have been used (and by which users) and various other features. The SDK documentation is also really good.

Which finally brings us to the one downside - it isn't free! They have a free trial, and a basic plan for $25 a month, but prices jump to $325+ per month from there. You'll be paying based on the number of servers you have, the number of developers (UI-users) you have, as well as the number of active customers you have. It really does seem like a great product, but that comes at a cost, so it depends where your priorities lie.

RimDev.AspNetCore.FeatureFlags

RimDev.AspNetCore.FeatureFlags caught my eye as I was looking through NuGet packages, as it's from the team at RIMdev who have various open source projects like Stuntman. This library feels like a perfect example of the case I described at the start of the post - a project that started as in-house solution to a problem.

One of the interesting approaches used in the library is that features are defined using strongly-typed classes, (rather than the magic strings that are often used), and injecting the features objects directly into your services. For example, you might create a feature MyFeature. To check if it's enabled, you inject it into your service, and check feature.Value:

public class MyFeature : Feature
{
    // Optional, displays on UI:
    public override string Description { get; } = "My feature description.";
}

public class MyController : Controller
{
    private readonly MyFeature _feature;
    public MyController(MyFeature myFeature)
    {
        _feature = myFeature;
    }

    [HttpGet]
    public IActionResult OnGet()
    {
        if(_feature.Value)
        {
            // feature is enabled
        }
    }
}

RimDev.FeatureFlags uses an IFeatureProvider interface to get and update Feature instances. The library includes a single implementation, which uses SqlClient to store feature values in a SQL Server database. The library also includes a simple UI for enabling and disabling feature flags:

Enabling and disabling feature flags using the UI

Overall this is a pretty basic library, and lacks some of the dynamic features of other options, but if basic is all you need, then why go for complex!

Moggles

Moggles is a recently open-sourced project that was pointed out to me by Jeremy Bailey, one of the maintainers. Moggles follows a similar architecture to LaunchDarkly, where you have a server component that manages the feature toggles, and a client SDK that looks up the values of feature flags in your application.

Moggles in action

The server component has a UI that allows providing descriptions for feature flags, and supports multiple environments (desv/staging/production). It also includes the LaunchDarkly feature of marking feature flags as temporary vs permanent for filtering purposes. It can similarly integrate with a RabbitMq cluster to ensure updates to feature toggles are pushed out to applications without requiring the apps to poll for changes.

This project also grew out of an internal need, and that's relatively evident in the technologies used. The server currently only supports Microsoft SQL Server, and uses Windows Authentication with role-based authorization. If Moggles looks interesting but you have other requirements, maybe consider contributing, I'm sure they'd love the support.

Esquio

Esquio is an open source project on GitHub from Xabaril (creators of the excellent BeatPulse library). It was suggested to me by one of the maintainers of the project, Unai Zorrila Castro. It looks very interesting, is targeting .NET Core 3.0, and has a lot of nice features.

The basic API for Esquio is similar to the Microsoft.FeatureManagement library, but with a couple of key differences:

  • The API is async, unlike the synchronous API of Microsoft.FeatureManagement. So instead of IsEnabled, you have IsEnabledAsync.
  • You can have multiple stores for your feature flag configuration. IConfiguration is an option, but there's also an EF Core store if you wish to store your feature flag configuration in the database instead. Or you could write your own store implementation!

Esquio provides the same nice hooks into the ASP.NET Core infrastructure as Microsoft.FetureManagagement, like [FeatureFilter] attributes for hiding controllers or actions based on a flag's state; various fall-back options when an action is disabled; and Tag Helpers for conditionally showing sections of UI. As it's built on .NET Core 3.0, Esquio also allows you to attach feature filters directly to an endpoint too.

One interesting feature described in the docs is the ability to use the [FeatureFilter] attribute as an action constraint, so you can conditionally match an action based on whether a feature is enabled:

[ActionName("Detail")] // Same ActionName on both methods
public IActionResult DetailWhenFlagsIsNotActive()
{
    return View();
}

[FeatureFilter(Names = Flags.MinutesRealTime)] // Acts as an action constraint
[ActionName("Detail")]
public IActionResult DetailWhenFlagsIsActive()
{
    return View();
}

Esquio also includes the equivalent of Microsoft.FeatureManagement's feature filters, for dynamically controlling whether features are enabled based on the current user, for example. In Esquio, they're called toggles, but they're a very similar concept. One of the biggest differences is how many toggles Esquio comes with out of the box:

  • OnToggle/OffToggle - fixed Boolean on/off
  • UserNameToggle - enable the feature for a fixed set of users
  • RoleNameToggle - enable the feature for users in one of a set of roles
  • EnvironmentToggle - enable the feature when running in a given environment
  • FromToToggle - a windowing toggle to enable features for fixed time windows
  • ClaimValueToggle - enable the feature if a user has a given claim with one of an allowed set of values
  • GradualRolloutUserNameToggle - rollout to to a percentage of users, using a stable hash function (the Jenkins hash function) based on the username. There are similar gradual rollout toggles based on the value of a particular claim, the value of a header, or the ASP.NET Core Session ID.

As you'd expect, you're free to create your own custom Toggles too.

The gradual rollout toggles in particular are interesting, as they remove the need for the ISessionManager required by Microsoft.FeatureManagement to ensure consistency between requests for the PercentageFilter.

Esquio also includes a similar feature to LaunchDarkly where you can make the feature flag state available to SPA/mobile clients by including an endpoint for querying features:

app.UseEndpoints(routes =>
{
    routes.MapEsquio(pattern: "esquio");
});

On top of that, there's a UI for managing your feature flags! I haven't tried running that yet, but it's next on my list.

But wait! There's more!

There are even docs about how to integrate rolling out your feature flags as part of a release using Azure DevOps. If you integrate feature flags fully into your release pipeline, you can use canary releases that are only used by a few users, before increasing the percentage and enabling the feature across the board.

Enabling a feature as part of an Azure DevOps release

All in all I'm very impressed with the Esquio library. If you're already working with .NET Core 3.0 previews then it's definitely worth taking a look at if you need feature toggle functionality.

Summary

The Microsoft.FeatureManagement is intended to act as a thin layer over the Microsoft.Extensions.Configuration APIs, and as such it has certain limitations. It's always a good idea to look around and see what the other options are before committing to a library, and to try and understand the limitations of your choices. If money is no object, you can't go wrong with LaunchDarkly - they are well known in the space, and have a broad feature set. Personally, I'm very interested in Esquio as a great open-source alternative.

Loading comments powered by Disqus, please wait…
Andrew Lock | .Net Escapades

Stay up to the date with the latest posts!

Oops! Check your details and try again.
Thanks! Check your email for confirmation.