In my previous blog, I discussed feature flag and how this can be implemented using simple Boolean flag. Please refer to the below link for the same.
In this tutorial, we are going to discuss the below topics
- Advanced Feature Flag
- Custom Feature Flag
In the previous blog, we have seen that feature flagging is implemented using a simple Boolean flag.
The source code can be downloaded from the GitHub
Advanced Feature flag
Let us consider a scenario, where we have consumed a weather forecast API, and by that time, a new API has been released. How this can be included? Let us see how the Advanced Feature flag would help us in this scenario.
I have introduced a new property in Weatherforecast.cs class for explaining this Advanced Feature flag
//New Feature for advanced Advanced feature flag
public string? SnowPercentage { get; set; }
In WeatherforecastController.cs, Let us forecast the existing method to create as an Old algorithm method as below
private async Task<IEnumerable<WeatherForecast>> OldForecastAlgorithm()
{
//Feature Flag - this needs to be configure in AppSettings.json
var isTemperatureFEnabled = await _featureManager.IsEnabledAsync("TemperatureF");
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
//check whether the Feature Flag is enabled
TemperatureF = isTemperatureFEnabled ? (int)(Random.Shared.Next(-20, 55) * 1.8) + 32 : null,
//To Identify the old algorithm, "OLD" has been suffixed
SnowPercentage = $"{Random.Shared.Next(0, 100)} % - OLD",
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
To identify the Old Algorithm, in Summary, I have suffixed "OLD".
For demo purpose, let me replicate the OldForecastAlgorithm method logic into a new method as below
private async Task<IEnumerable<WeatherForecast>> NewForecastAlgorithm()
{
//Feature Flag - this needs to be configure in AppSettings.json
var isTemperatureFEnabled = await _featureManager.IsEnabledAsync("TemperatureF");
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
//check whether the Feature Flag is enabled
TemperatureF = isTemperatureFEnabled ? (int)(Random.Shared.Next(-20, 55) * 1.8) + 32 : null,
//To Identify the New algorithm, "NEW" has been suffixed
SnowPercentage = $"{Random.Shared.Next(0, 100)} % - NEW",
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
To identify the new algorithm, I have suffixed “NEW” in SnowPercentage property.
Now, let us go ahead and add a new feature flag for the new algorithm in GetNewFeature() endpoint in WeatherforecastController.cs
var newAlgorithm = await _featureManager.IsEnabledAsync("NewAlgorithmEnabled");
The updated GetNewFeature() endpoint looks like below.
[HttpGet("newfeature")]
[FeatureGate("NewFeature")]
public async Task<IEnumerable<WeatherForecast>> GetNewFeature()
{
var newAlgorithm = await _featureManager.IsEnabledAsync("NewAlgorithmEnabled");
return newAlgorithm? await NewForecastAlgorithm() : await OldForecastAlgorithm();
}
So far so good. Now let us look at the Appsettings.json file and make sure that new feature flag for new algorithm has been added.
"FeatureManagement": {
"TemperatureF": false,
"NewFeature": true,
"NewAlgorithmEnabled": {
"EnabledFor": [
{
"Name": "Microsoft.Percentage",
"Parameters": {
"Value": 50
}
}
]
}
}
Here “NewAlgorithEnabled” is the settings we are configuring to implement advanced feature flag. So far, we have played around with boolean flags. Here, "EnabledFor" is a property that take a list of parameters to configure the features. “Microsoft.Percentage” is built-in with feature management library. The “Value” which makes the feature flag decision.
There is a final step which needs to be added in Program.cs as below
builder.Services.AddFeatureManagement()
.AddFeatureFilter<PercentageFilter>();
Let's go ahead and execute the endpoint “NewFeature”
For the first run, the output would be as below
[
{
"date": "2022-09-26T17:04:02.0183655-04:00",
"temperatureC": 54,
"summary": "Warm",
"snowPercentage": "50% - NEW"
},
{
"date": "2022-09-27T17:04:02.0183813-04:00",
"temperatureC": 15,
"summary": "Mild",
"snowPercentage": "35% - NEW"
},
{
"date": "2022-09-28T17:04:02.0183825-04:00",
"temperatureC": -18,
"summary": "Mild",
"snowPercentage": "48% - NEW"
},
Looks like new Algorithm has been triggered. Let us execute the endpoint and see how the algorithms are flipping
[
{
"date": "2022-09-26T17:05:59.1966046-04:00",
"temperatureC": -15,
"summary": "Hot",
"snowPercentage": "72% - OLD"
},
{
"date": "2022-09-27T17:05:59.196609-04:00",
"temperatureC": 18,
"summary": "Freezing",
"snowPercentage": "2% - OLD"
},
{
"date": "2022-09-28T17:05:59.1966094-04:00",
"temperatureC": 38,
"summary": "Chilly",
"snowPercentage": "38% - OLD"
},
Looks like the algorithms are switching between old and new on an average of 50 percentage. You can adjust this value in appsettings as you wish.
If you look at the definition of PercentageFilter, you can see that the IFeatureFilter interface has been implemented as below
[FilterAlias("Microsoft.Percentage")]
public class PercentageFilter : IFeatureFilter, IFeatureFilterMetadata
{
private const string Alias = "Microsoft.Percentage";
}
Please read out the documentation for the built-in feature filters and the details related to Feature flag managements.
As we have seen that the Built-in feature has been created using the IFeatureFilter interface, there are possibilities to create custom feature filter. Now we are going to explore how custom filters can be created.
Custom Feature Flag
Now let us go ahead and create a custom filter using the interface IFeatureFilter.
[FilterAlias("Browser")]
public class BrowserFilter : IFeatureFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public BrowserFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
{
if(_httpContextAccessor.HttpContext !=null)
{
var userAgent = _httpContextAccessor
.HttpContext
.Request.Headers["User-Agent"].ToString();
var _browserSettings = context.Parameters.Get<BrowserFilterSettings>();
return Task.FromResult(_browserSettings.Allowed.Any(x => userAgent.Contains(x)));
}
return Task.FromResult(false);
}
}
Where BrowserFilterSettings will be a class as below. Here I have used the extension method “Contains” to check browser. This is because, for an instance, Edge has multiple version as mentioned in the link
public class BrowserFilterSettings
{
public string[] Allowed { get; set; }
}
Now let us go ahead and register this custom filter in Program.cs as below
// Add services to the container.
builder.Services.AddFeatureManagement()
.AddFeatureFilter<PercentageFilter>()
.AddFeatureFilter<BrowserFilter>();
As we have leveraged HttpContextAccessor, we need to register this as well into Program.cs as below
builder.Services.AddHttpContextAccessor();
Now let us go ahead and configure the flag properties in AppSettings.json.
As we have already configured a list of properties for configuring the feature filters, let us go ahead and another value into the array as below
"NewAlgorithmEnabled": {
"EnabledFor": [
{
"Name": "Microsoft.Percentage",
"Parameters": {
"Value": 50
}
},
{
"Name": "Browser",
"Parameters": {
"Allowed": [ "Edg" ]
}
}
]
}
I have configured this feature for Edge browser. As we have declared “Allowed” as a string array, we are passing the value as array.
Now let us go ahead and execute “NewFeature” in Edge browser. You can see that; the new algorithm will be executed all the time. If we execute the same in Chrome, you can see that the always will be switched as the feature flag Percentage will be considered.
So far, we have seen the advanced and custom filters for feature management. Thank you for reading my blog and please leave your comments in the comment box below.