DEV Community

Cover image for Feature Flags in .NET
ZenOfCode
ZenOfCode

Posted on • Originally published at zencoding.hashnode.dev

Feature Flags in .NET

Feature Flags in .NET: A Minimal API with Toggle Power (Using Microsoft.FeatureManagement)

Feature flags give you fine-grained control over your application’s behavior—turn features on or off, run experiments, and roll back instantly without redeploying. In this tutorial, you’ll integrate the first‑party Microsoft.FeatureManagement package into a .NET Minimal API, source flags from configuration and a custom provider, and explore both attribute- and code-based gating.


Why Use Microsoft.FeatureManagement?

  • First‑party support: Official Microsoft package, integrates with .NET configuration and dependency injection.
  • Flexible filters: Time windows, percentages, custom logic.
  • Clean architecture alignment: Business logic stays decoupled from flag storage.

Table of Contents


Project Setup

dotnet new webapi -n FlagsWithMicrosoft cd FlagsWithMicrosoft 
Enter fullscreen mode Exit fullscreen mode

Edit your .csproj to target .NET 6+ and remove the default Weather sample. Then install the feature management package:

dotnet add package Microsoft.FeatureManagement 
Enter fullscreen mode Exit fullscreen mode

Define Feature Flags in Configuration

Using appsettings.json, declare flags under a FeatureManagement section. This keeps flags in config, supporting environment-based overrides.

// appsettings.json { "FeatureManagement": { "NewUI": true, "BetaEndpoint": false, "TimeLimitedFeature": { "EnabledFor": [ { "Name": "TimeWindow", "Parameters": { "Start": "2025-05-01", "End": "2025-06-01" } } ] } } } 
Enter fullscreen mode Exit fullscreen mode
  • NewUI: simple on/off.
  • BetaEndpoint: off by default.
  • TimeLimitedFeature: auto‑enabled during a date range.

Wire Up Microsoft.FeatureManagement

In Program.cs, register feature management services:

using Microsoft.FeatureManagement; var builder = WebApplication.CreateBuilder(args); // Add feature management and built-in filters builder.Services.AddFeatureManagement() .AddFeatureFilter<Microsoft.FeatureManagement.FeatureFilters.TimeWindowFilter>(); var app = builder.Build(); 
Enter fullscreen mode Exit fullscreen mode

This reads FeatureManagement from configuration and enables the built‑in TimeWindowFilter.


Use Flags in Endpoints

You can check flags manually using IFeatureManagerSnapshot:

using Microsoft.FeatureManagement; app.MapGet("/new-ui-data", async ([FromServices] IFeatureManagerSnapshot fm) => { if (!await fm.IsEnabledAsync("NewUI")) return Results.NotFound("New UI not available."); return Results.Ok(new { message = "Welcome to the new UI!" }); }); 
Enter fullscreen mode Exit fullscreen mode

Above: manual check with IsEnabledAsync().


FeatureGate Attributes

For attribute-based gating, add [FeatureGate] on endpoints or controllers. This automatically returns 404 if the feature is disabled.

using Microsoft.FeatureManagement; // Attribute on Minimal API endpoint app.MapGet("/beta-attr", () => "Beta data via attribute") .RequireFeature("BetaEndpoint"); // In MVC controller [ApiController] [Route("api/[controller]")] public class ReportsController : ControllerBase { [HttpGet("special")] [FeatureGate("TimeLimitedFeature")] public IActionResult GetTimeLimitedReport() { return Ok("Time-limited report data"); } } 
Enter fullscreen mode Exit fullscreen mode
  • .RequireFeature("BetaEndpoint") for Minimal APIs
  • [FeatureGate("TimeLimitedFeature")] for controllers

Custom Feature Provider

Implement IFeatureDefinitionProvider to fetch flags from an external service.

// Infrastructure/ExternalFeatureProvider.cs using Microsoft.FeatureManagement; using Microsoft.FeatureManagement.FeatureDefinitions; public class ExternalFeatureProvider : IFeatureDefinitionProvider { private readonly IExternalFlagRepository _repo; public ExternalFeatureProvider(IExternalFlagRepository repo) => _repo = repo; public async Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName) { var flag = await _repo.FetchFlagAsync(featureName); return new FeatureDefinition( name: featureName, enabledFor: flag.Enabled ? new[] { new FeatureFilterConfiguration("AlwaysOn") } : Array.Empty<FeatureFilterConfiguration>()); } public async Task<IEnumerable<FeatureDefinition>> GetAllFeatureDefinitionsAsync() { var flags = await _repo.FetchAllFlagsAsync(); return flags.Select(f => new FeatureDefinition( f.Name, f.Enabled ? new[] { new FeatureFilterConfiguration("AlwaysOn") } : Array.Empty<FeatureFilterConfiguration>())); } } 
Enter fullscreen mode Exit fullscreen mode
// Infrastructure/ExternalFlagRepository.cs public interface IExternalFlagRepository { Task<FlagDto> FetchFlagAsync(string name); Task<IEnumerable<FlagDto>> FetchAllFlagsAsync(); } public class InMemoryExternalFlagRepo : IExternalFlagRepository { private readonly Dictionary<string,bool> _store = new() { ["NewUI"] = true, ["BetaEndpoint"] = false }; public Task<FlagDto> FetchFlagAsync(string name) => Task.FromResult(new FlagDto { Name = name, Enabled = _store.GetValueOrDefault(name) }); public Task<IEnumerable<FlagDto>> FetchAllFlagsAsync() => Task.FromResult(_store.Select(kv => new FlagDto { Name = kv.Key, Enabled = kv.Value })); } public record FlagDto { public string Name { get; init; } public bool Enabled { get; init; } } 
Enter fullscreen mode Exit fullscreen mode

Register in Program.cs:

builder.Services.AddSingleton<IExternalFlagRepository, InMemoryExternalFlagRepo>(); builder.Services.AddSingleton<IFeatureDefinitionProvider, ExternalFeatureProvider>(); 
Enter fullscreen mode Exit fullscreen mode

Testing

  • Unit test attribute endpoints by mocking IFeatureManagerSnapshot.
  • Integration test custom provider by seeding InMemoryExternalFlagRepo.

Example xUnit test for manual check:

[Fact] public async Task NewUI_ReturnsNotFound_WhenFlagOff() { var fm = new Mock<IFeatureManagerSnapshot>(); fm.Setup(x => x.IsEnabledAsync("NewUI", It.IsAny<CancellationToken>())).ReturnsAsync(false); var result = await new UIEndpoint(fm.Object).Get(); Assert.IsType<NotFoundObjectResult>(result); } 
Enter fullscreen mode Exit fullscreen mode

Feature flags give you the serenity to release often and safely. Stay Zen.


References


🧘‍♂️ Like this kind of content?

Follow my dev blog → ZenOfCode

Or drop me a follow here on Dev.to 💬

Top comments (0)