Skip to content

Feature Flags Reference - Audit Trail Platform (ATP)

Deploy fearlessly, toggle safely — ATP uses Microsoft.FeatureManagement + Azure App Configuration to enable runtime feature control, progressive rollouts, A/B testing, and instant kill switches without redeployment.


📋 Documentation Generation Plan

This document will be generated in 12 cycles. Current progress:

Cycle Topics Estimated Lines Status
Cycle 1 Feature Flag Philosophy & Architecture (1-2) ~3,000 ⏳ Not Started
Cycle 2 Microsoft.FeatureManagement Fundamentals (3-4) ~3,500 ⏳ Not Started
Cycle 3 Feature Flag Configuration (5-6) ~3,500 ⏳ Not Started
Cycle 4 Built-In Feature Filters (7-8) ~3,500 ⏳ Not Started
Cycle 5 Custom Feature Filters (9-10) ~3,000 ⏳ Not Started
Cycle 6 Azure App Configuration Integration (11-12) ~4,000 ⏳ Not Started
Cycle 7 Feature Flags in Code (13-14) ~3,500 ⏳ Not Started
Cycle 8 Progressive Rollout & A/B Testing (15-16) ~3,500 ⏳ Not Started
Cycle 9 Multi-Tenant & Edition-Based Flags (17-18) ~3,000 ⏳ Not Started
Cycle 10 Testing Feature Flags (19-20) ~2,500 ⏳ Not Started
Cycle 11 Operational Best Practices (21-22) ~3,000 ⏳ Not Started
Cycle 12 Troubleshooting & Monitoring (23-24) ~2,500 ⏳ Not Started

Total Estimated Lines: ~39,000


Purpose & Scope

This document provides the complete feature flag reference for the Audit Trail Platform (ATP), covering runtime feature toggling using Microsoft.FeatureManagement library, Azure App Configuration for centralized management, progressive rollout strategies, A/B testing patterns, multi-tenant targeting, and operational best practices.

Why Feature Flags for ATP? - Zero-Downtime Releases: Deploy code with features disabled, enable them when ready - Progressive Rollout: Gradually expose features to subsets of tenants/users - Kill Switches: Instantly disable problematic features without redeployment - A/B Testing: Test feature variants for performance and user acceptance - Compliance Testing: Enable audit-sensitive features only in validated environments - Multi-Tenant Control: Enable features per tenant, edition, or subscription level - Canary Deployments: Test new code paths with a small percentage of traffic

Feature Flag Use Cases in ATP - New Export Formats: Enable enhanced eDiscovery export formats for specific tenants - Advanced Search: Activate full-text search with PII masking for enterprise edition - Integrity Verification: Enable Merkle tree verification for high-assurance customers - Classification Models: Switch between PII detection algorithms (rule-based vs. ML) - Retention Policies: Activate tiered storage and archival for specific editions - Webhook Enhancements: Enable webhook batching, compression, or signature verification - Performance Optimizations: Test new caching strategies or query optimizations - Beta Features: Grant early access to select customers for feedback


Detailed Cycle Plan

CYCLE 1: Feature Flag Philosophy & Architecture (~3,000 lines)

Topic 1: ATP Feature Flag Philosophy

What will be covered: - Why Feature Flags? - Decouple deployment from release - Enable/disable features at runtime - No redeployment required to change behavior - Reduce risk of new features (instant rollback) - Enable progressive delivery and continuous deployment - Support A/B testing and experimentation

  • Feature Flag vs. Configuration
  • Feature Flags: Enable/disable entire code paths (boolean decisions)
  • Configuration: Tune behavior of existing features (values, settings)
  • Feature flags are temporary (removed after full rollout)
  • Configuration is permanent (operational tuning)

  • Feature Flag Lifecycle

    1. Development: Feature flag created, code paths added
    2. Testing: Feature tested in dev/test with flag ON
    3. Staging: Feature validated with flag ON
    4. Production (10%): Progressive rollout begins
    5. Production (50%): Expand rollout if stable
    6. Production (100%): Full rollout, feature default ON
    7. Cleanup: Remove feature flag, code path becomes default
    

  • ATP Feature Flag Principles

  • Short-Lived: Remove flags after full rollout (technical debt prevention)
  • Boolean-Only: Flags are on/off, not multi-valued (simplicity)
  • Default-Off: New features default to disabled (safe by default)
  • Centralized: All flags in Azure App Configuration (single source of truth)
  • Auditable: Flag changes tracked in App Configuration history
  • Testable: Flags easily overridden in tests
  • Monitored: Flag evaluation tracked in telemetry

Code Examples: - Feature flag lifecycle diagram - Feature flag vs. configuration comparison - ATP flag naming conventions

Diagrams: - Feature flag lifecycle - Deployment vs. release timeline - Flag decision tree

Deliverables: - Feature flag philosophy document - Lifecycle management guide - Naming conventions


Topic 2: Feature Flag Architecture

What will be covered: - ATP Feature Flag Stack

┌─────────────────────────────────────────┐
│  Application Code (IFeatureManager)     │
├─────────────────────────────────────────┤
│  Microsoft.FeatureManagement Library    │
├─────────────────────────────────────────┤
│  Feature Filters (Percentage, Time,     │
│  Targeting, Custom)                     │
├─────────────────────────────────────────┤
│  Configuration Providers                │
│  - appsettings.json (local dev)         │
│  - Azure App Configuration (prod)       │
├─────────────────────────────────────────┤
│  Azure App Configuration Service        │
│  (Centralized flag management + UI)     │
└─────────────────────────────────────────┘

  • Feature Flag Components
  • IFeatureManager: Evaluate if flag is enabled
  • IFeatureManagerSnapshot: Cached evaluation per request (consistency)
  • IFeatureFilter: Custom logic for conditional enablement
  • ITargetingContextAccessor: Provide user/tenant context for targeting
  • FeatureGate: Attribute to gate ASP.NET Core actions/controllers

  • Configuration Sources

  • Development: appsettings.Development.json (local testing)
  • Staging/Production: Azure App Configuration (centralized, dynamic)
  • Testing: In-memory configuration (test overrides)

  • Feature Flag Scope

  • Global: Applies to all requests/tenants
  • Per-Tenant: Enabled for specific tenants (targeting filter)
  • Per-Edition: Enabled based on subscription edition (Enterprise, Pro, Free)
  • Per-Environment: Different flag states per environment (dev/staging/prod)

Code Examples: - Feature flag architecture diagram - IFeatureManager registration - Configuration source precedence

Diagrams: - ATP feature flag architecture - Component interaction flow - Configuration source hierarchy

Deliverables: - Architecture overview - Component reference - Configuration strategy


CYCLE 2: Microsoft.FeatureManagement Fundamentals (~3,500 lines)

Topic 3: Microsoft.FeatureManagement Library

