The ability to conditionally turn features on or off in your application without redeploying the code is a powerful tool.
It lets you quickly iterate on new features and frequently integrate your changes with the main branch.
You can use feature flags to achieve this.
Feature flags are a software development technique that allows you to wrap application features in a conditional statement. You can then toggle the feature on or off in runtime to control which features are enabled.
We have a lot to cover in this week's newsletter:
- Feature flag fundamentals in .NET
- Feature filters and phased rollouts
- Trunk-based development
- A/B testing
Let's dive in!
Feature Flags In .NET
Feature flags provide a way for .NET and ASP.NET Core applications to turn features on or off dynamically.
To get started, you need to install the Microsoft.FeatureManagement
library in your project:
Install-Package Microsoft.FeatureManagement
This library will allow you to develop and expose application functionality based on features. It's useful when you have special requirements when a new feature should be enabled and under what conditions.
The next step is to register the required services with dependency injection by calling AddFeatureManagement
:
builder.Services.AddFeatureManagement();
And you are ready to create your first feature flag. Feature flags are built on top of the .NET configuration system. Any .NET configuration provider can act as the backbone for exposing feature flags.
Let's create a feature flag called ClipArticleContent
in our appsettings.json
file:
"FeatureManagement": {
"ClipArticleContent": false
}
By convention, feature flags have to be defined in the FeatureManagement
configuration section. But you can change this by providing a different configuration section when calling AddFeatureManagement
.
Microsoft recommends exposing feature flags using enums and then consuming them with the nameof
operator. For example, you would write nameof(FeatureFlags.ClipArticleContent)
.
However, I prefer defining feature flags as constants in a static class because it simplifies the usage.
// Using enums
public enum FeatureFlags
{
ClipArticleContent = 1
}
// Using constants
public static class FeatureFlags
{
public const string ClipArticleContent = "ClipArticleContent";
}
To check the feature flag state, you can use the IFeatureManager
service. In this example, if the ClipArticleContent
feature flag is turned on, we will return only the first thirty characters of the article's content.
app.MapGet("articles/{id}", async (
Guid id,
IGetArticle query,
IFeatureManager featureManager) =>
{
var article = query.Execute(id);
if (await featureManager.IsEnabledAsync(FeatureFlags.ClipArticleContent))
{
article.Content = article.Content.Substring(0, 50);
}
return Results.Ok(article);
});
You can also apply feature flags on a controller or endpoint level using the FeatureGate
attribute:
[FeatureGate(FeatureFlags.EnableArticlesApi)]
public class ArticlesController : Controller
{
// ...
}
This covers the fundamentals of using feature flags, and now, let's tackle more advanced topics.
Feature Filters And Phased Rollouts
The feature flags I showed you in the previous section were like a simple on-off switch. Although practical, you might want more flexibility from your feature flags.
The Microsoft.FeatureManagement
package comes with a few built-in feature filters that allow you to create dynamic rules for enabling feature flags.
The available feature filters areMicrosoft.Percentage
,Microsoft.TimeWindow
and Microsoft.Targeting
.
Here's an example of defining a ShowArticlePreview
feature flag that uses a percentage filter:
"FeatureFlags": {
"ClipArticleContent": false,
"ShowArticlePreview": {
"EnabledFor": [
{
"Name": "Percentage",
"Parameters": {
"Value": 50
}
}
]
}
}
This means the feature flag will be randomly turned on 50% of the time. The downside is the same user might see different behavior on subsequent requests. A more realistic scenario is to have the feature flag state be cached for the duration of the user's session.
To use the PercentageFilter
, you need to enable it by calling AddFeatureFilter
:
builder.Services.AddFeatureManagement().AddFeatureFilter<PercentageFilter>();
Another interesting feature filter is the TargetingFilter
, which allows you to target specific users. Targeting is used in phased rollouts, where you want to introduce a new feature to your users gradually. You start by enabling the feature for a small percentage of users and slowly increase the rollout percentage while monitoring how the system responds.
Trunk-based Development and Feature Flags
Trunk-based development is a Git branching strategy where all developers work in short-lived branches or directly in the trunk, the main codebase. The "trunk" is the main branch of your repository. If you're using Git, it will be either the main
or master
branch. Trunk-based development avoids the "merge hell" problem caused by long-lived branches.
So, how do feature flags fit into trunk-based development?
The only way to ensure the trunk is always releasable is to hide incomplete features behind feature flags. You continue pushing changes to the trunk as you work on the feature while the feature flag remains turned off on the main branch. When the feature is complete, you turn on the feature flag and release it to production.
How I Used Feature Flags for A/B Testing On My Website
A/B testing (split testing) is an experiment where two or more variants of a page (or feature) are shown randomly to users. Statistical analysis is performed in the background to determine which variation performs better for a given conversion goal.
Here's an example A/B test I performed on my website:
The hypothesis was that removing the image and focusing on the benefits would make more people want to subscribe. I measure this using the conversion rate, which is the number of people visiting the page divided by the number of people subscribing.
I'm using a platform called Posthog to run experiments, which automatically calculates the results.
You can see that the test variant has a significantly higher conversion rate, so it becomes the winner of the A/B test.
Takeaway
The ability to dynamically turn features on or off without deploying the code is like a superpower. Feature flags give you this ability with very little work.
You can work with feature flags in .NET by installing the Microsoft.FeatureManagement
library. Feature flags build on top of the .NET configuration system, and you can check the feature flag state using the IFeatureManager
service.
Another use case for feature flags is A/B testing. I run weekly experiments on my website, testing changes that will improve my conversion rate. Feature flags help me decide which version of the website to show to the user. And then, I can measure results based on the user's actions.
I also made a video about feature flagging in .NET, and you can watch it here if you want to learn more.
Hope this was valuable.
Stay awesome!
P.S. Whenever you're ready, there are 2 ways I can help you:
Pragmatic Clean Architecture: This comprehensive course will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture. Join 1,000+ students here.
Patreon Community: Think like a senior software engineer with access to the source code I use in my YouTube videos and exclusive discounts for my courses. Join 860+ engineers here.