The riskiest moment in most deployments isn't the deploy itself — it's the release. Feature flags separate the two: you can ship code to production dark, turn it on for 1% of traffic, watch it, and turn it back off in seconds if something's wrong, without touching the deployment pipeline at all.
- Deploying and releasing are different events. Flags let you decouple them so a bad release is a config change, not a rollback.
- Microsoft.FeatureManagement gives you flag evaluation, targeting, and percentage rollout without adopting a full third-party platform.
- Back flags with Azure App Configuration for dynamic, no-redeploy toggling in production.
- Every flag is technical debt the moment it's fully rolled out. Track flag age and delete the ones that have outlived their purpose.
Setup
dotnet add package Microsoft.FeatureManagement.AspNetCore
{
"FeatureManagement": {
"NewCheckoutFlow": true,
"BulkExportApi": false
}
}
builder.Services.AddFeatureManagement();
Gating an endpoint or a service
The [FeatureGate] attribute works on MVC controllers and actions. For Minimal APIs or arbitrary business logic, inject IFeatureManager directly and check it explicitly — which also makes intent obvious in a code review.
app.MapPost("/api/checkout", async (
CheckoutRequest req, IFeatureManager features, ICheckoutService svc, CancellationToken ct) =>
{
var result = await features.IsEnabledAsync("NewCheckoutFlow")
? await svc.CheckoutV2Async(req, ct)
: await svc.CheckoutV1Async(req, ct);
return Results.Ok(result);
});
Gradual rollout with targeting filters
A flag that's simply on or off doesn't let you roll out gradually. The targeting and percentage filters that ship with Microsoft.FeatureManagement let you roll a feature out to a percentage of users, or to specific groups, before flipping it on for everyone.
{
"FeatureManagement": {
"NewCheckoutFlow": {
"EnabledFor": [
{
"Name": "Microsoft.Targeting",
"Parameters": {
"Audience": {
"DefaultRolloutPercentage": 10,
"Groups": [ { "Name": "BetaTesters", "RolloutPercentage": 100 } ]
}
}
}
]
}
}
}
builder.Services.AddFeatureManagement()
.AddFeatureFilter();
builder.Services.AddScoped();
Dynamic flags without a redeploy
Flags in appsettings.json still need a deploy to change. Backing them with Azure App Configuration lets you flip a flag from a portal or CLI and have running instances pick it up on the next refresh cycle — no redeploy, no restart.
builder.Configuration.AddAzureAppConfiguration(options =>
{
options.Connect(builder.Configuration.GetConnectionString("AppConfig"))
.UseFeatureFlags(ff => ff.PollInterval = TimeSpan.FromSeconds(30));
});
builder.Services.AddAzureAppConfiguration();
builder.Services.AddFeatureManagement();
// after building the app
app.UseAzureAppConfiguration();