What will be covered: - Library Overview - Microsoft-maintained open-source library - NuGet packages: - Microsoft.FeatureManagement (core) - Microsoft.FeatureManagement.AspNetCore (MVC/API support) - GitHub: https://github.com/microsoft/FeatureManagement-Dotnet

  • Core Interfaces

    // Check if feature enabled
    public interface IFeatureManager
    {
        Task<bool> IsEnabledAsync(string feature);
        Task<bool> IsEnabledAsync<TContext>(string feature, TContext context);
        IAsyncEnumerable<string> GetFeatureNamesAsync();
    }
    
    // Cached per-request evaluation (consistency within request)
    public interface IFeatureManagerSnapshot : IFeatureManager
    {
        // Same interface, but evaluations cached per HTTP request
    }
    
    // Custom feature filter logic
    public interface IFeatureFilter
    {
        Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context);
    }
    
    // Provide user/tenant context for targeting
    public interface ITargetingContextAccessor
    {
        Task<TargetingContext> GetContextAsync();
    }
    

  • Registration

    services.AddFeatureManagement()
        .AddFeatureFilter<PercentageFilter>()
        .AddFeatureFilter<TimeWindowFilter>()
        .AddFeatureFilter<TargetingFilter>()
        .AddFeatureFilter<TenantFeatureFilter>();  // Custom ATP filter
    

  • ATP Extension Method

    // In FeatureFlagsExtensions.cs
    internal static IServiceCollection AddAuditFeatureManagement(
        this IServiceCollection services)
    {
        services.AddFeatureManagement()
            .AddFeatureFilter<PercentageFilter>()
            .AddFeatureFilter<TimeWindowFilter>()
            .AddFeatureFilter<TargetingFilter>()
            .AddFeatureFilter<TenantFeatureFilter>()
            .AddFeatureFilter<EditionFeatureFilter>();
    
        // Add ATP-specific targeting context
        services.AddScoped<ITargetingContextAccessor, AuditTargetingContextAccessor>();
    
        return services;
    }
    

Code Examples: - IFeatureManager interface usage - IFeatureManagerSnapshot for request consistency - Feature management registration - ATP-specific extensions

Diagrams: - Microsoft.FeatureManagement architecture - Interface hierarchy - Registration flow

Deliverables: - Library reference guide - Interface documentation - Registration patterns


Topic 4: Basic Feature Flag Usage

What will be covered: - Simple Boolean Flag

public class ExportService
{
    private readonly IFeatureManager _featureManager;

    public ExportService(IFeatureManager featureManager)
    {
        _featureManager = featureManager;
    }

    public async Task<ExportPackage> CreateExportAsync(ExportRequest request)
    {
        if (await _featureManager.IsEnabledAsync("EnableEnhancedExportFormat"))
        {
            return await CreateEnhancedExportAsync(request);
        }
        else
        {
            return await CreateLegacyExportAsync(request);
        }
    }
}

  • FeatureGate Attribute (ASP.NET Core)

    [ApiController]
    [Route("api/[controller]")]
    public class AdvancedSearchController : ControllerBase
    {
        [HttpGet]
        [FeatureGate("EnableAdvancedSearch")]
        public async Task<IActionResult> Search([FromQuery] SearchQuery query)
        {
            // Only accessible if EnableAdvancedSearch is enabled
            var results = await _searchService.AdvancedSearchAsync(query);
            return Ok(results);
        }
    }
    

  • Disabling Actions

    [DisabledFeatures("EnableBetaFeatures")]
    public IActionResult LegacyExport()
    {
        // Only accessible if EnableBetaFeatures is DISABLED
    }
    

  • Feature-Aware Views (Razor)

    <feature name="EnableAdvancedSearch">
        <a href="/search/advanced">Advanced Search</a>
    </feature>
    

Code Examples: - IFeatureManager usage patterns (all scenarios) - FeatureGate attribute usage - DisabledFeatures attribute - Razor feature tag helper

Diagrams: - Feature flag evaluation flow - FeatureGate middleware

Deliverables: - Usage patterns guide - FeatureGate reference - Razor integration


CYCLE 3: Feature Flag Configuration (~3,500 lines)

Topic 5: Feature Flag Configuration Structure

What will be covered: - appsettings.json Feature Flags Section

{
  "FeatureManagement": {
    // Simple boolean flags
    "EnableEnhancedExportFormat": true,
    "EnableAdvancedSearch": false,

    // Filter-based flags (conditional enablement)
    "EnableIntegrityVerification": {
      "EnabledFor": [
        {
          "Name": "Percentage",
          "Parameters": {
            "Value": 50
          }
        }
      ]
    },

    "EnableBetaFeatures": {
      "EnabledFor": [
        {
          "Name": "TimeWindow",
          "Parameters": {
            "Start": "2025-11-01T00:00:00Z",
            "End": "2025-12-31T23:59:59Z"
          }
        }
      ]
    },

    "EnableEnterpriseFeatures": {
      "EnabledFor": [
        {
          "Name": "Edition",
          "Parameters": {
            "AllowedEditions": ["Enterprise"]
          }
        }
      ]
    }
  }
}

  • Configuration Structure
  • Section Name: Always "FeatureManagement"
  • Boolean Flags: "FlagName": true/false
  • Filter-Based Flags: "FlagName": { "EnabledFor": [...] }
  • Multiple Filters: AND logic (all must pass)

  • Environment-Specific Overrides

    // appsettings.Development.json
    {
      "FeatureManagement": {
        "EnableBetaFeatures": true,  // Always enabled in dev
        "EnableEnterpriseFeatures": true  // Test enterprise features
      }
    }
    
    // appsettings.Production.json
    {
      "FeatureManagement": {
        "EnableBetaFeatures": false,  // Disabled in production
        "EnableEnterpriseFeatures": {
          "EnabledFor": [
            {
              "Name": "Edition",
              "Parameters": {
                "AllowedEditions": ["Enterprise"]
              }
            }
          ]
        }
      }
    }
    

Code Examples: - Complete appsettings.json FeatureManagement section - Environment-specific overrides - Filter configuration examples

Diagrams: - FeatureManagement section structure - Environment override hierarchy

Deliverables: - Configuration reference - ATP feature flags catalog - Environment override patterns


Topic 6: ATP Feature Flags Catalog

What will be covered: - ATP Feature Flags Registry

// ATP Feature Flag Names (constants)
public static class AuditFeatureFlags
{
    // Export Features
    public const string EnableEnhancedExportFormat = "EnableEnhancedExportFormat";
    public const string EnableExportCompression = "EnableExportCompression";
    public const string EnableExportEncryption = "EnableExportEncryption";

    // Search Features
    public const string EnableAdvancedSearch = "EnableAdvancedSearch";
    public const string EnableFullTextSearch = "EnableFullTextSearch";
    public const string EnableSemanticSearch = "EnableSemanticSearch";

    // Integrity Features
    public const string EnableIntegrityVerification = "EnableIntegrityVerification";
    public const string EnableMerkleTreeVerification = "EnableMerkleTreeVerification";
    public const string EnableTimestampAnchoring = "EnableTimestampAnchoring";

    // Classification Features
    public const string EnableMLClassification = "EnableMLClassification";
    public const string EnableAdvancedPIIDetection = "EnableAdvancedPIIDetection";

    // Storage Features
    public const string EnableTieredStorage = "EnableTieredStorage";
    public const string EnableArchivalToBlob = "EnableArchivalToBlob";

    // Webhook Features
    public const string EnableWebhookBatching = "EnableWebhookBatching";
    public const string EnableWebhookCompression = "EnableWebhookCompression";
    public const string EnableWebhookSignatureV2 = "EnableWebhookSignatureV2";

