Feature Flags

Learn what Feature Flags are, the problems they solve, how to implement them in .NET, and their most common use cases.

It's common for a feature to take days or even weeks to be completed. However, that doesn't mean the rest of the team should stop deploying bug fixes or new functionality in the meantime.

How can we integrate unfinished code without exposing it to users? What if we want to enable a feature only for a specific group of customers, run an A/B test, or quickly disable it if something goes wrong in production?

Feature Flags (also known as Feature Toggles) are a pattern that allows you to control an application's behavior at runtime by enabling or disabling features without requiring a new deployment.


#What is a Feature Flag?

A Feature Flag is a mechanism that determines whether a feature should be executed at runtime.

Although the simplest implementation is a boolean flag, Feature Flags can be evaluated using many different criteria, such as user percentage, environment, user roles, time windows, or any custom business rule.

The main idea is to decouple deployment from release. In other words, code can be deployed to production without necessarily making the feature available to users.


#What problem do they solve?

Without Feature Flags, deploying a new version usually means releasing every feature included in that deployment.

This introduces several challenges:

  • An unfinished feature can delay an entire deployment.
  • Long-lived branches become common to prevent incomplete work from reaching production.
  • If a single feature causes issues, rolling back the entire application may be the only option.

Feature Flags allow code to be deployed independently from when the feature is actually released.

In other words:

Deployment and Release become two separate events.


#Implementing Feature Flags in .NET

Microsoft provides the Microsoft.FeatureManagement.AspNetCore package, which makes implementing Feature Flags in ASP.NET Core applications straightforward.

#Installation

Install the package.

BASH
dotnet add package Microsoft.FeatureManagement.AspNetCore

#Register the service

Enable Feature Management during application startup.

CSHARP
builder.Services.AddFeatureManagement();

#Define your Feature Flags

The simplest approach is to configure them in appsettings.json.

JSON
{
  "FeatureManagement": {
    "NewDashboard": false,
    "NewCheckout": true
  }
}

To avoid typos and centralize flag names, it's recommended to define them in a static class.

CSHARP
public static class FeatureFlags
{
    public const string NewDashboard = nameof(NewDashboard);
    public const string NewCheckout = nameof(NewCheckout);
}

#Check a Feature Flag

IFeatureManager lets you determine whether a feature is enabled.

CSHARP
public class DashboardService
{
    private readonly IFeatureManager _featureManager;

    public DashboardService(IFeatureManager featureManager)
    {
        _featureManager = featureManager;
    }

    public async Task ShowDashboardAsync()
    {
        if (await _featureManager.IsEnabledAsync(FeatureFlags.NewDashboard))
        {
            Console.WriteLine("New Dashboard");
        }
        else
        {
            Console.WriteLine("Classic Dashboard");
        }
    }
}

The decision is made at runtime, so changing the flag changes the application's behavior without modifying the code.


#Protect an entire endpoint

If an API endpoint should only be available when a feature is enabled, use the FeatureGate attribute.

CSHARP
[FeatureGate(FeatureFlags.NewDashboard)]
[ApiController]
[Route("dashboard")]
public class DashboardController : ControllerBase
{
}

If the Feature Flag is disabled, the endpoint becomes unavailable automatically.


#Filters

Not every Feature Flag is a simple true or false value.

The library includes several built-in filters that allow you to define when a feature should be enabled.

Some of the most common are:

  • Boolean Filter: Enables or disables a feature manually.
  • Time Window Filter: Enables a feature only during a specific time period.
  • Percentage Filter: Enables a feature for a percentage of users.
  • Targeting Filter: Enables a feature for specific users or groups.

You can also create custom filters to implement any business-specific rule.


#Common use cases

#Deploy ≠ Release

Probably the most important use case.

A feature can remain hidden for days or weeks while the rest of the application continues to be deployed normally.

When the feature is ready, simply enable the Feature Flag.


#Kill Switch

If a feature starts causing issues in production, performing a full rollback isn't always necessary.

Disabling the Feature Flag is often enough to immediately stop using the problematic functionality.

This significantly reduces incident response time.


#Gradual Rollout

Not every feature should be released to all users at once.

You can enable it for a small percentage of users first and gradually increase the rollout while monitoring system behavior.


#Private Beta

Some features should only be available to internal users, testers, or selected customers.

Feature Flags allow you to control access without maintaining multiple application versions.


#A/B Testing

Another common use case is exposing different implementations of the same feature.

For example:

  • 50% of users see the current experience.
  • 50% see a new version.

You can then compare metrics such as conversion rate, engagement, or retention to determine which version performs better.


#Best Practices

#Keep Feature Flags temporary

Once a feature is stable and no longer needs to be controlled, remove its Feature Flag.

Leaving obsolete flags in the codebase increases complexity and creates technical debt.


#One Feature Flag, one responsibility

Avoid using the same flag to control multiple unrelated behaviors.

Each Feature Flag should represent a single feature.


#Avoid nested Feature Flags

Code like this is usually a warning sign.

CSHARP
if (await featureManager.IsEnabledAsync(FeatureFlags.FeatureA))
{
    if (await featureManager.IsEnabledAsync(FeatureFlags.FeatureB))
    {
        // ...
    }
}

The number of possible combinations grows quickly, making testing and maintenance more difficult.


#Use descriptive names

Names like:

TEXT
Feature1

don't provide much context.

Prefer names such as:

TEXT
NewCheckout
EnableDiscountCoupons
ExperimentalSearch

#Remove Feature Flags that no longer provide value

Feature Flags shouldn't become permanent configuration.

If a feature is fully rolled out and will always remain enabled, removing the Feature Flag keeps the codebase cleaner and easier to maintain.

#architecture #feature-flags #dotnet