{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreiclid2a4ipcwtawisrdn6t2gamkkpied5xik7s66u55hvbrzvyyva",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mp6uxic5m4d2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreiav5etu2febt25whpmnrspokc3cvihfcx6reimxud2ap7rkhi7lvm"
    },
    "mimeType": "image/webp",
    "size": 65080
  },
  "path": "/hossein_esmati/feature-toggles-feature-management-in-net-and-azure-4g63",
  "publishedAt": "2026-06-26T11:45:13.000Z",
  "site": "https://dev.to",
  "tags": [
    "feature",
    "toggles",
    "dotnet",
    "core",
    "Comprehensive Guide to Microservices Architecture in .NET Core, Cloud and Azure",
    ".NET feature management - Microsoft Learn",
    "Microsoft Feature Management",
    "Azure App Configuration",
    "Feature Flags Best Practices",
    "Microsoft.FeatureManagement.AspNetCore",
    "Microsoft.Azure.AppConfiguration.AspNetCore",
    "LaunchDarkly Feature Flag Guide"
  ],
  "textContent": "_This article is part of the Comprehensive Guide to Microservices Architecture in .NET Core, Cloud and Azure series._\n\n##  Understanding the Feature Ecosystem\n\n###  Microsoft.AspNetCore.Http.Features\n\n**Purpose:** Defines low-level HTTP feature interfaces used by the ASP.NET Core hosting and middleware pipeline.\n\n**Think of it as:** \"How ASP.NET Core describes what an HTTP request/response can do at the protocol level.\"\n\n\n\n    // Accessing low-level HTTP features\n    app.Use(async (context, next) =>\n    {\n        var features = context.Features;\n\n        // Connection information\n        var connection = features.Get<IHttpConnectionFeature>();\n        var remoteIp = connection?.RemoteIpAddress;\n        var localPort = connection?.LocalPort;\n\n        // Request body control\n        var bodyControl = features.Get<IHttpBodyControlFeature>();\n        bodyControl?.AllowSynchronousIO = true; // Use with caution\n\n        // HTTP/2 or WebSocket upgrade\n        var upgradeFeature = features.Get<IHttpUpgradeFeature>();\n        var isUpgradeable = upgradeFeature?.IsUpgradableRequest ?? false;\n\n        await next();\n    });\n\n\n**Key feature interfaces:**\n\n  * `IHttpRequestFeature` — Raw HTTP request data\n  * `IHttpResponseFeature` — Raw HTTP response data\n  * `IHttpConnectionFeature` — Connection info (remote IP, local port)\n  * `IHttpUpgradeFeature` — WebSocket or HTTP/2 upgrade handling\n  * `IHttpRequestBodyDetectionFeature` — Request body detection (.NET 8+)\n  * `IHttpResponseBodyFeature` — Response body streaming (.NET 6+)\n\n\n\n**When to use:** Custom middleware, hosting adapters, or low-level protocol handling. Most application code never needs this.\n\n###  Microsoft.Extensions.Features\n\n**Completely unrelated** to HTTP — this is a generic feature collection abstraction.\n\n**Purpose:** Provides a reusable feature pattern for any system, not limited to HTTP.\n\n**Think of it as:** \"A general-purpose feature system inspired by ASP.NET Core's pattern, but framework-agnostic.\"\n\n\n\n    // Define custom feature\n    public interface IMyCustomFeature\n    {\n        string GetValue();\n        void SetValue(string value);\n    }\n\n    public class MyCustomFeatureImpl : IMyCustomFeature\n    {\n        private string _value = string.Empty;\n\n        public string GetValue() => _value;\n        public void SetValue(string value) => _value = value;\n    }\n\n    // Use feature collection\n    var features = new FeatureCollection();\n    features.Set<IMyCustomFeature>(new MyCustomFeatureImpl());\n\n    var feature = features.Get<IMyCustomFeature>();\n    feature?.SetValue(\"Hello, Features!\");\n    Console.WriteLine(feature?.GetValue());\n\n\n**Used by:**\n\n  * YARP (Yet Another Reverse Proxy)\n  * .NET Aspire components\n  * gRPC services\n  * Custom extensibility layers\n\n\n\n**Not related to:** Feature flags or feature management.\n\n##  Microsoft.FeatureManagement.AspNetCore\n\nThe primary library for implementing feature flags in .NET applications.\n\n**Purpose:** Runtime feature toggles controlled by configuration, enabling progressive rollouts, A/B testing, and safe deployments.\n\n**Key capabilities:**\n\n  * Declarative `[FeatureGate]` attributes\n  * `IFeatureManager` for programmatic checks\n  * Dynamic configuration via multiple sources\n  * Targeting filters for user/group-based rollouts\n  * Variants for A/B testing\n  * Time window filters\n  * Custom filters\n\n\n\n###  Basic Setup (.NET 9)\n\n\n    // Program.cs\n    var builder = WebApplication.CreateBuilder(args);\n\n    // Add feature management\n    builder.Services.AddFeatureManagement();\n\n    var app = builder.Build();\n\n    app.MapGet(\"/api/orders\", async (IFeatureManager featureManager) =>\n    {\n        if (await featureManager.IsEnabledAsync(\"NewOrderApi\"))\n        {\n            return Results.Ok(\"New order API\");\n        }\n\n        return Results.Ok(\"Legacy order API\");\n    });\n\n    app.Run();\n\n\n\n    // appsettings.json\n    {\n      \"FeatureManagement\": {\n        \"NewOrderApi\": true,\n        \"AdvancedReporting\": false,\n        \"BetaUI\": true\n      }\n    }\n\n\n###  Using Feature Gates\n\n\n    // Controller-level feature gate\n    [ApiController]\n    [Route(\"api/[controller]\")]\n    public class OrdersController(\n        IFeatureManager featureManager,\n        IOrderService orderService,\n        ILogger<OrdersController> logger) : ControllerBase\n    {\n        // Action-level feature gate\n        [FeatureGate(\"AdvancedReporting\")]\n        [HttpGet(\"advanced-report\")]\n        public async Task<IActionResult> GetAdvancedReport(CancellationToken ct)\n        {\n            var report = await orderService.GenerateAdvancedReportAsync(ct);\n            return Ok(report);\n        }\n\n        // Programmatic feature check\n        [HttpPost]\n        public async Task<IActionResult> CreateOrder(\n            CreateOrderRequest request,\n            CancellationToken ct)\n        {\n            var order = await orderService.CreateAsync(request, ct);\n\n            // Conditional logic based on feature flag\n            if (await featureManager.IsEnabledAsync(\"NewCheckoutFlow\"))\n            {\n                logger.LogInformation(\"Using new checkout flow for order {OrderId}\", order.Id);\n                await orderService.ProcessWithNewCheckoutAsync(order, ct);\n            }\n            else\n            {\n                logger.LogInformation(\"Using legacy checkout flow for order {OrderId}\", order.Id);\n                await orderService.ProcessWithLegacyCheckoutAsync(order, ct);\n            }\n\n            return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);\n        }\n\n        [HttpGet(\"{id:guid}\")]\n        public async Task<IActionResult> GetOrder(Guid id, CancellationToken ct)\n        {\n            var order = await orderService.GetByIdAsync(id, ct);\n            return order is null ? NotFound() : Ok(order);\n        }\n    }\n\n\n###  Minimal APIs with Feature Gates\n\n\n    // Custom extension for feature-gated endpoints\n    public static class FeatureEndpointExtensions\n    {\n        public static RouteHandlerBuilder RequireFeature(\n            this RouteHandlerBuilder builder,\n            string featureName)\n        {\n            return builder.AddEndpointFilter(async (context, next) =>\n            {\n                var featureManager = context.HttpContext.RequestServices\n                    .GetRequiredService<IFeatureManager>();\n\n                if (!await featureManager.IsEnabledAsync(featureName))\n                {\n                    return Results.NotFound(new\n                    {\n                        message = \"This feature is not currently available\"\n                    });\n                }\n\n                return await next(context);\n            });\n        }\n    }\n\n    // Usage\n    app.MapGet(\"/api/beta/dashboard\", () => Results.Ok(\"Beta dashboard\"))\n        .RequireFeature(\"BetaDashboard\");\n\n    app.MapPost(\"/api/orders/advanced\", async (\n        CreateOrderRequest request,\n        IOrderService orderService,\n        CancellationToken ct) =>\n    {\n        var order = await orderService.CreateAdvancedAsync(request, ct);\n        return Results.Created($\"/api/orders/{order.Id}\", order);\n    })\n    .RequireFeature(\"AdvancedOrderCreation\");\n\n\n##  Azure App Configuration Integration\n\nAzure App Configuration provides centralized feature flag management with real-time updates.\n\n> Read more: .NET feature management - Microsoft Learn\n\n###  Setup with Azure App Configuration\n\n\n    // Install packages:\n    // Microsoft.Azure.AppConfiguration.AspNetCore\n    // Microsoft.FeatureManagement.AspNetCore\n\n    // Program.cs\n    var builder = WebApplication.CreateBuilder(args);\n\n    // Connect to Azure App Configuration\n    builder.Configuration.AddAzureAppConfiguration(options =>\n    {\n        var connectionString = builder.Configuration[\"AppConfig:ConnectionString\"]\n            ?? throw new InvalidOperationException(\"AppConfig connection string not found\");\n\n        options\n            .Connect(connectionString)\n            .ConfigureRefresh(refresh =>\n            {\n                // Refresh configuration when this sentinel key changes\n                refresh.Register(\"Settings:Sentinel\", refreshAll: true)\n                       .SetCacheExpiration(TimeSpan.FromSeconds(30));\n            })\n            .UseFeatureFlags(featureFlagOptions =>\n            {\n                // Cache feature flags for 30 seconds\n                featureFlagOptions.CacheExpirationInterval = TimeSpan.FromSeconds(30);\n\n                // Optional: Select specific feature flags by label\n                featureFlagOptions.Label = builder.Environment.EnvironmentName;\n            });\n    });\n\n    // Add Azure App Configuration middleware support\n    builder.Services.AddAzureAppConfiguration();\n\n    // Add feature management\n    builder.Services.AddFeatureManagement();\n\n    var app = builder.Build();\n\n    // Enable configuration refresh middleware\n    app.UseAzureAppConfiguration();\n\n    app.Run();\n\n\n###  User-Based Targeting with Filters\n\n**Targeting Filter** enables user-specific and group-based feature rollouts.\n\n\n\n    // Azure App Configuration - Feature flag with targeting\n    {\n      \"id\": \"NewDashboard\",\n      \"description\": \"New dashboard experience\",\n      \"enabled\": true,\n      \"conditions\": {\n        \"client_filters\": [\n          {\n            \"name\": \"Microsoft.Targeting\",\n            \"parameters\": {\n              \"Audience\": {\n                \"Users\": [\n                  \"alice@company.com\",\n                  \"bob@company.com\"\n                ],\n                \"Groups\": [\n                  {\n                    \"Name\": \"BetaTesters\",\n                    \"RolloutPercentage\": 50\n                  },\n                  {\n                    \"Name\": \"InternalUsers\",\n                    \"RolloutPercentage\": 100\n                  }\n                ],\n                \"DefaultRolloutPercentage\": 10\n              }\n            }\n          }\n        ]\n      }\n    }\n\n\n\n    // Configure targeting context accessor\n    builder.Services.AddHttpContextAccessor();\n\n    builder.Services.AddFeatureManagement()\n        .WithTargeting<CustomTargetingContextAccessor>();\n\n    // Custom targeting context implementation\n    public class CustomTargetingContextAccessor(\n        IHttpContextAccessor httpContextAccessor) : ITargetingContextAccessor\n    {\n        public ValueTask<TargetingContext> GetContextAsync()\n        {\n            var httpContext = httpContextAccessor.HttpContext;\n\n            if (httpContext?.User?.Identity?.IsAuthenticated != true)\n            {\n                return new ValueTask<TargetingContext>((TargetingContext)null!);\n            }\n\n            var user = httpContext.User;\n\n            var context = new TargetingContext\n            {\n                UserId = user.FindFirst(ClaimTypes.Email)?.Value\n                         ?? user.Identity.Name\n                         ?? \"anonymous\",\n                Groups = user.Claims\n                    .Where(c => c.Type == \"groups\" || c.Type == ClaimTypes.Role)\n                    .Select(c => c.Value)\n                    .ToList()\n            };\n\n            return new ValueTask<TargetingContext>(context);\n        }\n    }\n\n\n###  Percentage-Based Rollout\n\n\n    // Gradual rollout to percentage of users\n    {\n      \"id\": \"NewRecommendationEngine\",\n      \"enabled\": true,\n      \"conditions\": {\n        \"client_filters\": [\n          {\n            \"name\": \"Microsoft.Percentage\",\n            \"parameters\": {\n              \"Value\": 25\n            }\n          }\n        ]\n      }\n    }\n\n\n###  Time Window Filter\n\n\n    // Feature available only during specific time window\n    {\n      \"id\": \"BlackFridaySale\",\n      \"enabled\": true,\n      \"conditions\": {\n        \"client_filters\": [\n          {\n            \"name\": \"Microsoft.TimeWindow\",\n            \"parameters\": {\n              \"Start\": \"2025-11-29T00:00:00Z\",\n              \"End\": \"2025-12-02T00:00:00Z\"\n            }\n          }\n        ]\n      }\n    }\n\n\n##  Feature Variants for A/B Testing\n\nFeature variants enable A/B testing and multi-variate experiments.\n\n\n\n    // Feature with variants\n    {\n      \"id\": \"RecommendationAlgorithm\",\n      \"enabled\": true,\n      \"allocation\": {\n        \"percentile\": [\n          {\n            \"variant\": \"Collaborative\",\n            \"from\": 0,\n            \"to\": 33\n          },\n          {\n            \"variant\": \"ContentBased\",\n            \"from\": 33,\n            \"to\": 66\n          },\n          {\n            \"variant\": \"Hybrid\",\n            \"from\": 66,\n            \"to\": 100\n          }\n        ],\n        \"seed\": \"user_id\",\n        \"default_when_disabled\": \"Collaborative\"\n      },\n      \"variants\": [\n        {\n          \"name\": \"Collaborative\",\n          \"configuration_value\": {\n            \"algorithm\": \"collaborative_filtering\",\n            \"weight\": 1.0\n          }\n        },\n        {\n          \"name\": \"ContentBased\",\n          \"configuration_value\": {\n            \"algorithm\": \"content_based\",\n            \"weight\": 0.8\n          }\n        },\n        {\n          \"name\": \"Hybrid\",\n          \"configuration_value\": {\n            \"algorithm\": \"hybrid\",\n            \"collaborative_weight\": 0.6,\n            \"content_weight\": 0.4\n          }\n        }\n      ]\n    }\n\n\n\n    // Using feature variants\n    public class RecommendationService(\n        IFeatureManager featureManager,\n        IHttpContextAccessor httpContextAccessor,\n        ILogger<RecommendationService> logger)\n    {\n        public async Task<IEnumerable<Product>> GetRecommendationsAsync(\n            string userId,\n            CancellationToken ct = default)\n        {\n            var variant = await featureManager.GetVariantAsync(\n                \"RecommendationAlgorithm\",\n                httpContextAccessor.HttpContext,\n                ct);\n\n            if (variant is null)\n            {\n                logger.LogInformation(\"Using default recommendation algorithm for user {UserId}\", userId);\n                return await GetCollaborativeRecommendationsAsync(userId, ct);\n            }\n\n            var config = variant.Configuration?.Get<RecommendationConfig>();\n\n            logger.LogInformation(\n                \"Using {Algorithm} algorithm (variant: {Variant}) for user {UserId}\",\n                config?.Algorithm,\n                variant.Name,\n                userId);\n\n            return config?.Algorithm switch\n            {\n                \"collaborative_filtering\" => await GetCollaborativeRecommendationsAsync(userId, ct),\n                \"content_based\" => await GetContentBasedRecommendationsAsync(userId, ct),\n                \"hybrid\" => await GetHybridRecommendationsAsync(userId, config, ct),\n                _ => await GetCollaborativeRecommendationsAsync(userId, ct)\n            };\n        }\n\n        private async Task<IEnumerable<Product>> GetHybridRecommendationsAsync(\n            string userId,\n            RecommendationConfig config,\n            CancellationToken ct)\n        {\n            var collaborative = await GetCollaborativeRecommendationsAsync(userId, ct);\n            var contentBased = await GetContentBasedRecommendationsAsync(userId, ct);\n\n            // Blend results based on weights\n            return BlendRecommendations(\n                collaborative,\n                contentBased,\n                config.CollaborativeWeight ?? 0.5,\n                config.ContentWeight ?? 0.5);\n        }\n\n        private IEnumerable<Product> BlendRecommendations(\n            IEnumerable<Product> source1,\n            IEnumerable<Product> source2,\n            double weight1,\n            double weight2)\n        {\n            // Implementation details...\n            return source1.Take(10);\n        }\n\n        private async Task<IEnumerable<Product>> GetCollaborativeRecommendationsAsync(\n            string userId,\n            CancellationToken ct)\n        {\n            await Task.Delay(10, ct);\n            return [];\n        }\n\n        private async Task<IEnumerable<Product>> GetContentBasedRecommendationsAsync(\n            string userId,\n            CancellationToken ct)\n        {\n            await Task.Delay(10, ct);\n            return [];\n        }\n    }\n\n    public record RecommendationConfig\n    {\n        public string? Algorithm { get; init; }\n        public double? Weight { get; init; }\n        public double? CollaborativeWeight { get; init; }\n        public double? ContentWeight { get; init; }\n    }\n\n\n##  Custom Feature Filters\n\nCreate custom filters for business-specific scenarios.\n\n\n\n    // Ring-based deployment filter\n    [FilterAlias(\"RingBased\")]\n    public class RingBasedFeatureFilter(\n        IHttpContextAccessor httpContextAccessor,\n        ILogger<RingBasedFeatureFilter> logger) : IFeatureFilter\n    {\n        public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)\n        {\n            var parameters = context.Parameters.Get<RingSettings>();\n            var httpContext = httpContextAccessor.HttpContext;\n\n            if (httpContext is null)\n            {\n                return Task.FromResult(false);\n            }\n\n            // Get deployment ring from header or claim\n            var deploymentRing = httpContext.Request.Headers[\"X-Deployment-Ring\"].FirstOrDefault()\n                ?? httpContext.User.FindFirst(\"deployment_ring\")?.Value;\n\n            if (string.IsNullOrEmpty(deploymentRing))\n            {\n                logger.LogDebug(\"No deployment ring found, feature disabled\");\n                return Task.FromResult(false);\n            }\n\n            var ringOrder = new[] { \"Canary\", \"Ring1\", \"Ring2\", \"Ring3\", \"Production\" };\n            var userRingIndex = Array.IndexOf(ringOrder, deploymentRing);\n            var targetRingIndex = Array.IndexOf(ringOrder, parameters.TargetRing);\n\n            if (userRingIndex == -1 || targetRingIndex == -1)\n            {\n                logger.LogWarning(\"Invalid ring configuration: User={UserRing}, Target={TargetRing}\",\n                    deploymentRing, parameters.TargetRing);\n                return Task.FromResult(false);\n            }\n\n            var isEnabled = userRingIndex <= targetRingIndex;\n\n            logger.LogDebug(\n                \"Ring-based evaluation: User={UserRing}, Target={TargetRing}, Enabled={IsEnabled}\",\n                deploymentRing,\n                parameters.TargetRing,\n                isEnabled);\n\n            return Task.FromResult(isEnabled);\n        }\n    }\n\n    public record RingSettings\n    {\n        public required string TargetRing { get; init; }\n    }\n\n    // Register custom filter\n    builder.Services.AddFeatureManagement()\n        .AddFeatureFilter<RingBasedFeatureFilter>();\n\n\n\n    // Configuration\n    {\n      \"FeatureManagement\": {\n        \"NewPaymentGateway\": {\n          \"EnabledFor\": [\n            {\n              \"Name\": \"RingBased\",\n              \"Parameters\": {\n                \"TargetRing\": \"Ring1\"\n              }\n            }\n          ]\n        }\n      }\n    }\n\n\n###  Geographic-Based Filter\n\n\n    [FilterAlias(\"Geographic\")]\n    public class GeographicFeatureFilter(\n        IHttpContextAccessor httpContextAccessor,\n        ILogger<GeographicFeatureFilter> logger) : IFeatureFilter\n    {\n        public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)\n        {\n            var parameters = context.Parameters.Get<GeographicSettings>();\n            var httpContext = httpContextAccessor.HttpContext;\n\n            if (httpContext is null)\n            {\n                return Task.FromResult(false);\n            }\n\n            // Get country from header, claim, or IP geolocation\n            var country = httpContext.Request.Headers[\"X-Country-Code\"].FirstOrDefault()\n                ?? httpContext.User.FindFirst(\"country\")?.Value\n                ?? \"US\"; // default\n\n            var isEnabled = parameters.AllowedCountries?.Contains(\n                country,\n                StringComparer.OrdinalIgnoreCase) ?? false;\n\n            logger.LogDebug(\n                \"Geographic evaluation: Country={Country}, Allowed={Allowed}, Enabled={IsEnabled}\",\n                country,\n                string.Join(\",\", parameters.AllowedCountries ?? []),\n                isEnabled);\n\n            return Task.FromResult(isEnabled);\n        }\n    }\n\n    public record GeographicSettings\n    {\n        public List<string>? AllowedCountries { get; init; }\n    }\n\n    // Configuration\n    {\n      \"FeatureManagement\": {\n        \"EuCompliantFeature\": {\n          \"EnabledFor\": [\n            {\n              \"Name\": \"Geographic\",\n              \"Parameters\": {\n                \"AllowedCountries\": [\"DE\", \"FR\", \"ES\", \"IT\", \"NL\", \"BE\"]\n              }\n            }\n          ]\n        }\n      }\n    }\n\n\n##  Safe Rollout Strategies\n\n###  Phased Rollout Approach\n\n**Progressive rollout stages:**\n\n  1. **Internal Testing** (0-5% of traffic) — Development team only\n  2. **Beta Users** (5-20%) — Early adopters and power users\n  3. **Early Majority** (20-50%) — Expanded user base\n  4. **General Availability** (50-100%) — All users\n\n\n\n\n    // Background service for gradual rollout automation\n    public class FeatureRolloutService(\n        IConfigurationRefresher configRefresher,\n        ILogger<FeatureRolloutService> logger) : BackgroundService\n    {\n        private readonly PeriodicTimer _timer = new(TimeSpan.FromSeconds(30));\n\n        protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n        {\n            logger.LogInformation(\"Feature rollout service started\");\n\n            while (!stoppingToken.IsCancellationRequested &&\n                   await _timer.WaitForNextTickAsync(stoppingToken))\n            {\n                try\n                {\n                    // Trigger configuration refresh\n                    await configRefresher.TryRefreshAsync(stoppingToken);\n                    logger.LogDebug(\"Feature flags refreshed successfully\");\n                }\n                catch (Exception ex)\n                {\n                    logger.LogError(ex, \"Error refreshing feature flags\");\n                }\n            }\n\n            logger.LogInformation(\"Feature rollout service stopped\");\n        }\n\n        public override void Dispose()\n        {\n            _timer.Dispose();\n            base.Dispose();\n        }\n    }\n\n    // Registration\n    builder.Services.AddSingleton(sp =>\n    {\n        var refreshers = sp.GetRequiredService<IEnumerable<IConfigurationRefresher>>();\n        return refreshers.First();\n    });\n\n    builder.Services.AddHostedService<FeatureRolloutService>();\n\n\n###  Circuit Breaker for New Features\n\nAutomatically disable new features if error rates exceed thresholds.\n\n\n\n    public class FeatureCircuitBreakerService(\n        IFeatureManager featureManager,\n        IDistributedCache cache,\n        ILogger<FeatureCircuitBreakerService> logger)\n    {\n        private const string ErrorCountPrefix = \"feature:errors:\";\n        private const int ErrorThreshold = 50;\n        private const int TimeWindowMinutes = 5;\n\n        public async Task<bool> IsFeatureHealthyAsync(\n            string featureName,\n            CancellationToken ct = default)\n        {\n            var isEnabled = await featureManager.IsEnabledAsync(featureName);\n\n            if (!isEnabled)\n            {\n                return false;\n            }\n\n            var errorCountKey = $\"{ErrorCountPrefix}{featureName}\";\n            var errorCountStr = await cache.GetStringAsync(errorCountKey, ct);\n\n            if (int.TryParse(errorCountStr, out var errorCount) && errorCount >= ErrorThreshold)\n            {\n                logger.LogWarning(\n                    \"Feature {FeatureName} circuit breaker OPEN - error count: {ErrorCount}\",\n                    featureName,\n                    errorCount);\n                return false;\n            }\n\n            return true;\n        }\n\n        public async Task RecordErrorAsync(\n            string featureName,\n            CancellationToken ct = default)\n        {\n            var errorCountKey = $\"{ErrorCountPrefix}{featureName}\";\n            var errorCountStr = await cache.GetStringAsync(errorCountKey, ct);\n\n            var errorCount = int.TryParse(errorCountStr, out var count) ? count + 1 : 1;\n\n            await cache.SetStringAsync(\n                errorCountKey,\n                errorCount.ToString(),\n                new DistributedCacheEntryOptions\n                {\n                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(TimeWindowMinutes)\n                },\n                ct);\n\n            logger.LogWarning(\n                \"Error recorded for feature {FeatureName}: {ErrorCount}/{Threshold}\",\n                featureName,\n                errorCount,\n                ErrorThreshold);\n        }\n\n        public async Task ResetCircuitAsync(\n            string featureName,\n            CancellationToken ct = default)\n        {\n            var errorCountKey = $\"{ErrorCountPrefix}{featureName}\";\n            await cache.RemoveAsync(errorCountKey, ct);\n\n            logger.LogInformation(\"Circuit breaker reset for feature {FeatureName}\", featureName);\n        }\n    }\n\n    // Usage in service\n    public class PaymentService(\n        FeatureCircuitBreakerService circuitBreaker,\n        ILogger<PaymentService> logger)\n    {\n        public async Task<PaymentResult> ProcessPaymentAsync(\n            PaymentRequest request,\n            CancellationToken ct = default)\n        {\n            const string featureName = \"NewPaymentGateway\";\n\n            var useNewGateway = await circuitBreaker.IsFeatureHealthyAsync(featureName, ct);\n\n            try\n            {\n                if (useNewGateway)\n                {\n                    logger.LogInformation(\"Using new payment gateway\");\n                    return await ProcessWithNewGatewayAsync(request, ct);\n                }\n\n                logger.LogInformation(\"Using legacy payment gateway\");\n                return await ProcessWithLegacyGatewayAsync(request, ct);\n            }\n            catch (Exception ex)\n            {\n                if (useNewGateway)\n                {\n                    await circuitBreaker.RecordErrorAsync(featureName, ct);\n                }\n\n                logger.LogError(ex, \"Payment processing failed\");\n                throw;\n            }\n        }\n\n        private async Task<PaymentResult> ProcessWithNewGatewayAsync(\n            PaymentRequest request,\n            CancellationToken ct)\n        {\n            await Task.Delay(10, ct);\n            return new PaymentResult { Success = true };\n        }\n\n        private async Task<PaymentResult> ProcessWithLegacyGatewayAsync(\n            PaymentRequest request,\n            CancellationToken ct)\n        {\n            await Task.Delay(10, ct);\n            return new PaymentResult { Success = true };\n        }\n    }\n\n\n##  Observability & Monitoring\n\n###  Telemetry for Feature Flags\n\n\n    // Decorator pattern for observable feature manager\n    public class ObservableFeatureManager(\n        IFeatureManager innerFeatureManager,\n        ILogger<ObservableFeatureManager> logger,\n        IMeterFactory meterFactory) : IFeatureManager\n    {\n        private readonly Meter _meter = meterFactory.Create(\"Company.FeatureManagement\");\n        private readonly Counter<long> _evaluationCounter = meterFactory\n            .Create(\"Company.FeatureManagement\")\n            .CreateCounter<long>(\"feature.evaluations\", \"evaluations\", \"Number of feature evaluations\");\n        private readonly Histogram<double> _evaluationDuration = meterFactory\n            .Create(\"Company.FeatureManagement\")\n            .CreateHistogram<double>(\"feature.evaluation.duration\", \"ms\", \"Feature evaluation duration\");\n\n        public async Task<bool> IsEnabledAsync(string feature)\n        {\n            var sw = Stopwatch.StartNew();\n\n            try\n            {\n                var isEnabled = await innerFeatureManager.IsEnabledAsync(feature);\n                sw.Stop();\n\n                // Record metrics\n                _evaluationCounter.Add(1,\n                    new KeyValuePair<string, object?>(\"feature\", feature),\n                    new KeyValuePair<string, object?>(\"enabled\", isEnabled));\n\n                _evaluationDuration.Record(sw.Elapsed.TotalMilliseconds,\n                    new KeyValuePair<string, object?>(\"feature\", feature));\n\n                logger.LogDebug(\n                    \"Feature {FeatureName} evaluated to {IsEnabled} in {ElapsedMs}ms\",\n                    feature,\n                    isEnabled,\n                    sw.ElapsedMilliseconds);\n\n                return isEnabled;\n            }\n            catch (Exception ex)\n            {\n                sw.Stop();\n\n                logger.LogError(ex,\n                    \"Error evaluating feature {FeatureName} after {ElapsedMs}ms\",\n                    feature,\n                    sw.ElapsedMilliseconds);\n\n                // Record error metric\n                _evaluationCounter.Add(1,\n                    new KeyValuePair<string, object?>(\"feature\", feature),\n                    new KeyValuePair<string, object?>(\"enabled\", false),\n                    new KeyValuePair<string, object?>(\"error\", true));\n\n                throw;\n            }\n        }\n\n        public async Task<bool> IsEnabledAsync<TContext>(string feature, TContext context)\n        {\n            return await innerFeatureManager.IsEnabledAsync(feature, context);\n        }\n\n        public IAsyncEnumerable<string> GetFeatureNamesAsync()\n        {\n            return innerFeatureManager.GetFeatureNamesAsync();\n        }\n    }\n\n    // Registration using Scrutor\n    builder.Services.AddFeatureManagement();\n    builder.Services.Decorate<IFeatureManager, ObservableFeatureManager>();\n\n\n###  Application Insights Integration\n\n\n    public class ApplicationInsightsFeatureManager(\n        IFeatureManager innerFeatureManager,\n        TelemetryClient telemetryClient,\n        IHttpContextAccessor httpContextAccessor) : IFeatureManager\n    {\n        public async Task<bool> IsEnabledAsync(string feature)\n        {\n            var sw = Stopwatch.StartNew();\n            var isEnabled = await innerFeatureManager.IsEnabledAsync(feature);\n            sw.Stop();\n\n            var properties = new Dictionary<string, string>\n            {\n                [\"FeatureName\"] = feature,\n                [\"IsEnabled\"] = isEnabled.ToString(),\n                [\"EvaluationTimeMs\"] = sw.ElapsedMilliseconds.ToString()\n            };\n\n            // Add user context if available\n            var httpContext = httpContextAccessor.HttpContext;\n            if (httpContext?.User?.Identity?.IsAuthenticated == true)\n            {\n                properties[\"UserId\"] = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value\n                                       ?? \"unknown\";\n                properties[\"UserEmail\"] = httpContext.User.FindFirst(ClaimTypes.Email)?.Value\n                                          ?? \"unknown\";\n            }\n\n            telemetryClient.TrackEvent(\"FeatureEvaluated\", properties);\n\n            return isEnabled;\n        }\n\n        public async Task<bool> IsEnabledAsync<TContext>(string feature, TContext context)\n        {\n            return await innerFeatureManager.IsEnabledAsync(feature, context);\n        }\n\n        public IAsyncEnumerable<string> GetFeatureNamesAsync()\n        {\n            return innerFeatureManager.GetFeatureNamesAsync();\n        }\n    }\n\n\n###  A/B Testing Framework\n\n\n    public class ABTestingService(\n        IFeatureManager featureManager,\n        TelemetryClient telemetryClient,\n        ILogger<ABTestingService> logger)\n    {\n        public async Task<T> RunExperimentAsync<T>(\n            string experimentName,\n            Func<Task<T>> controlGroup,\n            Func<Task<T>> treatmentGroup,\n            CancellationToken ct = default)\n        {\n            var featureName = $\"Experiment_{experimentName}\";\n            var isInTreatment = await featureManager.IsEnabledAsync(featureName);\n\n            var sw = Stopwatch.StartNew();\n            var variant = isInTreatment ? \"Treatment\" : \"Control\";\n\n            logger.LogInformation(\n                \"Running experiment {ExperimentName} with variant {Variant}\",\n                experimentName,\n                variant);\n\n            try\n            {\n                T result = isInTreatment\n                    ? await treatmentGroup()\n                    : await controlGroup();\n\n                sw.Stop();\n\n                TrackExperimentResult(\n                    experimentName,\n                    variant,\n                    sw.Elapsed,\n                    success: true);\n\n                return result;\n            }\n            catch (Exception ex)\n            {\n                sw.Stop();\n\n                TrackExperimentResult(\n                    experimentName,\n                    variant,\n                    sw.Elapsed,\n                    success: false,\n                    exception: ex);\n\n                logger.LogError(ex,\n                    \"Experiment {ExperimentName} failed for variant {Variant}\",\n                    experimentName,\n                    variant);\n\n                throw;\n            }\n        }\n\n        private void TrackExperimentResult(\n            string experimentName,\n            string variant,\n            TimeSpan duration,\n            bool success,\n            Exception? exception = null)\n        {\n            var properties = new Dictionary<string, string>\n            {\n                [\"Experiment\"] = experimentName,\n                [\"Variant\"] = variant,\n                [\"Success\"] = success.ToString(),\n                [\"DurationMs\"] = duration.TotalMilliseconds.ToString(\"F2\")\n            };\n\n            if (exception is not null)\n            {\n                properties[\"Error\"] = exception.Message;\n                properties[\"ExceptionType\"] = exception.GetType().Name;\n            }\n\n            var metrics = new Dictionary<string, double>\n            {\n                [\"Duration\"] = duration.TotalMilliseconds\n            };\n\n            telemetryClient.TrackEvent(\"ExperimentResult\", properties, metrics);\n        }\n    }\n\n    // Usage example\n    public class CheckoutService(\n        ABTestingService abTestingService,\n        ILegacyCheckoutService legacyCheckout,\n        INewCheckoutService newCheckout)\n    {\n        public async Task<CheckoutResult> ProcessCheckoutAsync(\n            Cart cart,\n            CancellationToken ct = default)\n        {\n            return await abTestingService.RunExperimentAsync(\n                \"CheckoutOptimization\",\n                controlGroup: async () => await legacyCheckout.ProcessAsync(cart, ct),\n                treatmentGroup: async () => await newCheckout.ProcessAsync(cart, ct),\n                ct);\n        }\n    }\n\n\n###  Multivariate Testing\n\n\n    public class MultivariateTestingService(\n        IFeatureManager featureManager,\n        TelemetryClient telemetryClient,\n        IHttpContextAccessor httpContextAccessor)\n    {\n        public async Task<T> RunMultivariateTestAsync<T>(\n            string testName,\n            Dictionary<string, Func<Task<T>>> variants,\n            CancellationToken ct = default)\n        {\n            var variant = await featureManager.GetVariantAsync(\n                testName,\n                httpContextAccessor.HttpContext,\n                ct);\n\n            var variantName = variant?.Name ?? \"Control\";\n\n            if (!variants.TryGetValue(variantName, out var variantFunc))\n            {\n                variantFunc = variants[\"Control\"];\n            }\n\n            var sw = Stopwatch.StartNew();\n\n            try\n            {\n                var result = await variantFunc();\n                sw.Stop();\n\n                telemetryClient.TrackEvent(\"MultivariateTestResult\",\n                    new Dictionary<string, string>\n                    {\n                        [\"TestName\"] = testName,\n                        [\"Variant\"] = variantName,\n                        [\"Success\"] = \"true\",\n                        [\"DurationMs\"] = sw.ElapsedMilliseconds.ToString()\n                    });\n\n                return result;\n            }\n            catch (Exception ex)\n            {\n                sw.Stop();\n\n                telemetryClient.TrackEvent(\"MultivariateTestResult\",\n                    new Dictionary<string, string>\n                    {\n                        [\"TestName\"] = testName,\n                        [\"Variant\"] = variantName,\n                        [\"Success\"] = \"false\",\n                        [\"Error\"] = ex.Message,\n                        [\"DurationMs\"] = sw.ElapsedMilliseconds.ToString()\n                    });\n\n                throw;\n            }\n        }\n    }\n\n    // Usage\n    var recommendations = await multivariateTestingService.RunMultivariateTestAsync(\n        \"ProductRecommendations\",\n        new Dictionary<string, Func<Task<List<Product>>>>\n        {\n            [\"Control\"] = async () => await GetBasicRecommendationsAsync(),\n            [\"Collaborative\"] = async () => await GetCollaborativeRecommendationsAsync(),\n            [\"ContentBased\"] = async () => await GetContentBasedRecommendationsAsync(),\n            [\"Hybrid\"] = async () => await GetHybridRecommendationsAsync()\n        },\n        ct);\n\n\n##  Monitoring Dashboards\n\n###  Azure Monitor KQL Queries\n\n\n    // Feature flag evaluation trends\n    customEvents\n    | where name == \"FeatureEvaluated\"\n    | extend FeatureName = tostring(customDimensions.FeatureName),\n             IsEnabled = tobool(customDimensions.IsEnabled)\n    | summarize EnabledCount = countif(IsEnabled),\n                DisabledCount = countif(not(IsEnabled)),\n                TotalEvaluations = count()\n                by FeatureName, bin(timestamp, 1h)\n    | project timestamp, FeatureName, EnabledCount, DisabledCount,\n              EnabledPercentage = 100.0 * EnabledCount / TotalEvaluations\n    | render timechart\n\n    // A/B test performance comparison\n    customEvents\n    | where name == \"ExperimentResult\"\n    | extend Experiment = tostring(customDimensions.Experiment),\n             Variant = tostring(customDimensions.Variant),\n             Success = tobool(customDimensions.Success),\n             Duration = todouble(customDimensions.DurationMs)\n    | summarize SuccessRate = 100.0 * countif(Success) / count(),\n                AvgDuration = avg(Duration),\n                P50Duration = percentile(Duration, 50),\n                P95Duration = percentile(Duration, 95),\n                P99Duration = percentile(Duration, 99),\n                Count = count()\n                by Experiment, Variant\n    | order by Experiment, Variant\n\n    // Feature flag error rate by feature\n    customEvents\n    | where name == \"FeatureEvaluated\"\n    | extend FeatureName = tostring(customDimensions.FeatureName),\n             IsEnabled = tobool(customDimensions.IsEnabled),\n             HasError = tobool(customDimensions.error)\n    | summarize ErrorCount = countif(HasError),\n                TotalCount = count(),\n                ErrorRate = 100.0 * countif(HasError) / count()\n                by FeatureName, bin(timestamp, 5m)\n    | where ErrorRate > 1.0\n    | render timechart\n\n    // User targeting distribution\n    customEvents\n    | where name == \"FeatureEvaluated\"\n    | extend FeatureName = tostring(customDimensions.FeatureName),\n             IsEnabled = tobool(customDimensions.IsEnabled),\n             UserId = tostring(customDimensions.UserId)\n    | where isnotempty(UserId)\n    | summarize UniqueUsers = dcount(UserId),\n                EnabledUsers = dcountif(UserId, IsEnabled),\n                DisabledUsers = dcountif(UserId, not(IsEnabled))\n                by FeatureName\n    | project FeatureName, UniqueUsers, EnabledUsers, DisabledUsers,\n              EnabledPercentage = 100.0 * EnabledUsers / UniqueUsers\n    | order by EnabledPercentage desc\n\n    // Experiment conversion funnel\n    customEvents\n    | where name == \"ExperimentResult\"\n    | extend Experiment = tostring(customDimensions.Experiment),\n             Variant = tostring(customDimensions.Variant),\n             Success = tobool(customDimensions.Success)\n    | summarize TotalAttempts = count(),\n                SuccessfulConversions = countif(Success),\n                ConversionRate = 100.0 * countif(Success) / count()\n                by Experiment, Variant, bin(timestamp, 1d)\n    | render columnchart\n\n\n###  Prometheus Metrics\n\n\n    // Export feature flag metrics for Prometheus\n    public class PrometheusFeatureMetrics(IMeterFactory meterFactory)\n    {\n        private readonly Meter _meter = meterFactory.Create(\"feature_management\");\n\n        private readonly Counter<long> _evaluations = meterFactory\n            .Create(\"feature_management\")\n            .CreateCounter<long>(\n                \"feature_flag_evaluations_total\",\n                \"evaluations\",\n                \"Total number of feature flag evaluations\");\n\n        private readonly Histogram<double> _evaluationDuration = meterFactory\n            .Create(\"feature_management\")\n            .CreateHistogram<double>(\n                \"feature_flag_evaluation_duration_seconds\",\n                \"s\",\n                \"Feature flag evaluation duration in seconds\");\n\n        private readonly Counter<long> _evaluationErrors = meterFactory\n            .Create(\"feature_management\")\n            .CreateCounter<long>(\n                \"feature_flag_evaluation_errors_total\",\n                \"errors\",\n                \"Total number of feature flag evaluation errors\");\n\n        public void RecordEvaluation(string featureName, bool isEnabled, double durationSeconds)\n        {\n            _evaluations.Add(1,\n                new KeyValuePair<string, object?>(\"feature\", featureName),\n                new KeyValuePair<string, object?>(\"enabled\", isEnabled));\n\n            _evaluationDuration.Record(durationSeconds,\n                new KeyValuePair<string, object?>(\"feature\", featureName));\n        }\n\n        public void RecordError(string featureName)\n        {\n            _evaluationErrors.Add(1,\n                new KeyValuePair<string, object?>(\"feature\", featureName));\n        }\n    }\n\n    // Configure OpenTelemetry with Prometheus exporter\n    builder.Services.AddOpenTelemetry()\n        .WithMetrics(metrics =>\n        {\n            metrics\n                .AddMeter(\"feature_management\")\n                .AddPrometheusExporter();\n        });\n\n    app.MapPrometheusScrapingEndpoint();\n\n\n##  Best Practices\n\n###  1. Feature Flag Lifecycle Management\n\n\n    // Document feature flag metadata\n    public record FeatureFlagMetadata\n    {\n        public required string Name { get; init; }\n        public required string Description { get; init; }\n        public required string Owner { get; init; }\n        public required DateOnly CreatedDate { get; init; }\n        public DateOnly? TargetRemovalDate { get; init; }\n        public required string JiraTicket { get; init; }\n        public List<string> Dependencies { get; init; } = [];\n    }\n\n    // Track feature flag age and cleanup\n    public class FeatureFlagAuditService(\n        IFeatureManager featureManager,\n        ILogger<FeatureFlagAuditService> logger)\n    {\n        private static readonly Dictionary<string, FeatureFlagMetadata> FlagMetadata = new()\n        {\n            [\"NewCheckoutFlow\"] = new()\n            {\n                Name = \"NewCheckoutFlow\",\n                Description = \"Enable new checkout experience\",\n                Owner = \"payments-team\",\n                CreatedDate = new DateOnly(2025, 1, 15),\n                TargetRemovalDate = new DateOnly(2025, 4, 15),\n                JiraTicket = \"PAY-1234\",\n                Dependencies = [\"NewPaymentGateway\"]\n            },\n            [\"AdvancedReporting\"] = new()\n            {\n                Name = \"AdvancedReporting\",\n                Description = \"Enable advanced analytics dashboard\",\n                Owner = \"analytics-team\",\n                CreatedDate = new DateOnly(2025, 2, 1),\n                TargetRemovalDate = new DateOnly(2025, 5, 1),\n                JiraTicket = \"ANAL-5678\",\n                Dependencies = []\n            }\n        };\n\n        public async Task<List<FeatureFlagMetadata>> GetStaleFlagsAsync(CancellationToken ct = default)\n        {\n            var today = DateOnly.FromDateTime(DateTime.UtcNow);\n            var staleFlags = new List<FeatureFlagMetadata>();\n\n            await foreach (var flagName in featureManager.GetFeatureNamesAsync().WithCancellation(ct))\n            {\n                if (FlagMetadata.TryGetValue(flagName, out var metadata))\n                {\n                    var age = today.DayNumber - metadata.CreatedDate.DayNumber;\n\n                    // Flag is stale if older than 90 days or past target removal date\n                    if (age > 90 ||\n                        (metadata.TargetRemovalDate.HasValue && today >= metadata.TargetRemovalDate.Value))\n                    {\n                        staleFlags.Add(metadata);\n\n                        logger.LogWarning(\n                            \"Feature flag {FlagName} is stale (age: {Age} days, target removal: {TargetDate})\",\n                            metadata.Name,\n                            age,\n                            metadata.TargetRemovalDate);\n                    }\n                }\n            }\n\n            return staleFlags;\n        }\n    }\n\n\n###  2. Testing Feature Flags\n\n\n    // Unit test with mocked feature manager\n    public class OrderServiceTests\n    {\n        [Fact]\n        public async Task CreateOrder_WithNewCheckoutFlow_UsesNewService()\n        {\n            // Arrange\n            var featureManagerMock = new Mock<IFeatureManager>();\n            featureManagerMock\n                .Setup(x => x.IsEnabledAsync(\"NewCheckoutFlow\"))\n                .ReturnsAsync(true);\n\n            var newCheckoutMock = new Mock<ICheckoutService>();\n            var legacyCheckoutMock = new Mock<ICheckoutService>();\n\n            var orderService = new OrderService(\n                featureManagerMock.Object,\n                newCheckoutMock.Object,\n                legacyCheckoutMock.Object);\n\n            var order = new Order { Id = Guid.NewGuid() };\n\n            // Act\n            await orderService.ProcessOrderAsync(order);\n\n            // Assert\n            newCheckoutMock.Verify(x => x.ProcessAsync(order, It.IsAny<CancellationToken>()), Times.Once);\n            legacyCheckoutMock.Verify(x => x.ProcessAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Never);\n        }\n\n        [Fact]\n        public async Task CreateOrder_WithLegacyCheckoutFlow_UsesLegacyService()\n        {\n            // Arrange\n            var featureManagerMock = new Mock<IFeatureManager>();\n            featureManagerMock\n                .Setup(x => x.IsEnabledAsync(\"NewCheckoutFlow\"))\n                .ReturnsAsync(false);\n\n            var newCheckoutMock = new Mock<ICheckoutService>();\n            var legacyCheckoutMock = new Mock<ICheckoutService>();\n\n            var orderService = new OrderService(\n                featureManagerMock.Object,\n                newCheckoutMock.Object,\n                legacyCheckoutMock.Object);\n\n            var order = new Order { Id = Guid.NewGuid() };\n\n            // Act\n            await orderService.ProcessOrderAsync(order);\n\n            // Assert\n            legacyCheckoutMock.Verify(x => x.ProcessAsync(order, It.IsAny<CancellationToken>()), Times.Once);\n            newCheckoutMock.Verify(x => x.ProcessAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Never);\n        }\n    }\n\n    // Integration test with feature flags\n    public class OrderApiIntegrationTests : IClassFixture<WebApplicationFactory<Program>>\n    {\n        private readonly WebApplicationFactory<Program> _factory;\n\n        public OrderApiIntegrationTests(WebApplicationFactory<Program> factory)\n        {\n            _factory = factory;\n        }\n\n        [Fact]\n        public async Task CreateOrder_WithNewCheckoutEnabled_ReturnsSuccess()\n        {\n            // Arrange\n            var client = _factory.WithWebHostBuilder(builder =>\n            {\n                builder.ConfigureAppConfiguration((context, config) =>\n                {\n                    config.AddInMemoryCollection(new Dictionary<string, string?>\n                    {\n                        [\"FeatureManagement:NewCheckoutFlow\"] = \"true\"\n                    });\n                });\n            }).CreateClient();\n\n            var request = new CreateOrderRequest\n            {\n                CustomerId = \"test-customer\",\n                Items = [new OrderItem { ProductId = \"product-1\", Quantity = 2 }]\n            };\n\n            // Act\n            var response = await client.PostAsJsonAsync(\"/api/orders\", request);\n\n            // Assert\n            response.EnsureSuccessStatusCode();\n            var order = await response.Content.ReadFromJsonAsync<Order>();\n            Assert.NotNull(order);\n        }\n    }\n\n\n###  3. Feature Flag Naming Conventions\n\n\n    // Consistent naming pattern\n    public static class FeatureFlags\n    {\n        // Format: <Team>_<Feature>_<Aspect>\n        public const string Payments_NewGateway_Enabled = \"Payments.NewGateway.Enabled\";\n        public const string Payments_NewGateway_RolloutPercentage = \"Payments.NewGateway.RolloutPercentage\";\n\n        public const string Analytics_AdvancedReporting_Enabled = \"Analytics.AdvancedReporting.Enabled\";\n        public const string Analytics_RealTimeMetrics_Enabled = \"Analytics.RealTimeMetrics.Enabled\";\n\n        public const string Ui_BetaDashboard_Enabled = \"UI.BetaDashboard.Enabled\";\n        public const string Ui_DarkMode_Enabled = \"UI.DarkMode.Enabled\";\n\n        // Experiments use \"Experiment_\" prefix\n        public const string Experiment_CheckoutOptimization = \"Experiment.CheckoutOptimization\";\n        public const string Experiment_PricingStrategy = \"Experiment.PricingStrategy\";\n    }\n\n    // Usage with strongly-typed access\n    public class OrderService(IFeatureManager featureManager)\n    {\n        public async Task ProcessOrderAsync(Order order, CancellationToken ct = default)\n        {\n            if (await featureManager.IsEnabledAsync(FeatureFlags.Payments_NewGateway_Enabled))\n            {\n                await ProcessWithNewGatewayAsync(order, ct);\n            }\n            else\n            {\n                await ProcessWithLegacyGatewayAsync(order, ct);\n            }\n        }\n\n        private async Task ProcessWithNewGatewayAsync(Order order, CancellationToken ct)\n        {\n            await Task.Delay(10, ct);\n        }\n\n        private async Task ProcessWithLegacyGatewayAsync(Order order, CancellationToken ct)\n        {\n            await Task.Delay(10, ct);\n        }\n    }\n\n\n###  4. Default-Off Strategy\n\n\n    // Always default to safe/stable behavior when feature flag evaluation fails\n    public class SafeFeatureManager(\n        IFeatureManager innerFeatureManager,\n        ILogger<SafeFeatureManager> logger) : IFeatureManager\n    {\n        public async Task<bool> IsEnabledAsync(string feature)\n        {\n            try\n            {\n                return await innerFeatureManager.IsEnabledAsync(feature);\n            }\n            catch (Exception ex)\n            {\n                logger.LogError(ex,\n                    \"Error evaluating feature {FeatureName}, defaulting to disabled\",\n                    feature);\n\n                // Fail safe: default to disabled\n                return false;\n            }\n        }\n\n        public async Task<bool> IsEnabledAsync<TContext>(string feature, TContext context)\n        {\n            try\n            {\n                return await innerFeatureManager.IsEnabledAsync(feature, context);\n            }\n            catch (Exception ex)\n            {\n                logger.LogError(ex,\n                    \"Error evaluating feature {FeatureName} with context, defaulting to disabled\",\n                    feature);\n\n                return false;\n            }\n        }\n\n        public IAsyncEnumerable<string> GetFeatureNamesAsync()\n        {\n            return innerFeatureManager.GetFeatureNamesAsync();\n        }\n    }\n\n    // Register safe wrapper\n    builder.Services.AddFeatureManagement();\n    builder.Services.Decorate<IFeatureManager, SafeFeatureManager>();\n\n\n##  Advanced Scenarios\n\n###  Feature Flags with Dependency Injection Scopes\n\n\n    // Scoped feature-dependent service registration\n    public static class FeatureDependentServiceExtensions\n    {\n        public static IServiceCollection AddFeatureDependentService<TService, TImplementation, TFallback>(\n            this IServiceCollection services,\n            string featureName)\n            where TService : class\n            where TImplementation : class, TService\n            where TFallback : class, TService\n        {\n            services.AddScoped<TImplementation>();\n            services.AddScoped<TFallback>();\n\n            services.AddScoped<TService>(sp =>\n            {\n                var featureManager = sp.GetRequiredService<IFeatureManager>();\n                var isEnabled = featureManager.IsEnabledAsync(featureName).GetAwaiter().GetResult();\n\n                return isEnabled\n                    ? sp.GetRequiredService<TImplementation>()\n                    : sp.GetRequiredService<TFallback>();\n            });\n\n            return services;\n        }\n    }\n\n    // Usage\n    builder.Services.AddFeatureDependentService<IPaymentService, NewPaymentService, LegacyPaymentService>(\n        FeatureFlags.Payments_NewGateway_Enabled);\n\n\n###  Feature Flags in Background Services\n\n\n    public class ScheduledReportService(\n        IFeatureManager featureManager,\n        IReportGenerator reportGenerator,\n        ILogger<ScheduledReportService> logger) : BackgroundService\n    {\n        private readonly PeriodicTimer _timer = new(TimeSpan.FromHours(1));\n\n        protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n        {\n            logger.LogInformation(\"Scheduled report service started\");\n\n            while (!stoppingToken.IsCancellationRequested &&\n                   await _timer.WaitForNextTickAsync(stoppingToken))\n            {\n                if (!await featureManager.IsEnabledAsync(FeatureFlags.Analytics_AdvancedReporting_Enabled))\n                {\n                    logger.LogDebug(\"Advanced reporting feature disabled, skipping\");\n                    continue;\n                }\n\n                try\n                {\n                    logger.LogInformation(\"Generating scheduled report\");\n                    await reportGenerator.GenerateAndSendReportAsync(stoppingToken);\n                }\n                catch (Exception ex)\n                {\n                    logger.LogError(ex, \"Error generating scheduled report\");\n                }\n            }\n\n            logger.LogInformation(\"Scheduled report service stopped\");\n        }\n\n        public override void Dispose()\n        {\n            _timer.Dispose();\n            base.Dispose();\n        }\n    }\n\n\n###  Feature Flags in gRPC Services\n\n\n    // gRPC interceptor for feature gates\n    public class FeatureGateInterceptor(\n        IFeatureManager featureManager,\n        ILogger<FeatureGateInterceptor> logger) : Interceptor\n    {\n        public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(\n            TRequest request,\n            ServerCallContext context,\n            UnaryServerMethod<TRequest, TResponse> continuation)\n        {\n            // Check for feature gate metadata\n            var featureGate = context.Method.Split('/').Last();\n            var featureName = $\"gRPC.{featureGate}\";\n\n            if (!await featureManager.IsEnabledAsync(featureName))\n            {\n                logger.LogWarning(\"gRPC method {Method} is disabled by feature flag\", context.Method);\n                throw new RpcException(new Status(StatusCode.Unavailable,\n                    \"This feature is currently unavailable\"));\n            }\n\n            return await continuation(request, context);\n        }\n    }\n\n    // Register interceptor\n    builder.Services.AddGrpc(options =>\n    {\n        options.Interceptors.Add<FeatureGateInterceptor>();\n    });\n\n\n##  Summary\n\nFeature toggles are essential for modern software delivery, enabling:\n\n  * **Progressive Rollouts** : Safely deploy features to subsets of users\n  * **A/B Testing** : Experiment with different implementations\n  * **Kill Switches** : Quickly disable problematic features\n  * **Operational Control** : Manage features without deployments\n  * **Testing in Production** : Validate features with real users\n\n\n\n**Key Takeaways:**\n\n  1. Use Azure App Configuration for centralized management\n  2. Implement comprehensive observability for feature flags\n  3. Follow consistent naming conventions\n  4. Default to safe behavior on failures\n  5. Track and remove stale feature flags\n  6. Test both enabled and disabled code paths\n  7. Use targeting filters for gradual rollouts\n  8. Monitor circuit breakers for automatic safety\n\n\n\n##  Resources\n\n###  Official Documentation\n\n  * Microsoft Feature Management\n  * Azure App Configuration\n  * Feature Flags Best Practices\n\n\n\n###  Libraries\n\n  * Microsoft.FeatureManagement.AspNetCore\n  * Microsoft.Azure.AppConfiguration.AspNetCore\n\n\n\n###  Tools\n\n  * Azure Portal - App Configuration\n  * Azure CLI - `az appconfig feature`\n  * Visual Studio Code - Azure App Configuration extension\n\n\n\n###  Books & Articles\n\n  * _Feature Toggles (aka Feature Flags)_ by Martin Fowler\n  * _Continuous Delivery_ by Jez Humble and David Farley\n  * LaunchDarkly Feature Flag Guide\n\n",
  "title": "Feature Toggles & Feature Management in .NET and Azure"
}