    // Performance Features
    public const string EnableRedisSecondLevelCache = "EnableRedisSecondLevelCache";
    public const string EnableQueryOptimization = "EnableQueryOptimization";
    public const string EnableBatchIngestion = "EnableBatchIngestion";

    // Beta Features
    public const string EnableBetaFeatures = "EnableBetaFeatures";
    public const string EnableExperimentalFeatures = "EnableExperimentalFeatures";

    // Edition Features
    public const string EnableEnterpriseFeatures = "EnableEnterpriseFeatures";
    public const string EnableProFeatures = "EnableProFeatures";
}

  • Feature Flag Metadata
    public class FeatureFlagMetadata
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public DateTime IntroducedDate { get; set; }
        public DateTime? RemovalTargetDate { get; set; }
        public string[] RequiredEditions { get; set; }
        public bool IsDeprecated { get; set; }
    }
    

Code Examples: - Complete ATP feature flags constants - Feature flag metadata class - Feature flag documentation generator

Diagrams: - ATP feature flags map (by domain)

Deliverables: - ATP feature flags catalog (complete) - Constants class - Metadata tracking


CYCLE 4: Built-In Feature Filters (~3,500 lines)

Topic 7: PercentageFilter & TimeWindowFilter

What will be covered: - PercentageFilter

{
  "FeatureManagement": {
    "EnableQueryOptimization": {
      "EnabledFor": [
        {
          "Name": "Percentage",
          "Parameters": {
            "Value": 25
          }
        }
      ]
    }
  }
}

  • Behavior: Enables feature for 25% of evaluations
  • Use Case: Gradual rollout, canary testing
  • Consistency: Non-deterministic unless context provided
  • Progressive Rollout: 10% → 25% → 50% → 100%

  • TimeWindowFilter

    {
      "FeatureManagement": {
        "EnableBlackFridayFeatures": {
          "EnabledFor": [
            {
              "Name": "TimeWindow",
              "Parameters": {
                "Start": "2025-11-24T00:00:00Z",
                "End": "2025-11-30T23:59:59Z"
              }
            }
          ]
        }
      }
    }
    

  • Behavior: Enables feature during UTC time window

  • Use Case: Scheduled releases, time-limited features, trials
  • Timezone: Always UTC (convert local times to UTC)

  • Combining Filters (AND logic)

    {
      "FeatureManagement": {
        "EnableNewFeature": {
          "EnabledFor": [
            {
              "Name": "TimeWindow",
              "Parameters": {
                "Start": "2025-11-01T00:00:00Z",
                "End": "2025-12-31T23:59:59Z"
              }
            },
            {
              "Name": "Percentage",
              "Parameters": {
                "Value": 50
              }
            }
          ]
        }
      }
    }
    

  • Behavior: Enabled only if time window is active AND 50% evaluation passes

  • Use Case: Time-limited gradual rollout

Code Examples: - PercentageFilter configuration and usage - TimeWindowFilter configuration - Combined filter examples - Progressive rollout timeline

Diagrams: - PercentageFilter evaluation flow - TimeWindowFilter timeline - Combined filter logic (AND)

Deliverables: - PercentageFilter guide - TimeWindowFilter guide - Progressive rollout strategy


Topic 8: TargetingFilter

What will be covered: - TargetingFilter Configuration

{
  "FeatureManagement": {
    "EnableBetaFeatures": {
      "EnabledFor": [
        {
          "Name": "Targeting",
          "Parameters": {
            "Audience": {
              "Users": [
                "tenant:acme-corp",
                "user:john@example.com"
              ],
              "Groups": [
                "beta-testers",
                "early-access"
              ],
              "DefaultRolloutPercentage": 0,
              "Exclusion": {
                "Users": [
                  "tenant:problematic-tenant"
                ]
              }
            }
          }
        }
      ]
    }
  }
}

  • Targeting Context

    public class AuditTargetingContextAccessor : ITargetingContextAccessor
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly ITenantResolver _tenantResolver;
    
        public async Task<TargetingContext> GetContextAsync()
        {
            var httpContext = _httpContextAccessor.HttpContext;
            var tenantId = await _tenantResolver.GetTenantIdAsync(httpContext);
            var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            var userEmail = httpContext.User.FindFirst(ClaimTypes.Email)?.Value;
    
            var groups = new List<string>();
    
            // Add tenant as group
            if (!string.IsNullOrEmpty(tenantId))
            {
                groups.Add($"tenant:{tenantId}");
            }
    
            // Add edition as group
            var edition = await _tenantResolver.GetEditionAsync(tenantId);
            if (!string.IsNullOrEmpty(edition))
            {
                groups.Add($"edition:{edition}");
            }
    
            return new TargetingContext
            {
                UserId = userEmail ?? userId,
                Groups = groups
            };
        }
    }
    

  • Targeting Strategies

  • User-Based: Enable for specific user emails
  • Tenant-Based: Enable for specific tenants
  • Edition-Based: Enable for specific editions (via groups)
  • Percentage Fallback: Enable for N% of non-targeted users

Code Examples: - TargetingFilter configuration (all patterns) - ITargetingContextAccessor implementation - Per-tenant targeting - Per-edition targeting

Diagrams: - TargetingFilter evaluation flow - Targeting context resolution

Deliverables: - TargetingFilter guide - Targeting context implementation - Targeting strategies


CYCLE 5: Custom Feature Filters (~3,000 lines)

Topic 9: Custom Feature Filter Implementation

What will be covered: - IFeatureFilter Interface

public interface IFeatureFilter
{
    Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context);
}

// Evaluation context
public class FeatureFilterEvaluationContext
{
    public string FeatureName { get; }
    public IConfiguration Parameters { get; }
}

  • EditionFeatureFilter (ATP Custom Filter)

    [FilterAlias("Edition")]
    public class EditionFeatureFilter : IFeatureFilter
    {
        private readonly ITenantResolver _tenantResolver;
    
        public EditionFeatureFilter(ITenantResolver tenantResolver)
        {
            _tenantResolver = tenantResolver;
        }
    
        public async Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
        {
            // Get allowed editions from configuration
            var allowedEditions = context.Parameters
                .GetSection("AllowedEditions")
                .Get<string[]>() ?? Array.Empty<string>();
    
            // Get current tenant's edition
            var currentEdition = await _tenantResolver.GetCurrentEditionAsync();
    
            // Check if current edition is allowed
            return allowedEditions.Contains(currentEdition, StringComparer.OrdinalIgnoreCase);
        }
    }
    
    // Usage in appsettings.json
    {
      "FeatureManagement": {
        "EnableEnterpriseFeatures": {
          "EnabledFor": [
            {
              "Name": "Edition",
              "Parameters": {
                "AllowedEditions": ["Enterprise"]
              }
            }
          ]
        }
      }
    }
    

  • TenantFeatureFilter (Per-Tenant Control)

    [FilterAlias("Tenant")]
    public class TenantFeatureFilter : IFeatureFilter
    {
        private readonly ITenantResolver _tenantResolver;
    
        public async Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
        {
            // Get allowed tenants from configuration
            var allowedTenants = context.Parameters
                .GetSection("AllowedTenants")
                .Get<string[]>() ?? Array.Empty<string>();
    
            // Get current tenant ID
            var currentTenantId = await _tenantResolver.GetCurrentTenantIdAsync();
    
            // Check if current tenant is allowed
            return allowedTenants.Contains(currentTenantId, StringComparer.OrdinalIgnoreCase);
        }
    }
    
    // Usage
    {
      "FeatureManagement": {
        "EnableBetaExportFormat": {
          "EnabledFor": [
            {
              "Name": "Tenant",
              "Parameters": {
                "AllowedTenants": ["acme-corp", "contoso", "fabrikam"]
              }
            }
          ]
        }
      }
    }
    

  • EnvironmentFeatureFilter

    [FilterAlias("Environment")]
    public class EnvironmentFeatureFilter : IFeatureFilter
    {
        private readonly IHostEnvironment _environment;
    
        public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
        {
            var allowedEnvironments = context.Parameters
                .GetSection("AllowedEnvironments")
                .Get<string[]>() ?? Array.Empty<string>();
    
            return Task.FromResult(
                allowedEnvironments.Contains(
                    _environment.EnvironmentName,
                    StringComparer.OrdinalIgnoreCase));
        }
    }
    

Code Examples: - Complete custom filter implementations (all ATP filters) - Filter registration and configuration - Custom filter testing

Diagrams: - Custom filter evaluation flow - Filter registration pipeline

Deliverables: - Custom filter library (all ATP filters) - Filter implementation guide - Registration patterns


Topic 10: Contextual Feature Filters

What will be covered: - IContextualFeatureFilter

public interface IContextualFeatureFilter<TContext>
{
    Task<bool> EvaluateAsync(
        FeatureFilterEvaluationContext featureContext,
        TContext appContext);
}

  • Example: RequestContextFeatureFilter

    [FilterAlias("RequestContext")]
    public class RequestContextFeatureFilter : IContextualFeatureFilter<HttpContext>
    {
        public Task<bool> EvaluateAsync(
            FeatureFilterEvaluationContext featureContext,
            HttpContext appContext)
        {
            // Enable feature only for specific request headers
            var requiredHeader = featureContext.Parameters["RequiredHeader"];
            var requiredValue = featureContext.Parameters["RequiredValue"];
    
            if (appContext.Request.Headers.TryGetValue(requiredHeader, out var headerValue))
            {
                return Task.FromResult(headerValue == requiredValue);
            }
    
            return Task.FromResult(false);
        }
    }
    

  • Providing Context

    if (await _featureManager.IsEnabledAsync("MyFeature", _httpContextAccessor.HttpContext))
    {
        // Feature enabled based on HTTP context
    }
    

Code Examples: - Contextual filter implementations - Context provision patterns - Request-specific feature flags

Diagrams: - Contextual filter flow - Context resolution

Deliverables: - Contextual filter guide - ATP contextual filters - Usage patterns


CYCLE 6: Azure App Configuration Integration (~4,000 lines)

Topic 11: Azure App Configuration for Feature Flags

What will be covered: - Why Azure App Configuration for Flags? - Centralized flag management (all services, all environments) - UI for non-technical users (PMs, QA, operations) - Dynamic updates without redeployment - Version history and rollback - Label-based environment separation - Import/export capabilities - Role-based access control (RBAC)

  • Integration Setup

    configurationBuilder.AddAzureAppConfiguration(options =>
    {
        options.Connect(new Uri("https://appconfig-atp-prod.azconfig.io"))
            .ConfigureKeyVault(kv => kv.SetCredential(new DefaultAzureCredential()));
    
        // Load feature flags
        options.UseFeatureFlags(featureFlags =>
        {
            featureFlags.Select("ConnectSoft.Audit:*", LabelFilter.Null);
            featureFlags.SetRefreshInterval(TimeSpan.FromMinutes(5));
            featureFlags.CacheExpirationInterval = TimeSpan.FromMinutes(5);
        });
    });
    
    // Add feature management services
    services.AddFeatureManagement()
        .AddFeatureFilter<PercentageFilter>()
        .AddFeatureFilter<TimeWindowFilter>()
        .AddFeatureFilter<TargetingFilter>()
        .AddFeatureFilter<EditionFeatureFilter>()
        .AddFeatureFilter<TenantFeatureFilter>();
    
    // Add middleware to refresh feature flags
    app.UseAzureAppConfiguration();
    

  • Feature Flag Naming in App Configuration

    Key Pattern: {AppName}:{FeatureFlagName}
    
    Examples:
    - ConnectSoft.Audit:EnableEnhancedExportFormat
    - ConnectSoft.Audit:EnableAdvancedSearch
    - ConnectSoft.Audit:EnableIntegrityVerification
    
    Note: ".appconfig.featureflag/" prefix added automatically by App Configuration
    

  • Label-Based Environments

  • Label = environment name (dev, test, staging, prod)
  • Same feature flag, different states per environment
  • Application selects label based on ASPNETCORE_ENVIRONMENT

Code Examples: - Complete Azure App Configuration setup - Feature flag loading configuration - Label-based environment separation - Dynamic refresh setup

Diagrams: - Azure App Configuration architecture for feature flags - Feature flag refresh flow - Label-based environment model

Deliverables: - App Configuration integration guide - Feature flag naming standards - Environment label strategy


Topic 12: Managing Flags in Azure Portal

What will be covered: - Azure Portal Feature Flags UI - Create/edit/delete feature flags - Toggle flags on/off instantly - Configure filters (percentage, time window, targeting) - View flag history - Rollback to previous version

  • Creating Feature Flag in Portal
  • Navigate to Azure App Configuration resource
  • Select "Feature manager" blade
  • Click "+ Create" to add new flag
  • Provide:

    • Key: ConnectSoft.Audit:EnableNewFeature
    • Label: prod (environment)
    • State: Enabled/Disabled
    • Description: What this flag controls
    • Filters: Add percentage, time window, or targeting
  • Targeting Configuration in Portal

  • Users: Add email addresses or IDs
  • Groups: Add group names (e.g., "beta-testers", "tenant:acme")
  • Default percentage: Fallback for non-targeted users
  • Exclusions: Explicitly exclude users/groups

  • Feature Flag Versioning

  • Every change creates a new version
  • View history: See who changed what and when
  • Rollback: Restore previous version instantly
  • Export: Download as JSON for backup/disaster recovery

Code Examples: - Azure CLI commands for flag management - ARM/Bicep templates for flag provisioning - REST API for programmatic flag updates

Diagrams: - Azure Portal feature flags UI (screenshots) - Feature flag change workflow - Versioning and rollback flow

Deliverables: - Portal management guide - CLI command reference - IaC templates for flags


CYCLE 7: Feature Flags in Code (~3,500 lines)

Topic 13: IFeatureManager Usage Patterns

What will be covered: - Basic Conditional Logic

public class ExportService
{
    private readonly IFeatureManager _featureManager;

    public async Task<ExportPackage> CreateExportAsync(ExportRequest request)
    {
        if (await _featureManager.IsEnabledAsync(AuditFeatureFlags.EnableEnhancedExportFormat))
        {
            return await CreateEnhancedExportAsync(request);
        }
        else
        {
            return await CreateLegacyExportAsync(request);
        }
    }
}

  • Early Return Pattern

    public async Task ProcessAsync()
    {
        if (!await _featureManager.IsEnabledAsync(AuditFeatureFlags.EnableBatchIngestion))
        {
            return;  // Feature disabled, skip processing
        }
    
        // Feature enabled, proceed with batch processing
        await ProcessBatchAsync();
    }
    

  • Fallback Pattern

    public async Task<SearchResults> SearchAsync(SearchQuery query)
    {
        if (await _featureManager.IsEnabledAsync(AuditFeatureFlags.EnableAdvancedSearch))
        {
            try
            {
                return await _advancedSearchService.SearchAsync(query);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Advanced search failed, falling back to basic search");
                return await _basicSearchService.SearchAsync(query);
            }
        }
        else
        {
            return await _basicSearchService.SearchAsync(query);
        }
    }
    

  • Strategy Pattern with Feature Flags

    public class ClassificationService
    {
        private readonly IFeatureManager _featureManager;
        private readonly IRuleBasedClassifier _ruleBasedClassifier;
        private readonly IMLClassifier _mlClassifier;
    
        public async Task<Classification> ClassifyAsync(AuditEvent auditEvent)
        {
            IClassifier classifier = await _featureManager.IsEnabledAsync(
                AuditFeatureFlags.EnableMLClassification)
                ? _mlClassifier
                : _ruleBasedClassifier;
    
            return await classifier.ClassifyAsync(auditEvent);
        }
    }
    

  • Feature Flag Caching (IFeatureManagerSnapshot)

    public class RequestProcessor
    {
        private readonly IFeatureManagerSnapshot _featureManager;
    
        public async Task ProcessAsync()
        {
            // Evaluate feature flag once at start of request
            var useNewLogic = await _featureManager.IsEnabledAsync("EnableNewLogic");
    
            // Use same result throughout request (consistent behavior)
            await Step1Async(useNewLogic);
            await Step2Async(useNewLogic);
            await Step3Async(useNewLogic);
        }
    }
    

Code Examples: - Complete IFeatureManager usage patterns (all scenarios) - Fallback and error handling - Strategy pattern with flags - IFeatureManagerSnapshot for consistency

Diagrams: - Feature flag decision trees - Fallback pattern flow - Strategy pattern with flags

Deliverables: - Usage patterns catalog - Code templates - Best practices guide


Topic 14: FeatureGate Attribute & Middleware

What will be covered: - FeatureGate Attribute

[ApiController]
[Route("api/[controller]")]
public class AdvancedSearchController : ControllerBase
{
    [HttpGet]
    [FeatureGate(AuditFeatureFlags.EnableAdvancedSearch)]
    public async Task<IActionResult> Search([FromQuery] SearchQuery query)
    {
        // Only accessible if EnableAdvancedSearch is enabled
        var results = await _searchService.SearchAsync(query);
        return Ok(results);
    }
}

  • FeatureGate Returns 404 (default)
  • If feature disabled, returns 404 Not Found
  • Hides feature from discovery

  • Custom Behavior

    services.AddFeatureManagement()
        .UseDisabledFeaturesHandler(new CustomDisabledFeatureHandler());
    
    public class CustomDisabledFeatureHandler : IDisabledFeaturesHandler
    {
        public Task HandleDisabledFeature(IEnumerable<string> features, ActionExecutingContext context)
        {
            // Return 403 Forbidden instead of 404
            context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
            return Task.CompletedTask;
        }
    }
    

  • DisabledFeatures Attribute

    [DisabledFeatures(AuditFeatureFlags.EnableBetaFeatures)]
    public IActionResult LegacyExport()
    {
        // Only accessible if EnableBetaFeatures is DISABLED
        return Ok();
    }
    

  • Multiple Feature Gates (AND logic)

    [FeatureGate(AuditFeatureFlags.EnableAdvancedSearch, AuditFeatureFlags.EnableEnterpriseFeatures)]
    public IActionResult EnterpriseAdvancedSearch()
    {
        // Only accessible if BOTH flags are enabled
    }
    

Code Examples: - FeatureGate attribute usage (all scenarios) - Custom disabled features handler - DisabledFeatures attribute - Multiple feature gate logic

Diagrams: - FeatureGate middleware flow - Feature gate decision tree

Deliverables: - FeatureGate reference - Custom handler guide - Middleware integration


CYCLE 8: Progressive Rollout & A/B Testing (~3,500 lines)

Topic 15: Progressive Rollout Strategy

What will be covered: - Progressive Rollout Phases

Phase 1: Development (100%)
  - Feature flag: ON in dev/test environments
  - Goal: Internal validation
  - Duration: 1-2 weeks

Phase 2: Canary (10%)
  - Feature flag: 10% in production (PercentageFilter)
  - Goal: Detect critical issues with small blast radius
  - Duration: 2-3 days
  - Metrics: Error rate, latency, resource usage

Phase 3: Expanded (50%)
  - Feature flag: 50% in production
  - Goal: Validate performance at scale
  - Duration: 1 week
  - Metrics: Same as canary + user feedback

Phase 4: Full Rollout (100%)
  - Feature flag: 100% in production
  - Goal: Complete migration to new feature
  - Duration: 1 week
  - Metrics: Confirm stability

Phase 5: Cleanup
  - Remove feature flag from code
  - Delete flag from App Configuration
  - New code path becomes default

  • Rollout Configuration

    // Week 1: Canary (10%)
    {
      "FeatureManagement": {
        "EnableQueryOptimization": {
          "EnabledFor": [
            {
              "Name": "Percentage",
              "Parameters": {
                "Value": 10
              }
            }
          ]
        }
      }
    }
    
    // Week 2: Expand (50%)
    {
      "FeatureManagement": {
        "EnableQueryOptimization": {
          "EnabledFor": [
            {
              "Name": "Percentage",
              "Parameters": {
                "Value": 50
              }
            }
          ]
        }
      }
    }
    
    // Week 3: Full rollout (100%)
    {
      "FeatureManagement": {
        "EnableQueryOptimization": true
      }
    }
    
    // Week 4: Cleanup (remove flag from code)
    

  • Rollout Monitoring

  • Error rate comparison (flag ON vs. OFF)
  • Latency comparison (P50, P95, P99)
  • Resource usage (CPU, memory, database queries)
  • User feedback (support tickets, NPS scores)
  • Rollback trigger: Error rate increase > 10%

Code Examples: - Progressive rollout timeline - Monitoring query examples (Application Insights) - Rollback procedure - Rollout checklist

Diagrams: - Progressive rollout timeline - Monitoring dashboard (feature flag impact) - Rollback decision tree

Deliverables: - Progressive rollout playbook - Monitoring guide - Rollback procedures


Topic 16: A/B Testing with Feature Flags

What will be covered: - A/B Testing Setup

public class ExportService
{
    private readonly IFeatureManager _featureManager;
    private readonly ITelemetryClient _telemetry;

    public async Task<ExportPackage> CreateExportAsync(ExportRequest request)
    {
        var useVariantB = await _featureManager.IsEnabledAsync("ExportFormatVariantB");

        // Track variant assignment
        _telemetry.TrackEvent("ExportFormatVariant", new Dictionary<string, string>
        {
            ["Variant"] = useVariantB ? "B" : "A",
            ["TenantId"] = request.TenantId,
            ["ExportType"] = request.ExportType
        });

        var result = useVariantB
            ? await CreateExportVariantBAsync(request)
            : await CreateExportVariantAAsync(request);

        // Track variant outcome
        _telemetry.TrackMetric("ExportDuration", result.DurationMs, new Dictionary<string, string>
        {
            ["Variant"] = useVariantB ? "B" : "A"
        });

        return result;
    }
}

  • 50/50 A/B Test Configuration

    {
      "FeatureManagement": {
        "ExportFormatVariantB": {
          "EnabledFor": [
            {
              "Name": "Percentage",
              "Parameters": {
                "Value": 50
              }
            }
          ]
        }
      }
    }
    

  • Analysis Queries (Application Insights)

    // Compare variants performance
    customEvents
    | where name == "ExportFormatVariant"
    | extend Variant = tostring(customDimensions.Variant)
    | join kind=inner (
        customMetrics
        | where name == "ExportDuration"
        | extend Variant = tostring(customDimensions.Variant)
    ) on Variant
    | summarize 
        Count = count(),
        AvgDuration = avg(value),
        P50Duration = percentile(value, 50),
        P95Duration = percentile(value, 95)
        by Variant
    | order by Variant
    

Code Examples: - A/B test implementation patterns - Telemetry tracking - Analysis queries (Application Insights, Kusto) - Statistical significance testing

Diagrams: - A/B test architecture - Variant assignment flow - Analysis dashboard

Deliverables: - A/B testing guide - Telemetry patterns - Analysis query library


CYCLE 9: Multi-Tenant & Edition-Based Flags (~3,000 lines)

Topic 17: Per-Tenant Feature Flags

What will be covered: - Tenant Targeting Filter

{
  "FeatureManagement": {
    "EnableBetaExportFormat": {
      "EnabledFor": [
        {
          "Name": "Tenant",
          "Parameters": {
            "AllowedTenants": ["acme-corp", "contoso", "fabrikam"]
          }
        }
      ]
    }
  }
}

  • Per-Tenant Overrides (Database or App Configuration)

    // Store tenant-specific overrides in database
    public class TenantFeatureOverride
    {
        public string TenantId { get; set; }
        public string FeatureName { get; set; }
        public bool Enabled { get; set; }
        public DateTime? ExpiresAt { get; set; }
    }
    
    // Custom feature filter with database lookups
    public class TenantFeatureOverrideFilter : IFeatureFilter
    {
        private readonly ITenantFeatureOverrideRepository _repository;
        private readonly ITenantResolver _tenantResolver;
    
        public async Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
        {
            var tenantId = await _tenantResolver.GetCurrentTenantIdAsync();
            var featureName = context.FeatureName;
    
            var override = await _repository.GetOverrideAsync(tenantId, featureName);
    
            if (override != null)
            {
                // Check if override expired
                if (override.ExpiresAt.HasValue && override.ExpiresAt.Value < DateTime.UtcNow)
                {
                    return false;  // Expired, fall back to default
                }
    
                return override.Enabled;
            }
    
            return false;  // No override, fall back to default
        }
    }
    

  • Tenant Feature Management API

    [ApiController]
    [Route("api/admin/tenant-features")]
    [Authorize(Roles = "Administrator")]
    public class TenantFeatureManagementController : ControllerBase
    {
        [HttpPost("{tenantId}/features/{featureName}/enable")]
        public async Task<IActionResult> EnableFeature(string tenantId, string featureName)
        {
            await _tenantFeatureService.EnableFeatureAsync(tenantId, featureName);
            return Ok();
        }
    
        [HttpPost("{tenantId}/features/{featureName}/disable")]
        public async Task<IActionResult> DisableFeature(string tenantId, string featureName)
        {
            await _tenantFeatureService.DisableFeatureAsync(tenantId, featureName);
            return Ok();
        }
    }
    

Code Examples: - Per-tenant targeting configuration - Tenant override database model - Tenant feature management API - Feature flag resolution with overrides

Diagrams: - Tenant feature flag resolution flow - Override precedence - Admin management UI

Deliverables: - Per-tenant feature flag guide - Override database schema - Management API


Topic 18: Edition-Based Feature Gating

What will be covered: - Edition Feature Matrix

Feature                           | Free | Pro | Enterprise
----------------------------------|------|-----|------------
BasicAuditIngestion               | ✅   | ✅  | ✅
AdvancedSearch                    | ❌   | ✅  | ✅
IntegrityVerification             | ❌   | ❌  | ✅
CustomRetentionPolicies           | ❌   | ✅  | ✅
TieredStorage                     | ❌   | ❌  | ✅
UnlimitedExport                   | ❌   | ❌  | ✅
PremiumSupport                    | ❌   | ❌  | ✅

  • Edition Feature Filter

    [FilterAlias("Edition")]
    public class EditionFeatureFilter : IFeatureFilter
    {
        private readonly ITenantResolver _tenantResolver;
    
        public async Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
        {
            var allowedEditions = context.Parameters
                .GetSection("AllowedEditions")
                .Get<string[]>() ?? Array.Empty<string>();
    
            var currentEdition = await _tenantResolver.GetCurrentEditionAsync();
    
            return allowedEditions.Contains(currentEdition, StringComparer.OrdinalIgnoreCase);
        }
    }
    

  • Configuration

    {
      "FeatureManagement": {
        "EnableAdvancedSearch": {
          "EnabledFor": [
            {
              "Name": "Edition",
              "Parameters": {
                "AllowedEditions": ["Pro", "Enterprise"]
              }
            }
          ]
        },
    
        "EnableIntegrityVerification": {
          "EnabledFor": [
            {
              "Name": "Edition",
              "Parameters": {
                "AllowedEditions": ["Enterprise"]
              }
            }
          ]
        }
      }
    }
    

  • Feature Enforcement

    [FeatureGate(AuditFeatureFlags.EnableAdvancedSearch)]
    public async Task<IActionResult> AdvancedSearch()
    {
        // Returns 404 if tenant is on Free edition
    }
    
    // Or with custom response
    public async Task<IActionResult> AdvancedSearch()
    {
        if (!await _featureManager.IsEnabledAsync(AuditFeatureFlags.EnableAdvancedSearch))
        {
            return StatusCode(402, new
            {
                Error = "FeatureNotAvailable",
                Message = "Advanced Search is available on Pro and Enterprise plans.",
                UpgradeUrl = "https://audit.connectsoft.io/pricing"
            });
        }
    
        // Feature enabled, proceed
    }
    

Code Examples: - Edition feature matrix (complete) - Edition filter implementation - Edition-based configuration - Feature enforcement patterns

Diagrams: - Edition feature matrix - Edition filter evaluation - Upgrade flow

Deliverables: - Edition feature guide - Edition filter implementation - Enforcement patterns


CYCLE 10: Testing Feature Flags (~2,500 lines)

Topic 19: Unit Testing with Feature Flags

What will be covered: - Mocking IFeatureManager

[TestClass]
public class ExportServiceTests
{
    [TestMethod]
    public async Task Should_UseEnhancedFormat_When_FlagEnabled()
    {
        // Arrange
        var featureManagerMock = new Mock<IFeatureManager>();
        featureManagerMock
            .Setup(fm => fm.IsEnabledAsync(AuditFeatureFlags.EnableEnhancedExportFormat))
            .ReturnsAsync(true);

        var service = new ExportService(featureManagerMock.Object, ...);

        // Act
        var result = await service.CreateExportAsync(new ExportRequest());

        // Assert
        Assert.IsInstanceOfType(result, typeof(EnhancedExportPackage));
    }

    [TestMethod]
    public async Task Should_UseLegacyFormat_When_FlagDisabled()
    {
        // Arrange
        var featureManagerMock = new Mock<IFeatureManager>();
        featureManagerMock
            .Setup(fm => fm.IsEnabledAsync(AuditFeatureFlags.EnableEnhancedExportFormat))
            .ReturnsAsync(false);

        var service = new ExportService(featureManagerMock.Object, ...);

        // Act
        var result = await service.CreateExportAsync(new ExportRequest());

        // Assert
        Assert.IsInstanceOfType(result, typeof(LegacyExportPackage));
    }
}

  • In-Memory Configuration for Tests
    [TestClass]
    public class FeatureFlagIntegrationTests
    {
        private IConfiguration CreateTestConfiguration(bool enableFeature)
        {
            var configData = new Dictionary<string, string>
            {
                ["FeatureManagement:EnableEnhancedExportFormat"] = enableFeature.ToString()
            };
    
            return new ConfigurationBuilder()
                .AddInMemoryCollection(configData)
                .Build();
        }
    
        [TestMethod]
        public async Task Should_LoadFeaturesFromConfiguration()
        {
            // Arrange
            var config = CreateTestConfiguration(enableFeature: true);
            var services = new ServiceCollection();
            services.AddSingleton<IConfiguration>(config);
            services.AddFeatureManagement();
    
            var provider = services.BuildServiceProvider();
            var featureManager = provider.GetRequiredService<IFeatureManager>();
    
            // Act
            var isEnabled = await featureManager.IsEnabledAsync("EnableEnhancedExportFormat");
    
            // Assert
            Assert.IsTrue(isEnabled);
        }
    }
    

Code Examples: - IFeatureManager mocking (all scenarios) - In-memory configuration setup - Test helper classes - Integration test patterns

Diagrams: - Test configuration hierarchy - Mock setup patterns

Deliverables: - Testing guide - Test helper library - Integration test patterns


Topic 20: Feature Flag Testing Strategies

What will be covered: - Test Matrix Strategy

Test Scenario              | Flag State | Expected Behavior
---------------------------|------------|-------------------
Enhanced Export (ON)       | ON         | Uses enhanced format
Enhanced Export (OFF)      | OFF        | Uses legacy format
Advanced Search (ON)       | ON         | Returns advanced results
Advanced Search (OFF)      | OFF        | Returns basic results
Combined (Both ON)         | ON + ON    | Uses both features
Combined (Mixed)           | ON + OFF   | Uses only enabled feature
Combined (Both OFF)        | OFF + OFF  | Uses legacy flow

  • Parameterized Tests

    [DataTestMethod]
    [DataRow(true, typeof(EnhancedExportPackage))]
    [DataRow(false, typeof(LegacyExportPackage))]
    public async Task Should_UseCorrectFormat_BasedOnFlag(
        bool flagEnabled,
        Type expectedType)
    {
        // Arrange
        var featureManagerMock = new Mock<IFeatureManager>();
        featureManagerMock
            .Setup(fm => fm.IsEnabledAsync(AuditFeatureFlags.EnableEnhancedExportFormat))
            .ReturnsAsync(flagEnabled);
    
        var service = new ExportService(featureManagerMock.Object, ...);
    
        // Act
        var result = await service.CreateExportAsync(new ExportRequest());
    
        // Assert
        Assert.IsInstanceOfType(result, expectedType);
    }
    

  • Feature Flag Test Helpers

    public static class FeatureFlagTestHelpers
    {
        public static IFeatureManager CreateFeatureManager(params (string name, bool enabled)[] flags)
        {
            var mock = new Mock<IFeatureManager>();
    
            foreach (var (name, enabled) in flags)
            {
                mock.Setup(fm => fm.IsEnabledAsync(name)).ReturnsAsync(enabled);
            }
    
            return mock.Object;
        }
    }
    
    // Usage
    var featureManager = FeatureFlagTestHelpers.CreateFeatureManager(
        (AuditFeatureFlags.EnableAdvancedSearch, true),
        (AuditFeatureFlags.EnableIntegrityVerification, false));
    

Code Examples: - Test matrix implementation - Parameterized tests - Test helper utilities - Acceptance test patterns

Diagrams: - Test matrix coverage - Test automation flow

Deliverables: - Testing strategy document - Test helper library - Acceptance test templates


CYCLE 11: Operational Best Practices (~3,000 lines)

Topic 21: Feature Flag Best Practices

What will be covered: - Feature Flag Best Practices - ✅ Short-Lived: Remove flags after full rollout (technical debt prevention) - ✅ Boolean-Only: Flags are on/off, not multi-valued (simplicity) - ✅ Default-Off: New features default to disabled (safe by default) - ✅ Constants for Names: Use constants, not magic strings (refactoring-safe) - ✅ Consistent Naming: Prefix with "Enable" (e.g., "EnableAdvancedSearch") - ✅ Document Ownership: Each flag has owner and removal target date - ✅ Monitor Usage: Track flag evaluations in telemetry - ✅ Test Both Paths: Test feature ON and OFF - ✅ Gradual Rollout: Progressive rollout (10% → 50% → 100%) - ✅ Cleanup Regularly: Schedule flag cleanup sprints (quarterly)

  • Anti-Patterns to Avoid
  • Long-Lived Flags: Flags that live forever (technical debt)
  • Magic Strings: "EnableFeature" instead of constants
  • Multi-Valued Flags: Flags with multiple values (use configuration)
  • No Monitoring: Not tracking flag evaluations
  • No Testing: Only testing feature ON path
  • No Documentation: No owner or removal plan
  • Nested Flags: Flags depending on other flags (complexity)
  • Default-On: New features default to enabled (risky)

  • Flag Lifecycle Management

    public class FeatureFlagMetadata
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public string Owner { get; set; }  // Team or person
        public DateTime IntroducedDate { get; set; }
        public DateTime? RemovalTargetDate { get; set; }
        public FeatureFlagStatus Status { get; set; }  // Active, Rollout, Cleanup
        public string[] RequiredEditions { get; set; }
    }
    
    public enum FeatureFlagStatus
    {
        Active,         // In use, not yet fully rolled out
        FullyRolledOut, // 100% enabled, candidate for cleanup
        Deprecated,     // Marked for removal
        Removed         // Removed from code
    }
    

Code Examples: - Best practices checklist - Anti-pattern examples - Flag metadata tracking - Cleanup automation scripts

Diagrams: - Feature flag lifecycle (with cleanup) - Best practices vs. anti-patterns

Deliverables: - Best practices handbook - Anti-pattern catalog - Lifecycle tracking system


Topic 22: Feature Flag Monitoring & Observability

What will be covered: - Telemetry for Feature Flags

public class FeatureFlagTelemetryFilter : IFeatureFilter
{
    private readonly IFeatureFilter _innerFilter;
    private readonly ITelemetryClient _telemetry;

    public async Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        var result = await _innerFilter.EvaluateAsync(context);
        stopwatch.Stop();

        _telemetry.TrackEvent("FeatureFlagEvaluated", new Dictionary<string, string>
        {
            ["FeatureName"] = context.FeatureName,
            ["FilterName"] = context.Parameters["Name"],
            ["Result"] = result.ToString(),
            ["DurationMs"] = stopwatch.ElapsedMilliseconds.ToString()
        });

        return result;
    }
}

  • Application Insights Queries

    // Feature flag evaluations by flag
    customEvents
    | where name == "FeatureFlagEvaluated"
    | extend FeatureName = tostring(customDimensions.FeatureName)
    | extend Result = tostring(customDimensions.Result)
    | summarize 
        Count = count(),
        EnabledCount = countif(Result == "True"),
        DisabledCount = countif(Result == "False")
        by FeatureName
    | extend EnabledPercentage = (EnabledCount * 100.0) / Count
    | order by Count desc
    
    // Feature flag evaluation latency
    customEvents
    | where name == "FeatureFlagEvaluated"
    | extend FeatureName = tostring(customDimensions.FeatureName)
    | extend DurationMs = todouble(customDimensions.DurationMs)
    | summarize 
        P50 = percentile(DurationMs, 50),
        P95 = percentile(DurationMs, 95),
        P99 = percentile(DurationMs, 99)
        by FeatureName
    

  • Monitoring Dashboards

  • Feature flag evaluation rate
  • Feature flag enabled/disabled ratio
  • Feature flag evaluation latency
  • Feature flag errors
  • Rollout progress (% enabled over time)

Code Examples: - Telemetry integration (complete) - Application Insights queries - Dashboard creation (ARM templates) - Alerting rules

Diagrams: - Feature flag telemetry flow - Monitoring dashboard (example)

Deliverables: - Monitoring guide - Query library - Dashboard templates


CYCLE 12: Troubleshooting & Monitoring (~2,500 lines)

Topic 23: Troubleshooting Feature Flag Issues

What will be covered: - Common Problems - Problem: Feature flag not updating - Cause: Azure App Configuration refresh interval not expired - Solution: Wait for refresh interval (5 minutes) or restart application - Debug: Check UseAzureAppConfiguration() middleware registered

  • Problem: Feature flag returns wrong value

    • Cause: Stale cached value (IOptions instead of IOptionsSnapshot)
    • Solution: Use IFeatureManagerSnapshot for per-request consistency
    • Debug: Log flag evaluation result and timestamp
  • Problem: Targeting filter not working

    • Cause: ITargetingContextAccessor not registered or returns null
    • Solution: Register ITargetingContextAccessor in DI
    • Debug: Log targeting context values
  • Problem: Custom filter not evaluating

    • Cause: Filter not registered with AddFeatureFilter<>()
    • Solution: Add filter to feature management registration
    • Debug: Check DI container registration
  • Debugging Feature Flags

    public class FeatureFlagDebugService
    {
        private readonly IFeatureManager _featureManager;
    
        public async Task LogAllFeatureFlags()
        {
            await foreach (var featureName in _featureManager.GetFeatureNamesAsync())
            {
                var isEnabled = await _featureManager.IsEnabledAsync(featureName);
                _logger.LogInformation("Feature {FeatureName} = {IsEnabled}", featureName, isEnabled);
            }
        }
    }
    

  • Enable Verbose Logging

    {
      "Logging": {
        "LogLevel": {
          "Microsoft.FeatureManagement": "Debug"
        }
      }
    }
    

Code Examples: - Debugging utilities (complete) - Verbose logging configuration - Troubleshooting scripts - Health check integration

Diagrams: - Troubleshooting decision tree - Debugging flow

Deliverables: - Troubleshooting guide - Common problems catalog - Debugging utilities


Topic 24: Feature Flag Cleanup

What will be covered: - When to Remove Feature Flags - Flag is enabled for 100% of users for 2+ weeks - No rollback concerns - Feature is stable and working as expected - Legacy code path no longer needed

  • Cleanup Process
  • Verify Full Rollout: Confirm flag is 100% enabled in production
  • Remove Conditional Logic: Replace if (feature enabled) with direct call
  • Delete Legacy Code Path: Remove else branch and old implementation
  • Remove Flag Configuration: Delete from appsettings.json and App Configuration
  • Remove Flag Constant: Delete from AuditFeatureFlags class
  • Update Tests: Remove feature flag mocking, test only new path
  • Update Documentation: Remove flag from documentation

  • Cleanup Example

    // BEFORE (with feature flag)
    public async Task<ExportPackage> CreateExportAsync(ExportRequest request)
    {
        if (await _featureManager.IsEnabledAsync(AuditFeatureFlags.EnableEnhancedExportFormat))
        {
            return await CreateEnhancedExportAsync(request);
        }
        else
        {
            return await CreateLegacyExportAsync(request);
        }
    }
    
    // AFTER (cleanup)
    public async Task<ExportPackage> CreateExportAsync(ExportRequest request)
    {
        return await CreateEnhancedExportAsync(request);
    }
    

  • Automated Cleanup Detection

    // Script to find flags enabled 100% for > 2 weeks
    var flagsForCleanup = await _flagMetadataRepo
        .Where(f => f.Status == FeatureFlagStatus.FullyRolledOut)
        .Where(f => f.RolloutCompletedDate < DateTime.UtcNow.AddDays(-14))
        .ToListAsync();
    
    foreach (var flag in flagsForCleanup)
    {
        _logger.LogInformation("Flag {FlagName} ready for cleanup", flag.Name);
    }
    

Code Examples: - Cleanup process (step-by-step) - Before/after code examples - Automated cleanup detection - Cleanup checklist

Diagrams: - Cleanup workflow - Cleanup timeline

Deliverables: - Cleanup playbook - Automation scripts - Cleanup checklist


Summary of Deliverables

Across all 12 cycles, this documentation will provide:

  1. Philosophy & Architecture
  2. Feature flag philosophy and principles
  3. ATP feature flag architecture
  4. Microsoft.FeatureManagement fundamentals

  5. Configuration

  6. appsettings.json structure
  7. ATP feature flags catalog (complete)
  8. Environment-specific overrides

  9. Filters

  10. Built-in filters (Percentage, TimeWindow, Targeting)
  11. Custom ATP filters (Edition, Tenant, Environment)
  12. Contextual filters

  13. Azure App Configuration

  14. Integration setup and configuration
  15. Portal management guide
  16. Dynamic refresh and versioning

  17. Code Usage

  18. IFeatureManager patterns (all scenarios)
  19. FeatureGate attribute and middleware
  20. Fallback and strategy patterns

  21. Rollout Strategies

  22. Progressive rollout playbook
  23. A/B testing patterns
  24. Monitoring and rollback procedures

  25. Multi-Tenant Control

  26. Per-tenant feature flags
  27. Edition-based feature gating
  28. Feature enforcement patterns

  29. Testing

  30. Unit testing with mocks
  31. Integration testing with in-memory configuration
  32. Test matrix and parameterized tests

  33. Operations

  34. Best practices and anti-patterns
  35. Monitoring and observability
  36. Telemetry integration

  37. Troubleshooting & Cleanup

    • Common problems and solutions
    • Debugging utilities
    • Cleanup playbook and automation


This documentation plan covers complete feature flag management for ATP, from philosophy and implementation with Microsoft.FeatureManagement to Azure App Configuration integration, progressive rollouts, A/B testing, multi-tenant control, testing strategies, and operational best practices for safe, controlled feature deployment.