Skip to content

Real World Scenarios

This guide provides complete, production-ready examples for common real-world use cases.

E-Commerce Checkout Flow

Gradually roll out a new checkout experience.

public class CheckoutService
{
    private readonly IFeatureFlagService _flagService;
    private readonly ILogger<CheckoutService> _logger;

    public CheckoutService(IFeatureFlagService flagService, ILogger<CheckoutService> logger)
    {
        _flagService = flagService;
        _logger = logger;
    }

    public async Task<CheckoutResponse> ProcessCheckout(CheckoutRequest request)
    {
        // Determine which checkout flow to use
        var useNewCheckout = await ShouldUseNewCheckout(request.UserId);

        _logger.LogInformation(
            "Processing checkout for user {UserId} using {CheckoutVersion}", 
            request.UserId, 
            useNewCheckout ? "new" : "legacy"
        );

        try
        {
            if (useNewCheckout)
            {
                return await ProcessNewCheckout(request);
            }
            else
            {
                return await ProcessLegacyCheckout(request);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Checkout failed for user {UserId}", request.UserId);

            // Fallback to legacy checkout if new checkout fails
            if (useNewCheckout)
            {
                _logger.LogWarning("Falling back to legacy checkout");
                return await ProcessLegacyCheckout(request);
            }

            throw;
        }
    }

    private async Task<bool> ShouldUseNewCheckout(string userId)
    {
        // Check if globally enabled
        if (!await _flagService.IsFlagEnabledAsync("new-checkout-flow"))
            return false;

        // Get rollout percentage
        var rolloutPercentage = await _flagService.GetValueAsync<int>("new-checkout-rollout", 0);

        // Always use new checkout for internal users
        var internalUsers = await _flagService.GetValueAsync<string>("internal-user-emails", "");
        if (!string.IsNullOrEmpty(internalUsers))
        {
            var internalList = internalUsers.Split(',');
            if (internalList.Any(email => userId.Contains(email)))
                return true;
        }

        // Percentage-based rollout
        var userHash = Math.Abs(userId.GetHashCode()) % 100;
        return userHash < rolloutPercentage;
    }

    private async Task<CheckoutResponse> ProcessNewCheckout(CheckoutRequest request)
    {
        // New checkout with improved features
        return new CheckoutResponse 
        { 
            Success = true, 
            OrderId = Guid.NewGuid().ToString(),
            CheckoutVersion = "v2"
        };
    }

    private async Task<CheckoutResponse> ProcessLegacyCheckout(CheckoutRequest request)
    {
        // Legacy checkout
        return new CheckoutResponse 
        { 
            Success = true, 
            OrderId = Guid.NewGuid().ToString(),
            CheckoutVersion = "v1"
        };
    }
}

public record CheckoutRequest(string UserId, List<CartItem> Items, PaymentInfo Payment);
public record CheckoutResponse(bool Success, string OrderId, string CheckoutVersion);
public record CartItem(string ProductId, int Quantity);
public record PaymentInfo(string Method, string Token);

Payment Gateway Switching

Switch payment gateways without code deployment.

public class PaymentGatewayService
{
    private readonly IFeatureFlagService _flagService;
    private readonly StripePaymentProvider _stripeProvider;
    private readonly PayPalPaymentProvider _paypalProvider;
    private readonly ILogger<PaymentGatewayService> _logger;

    public PaymentGatewayService(
        IFeatureFlagService flagService,
        StripePaymentProvider stripeProvider,
        PayPalPaymentProvider paypalProvider,
        ILogger<PaymentGatewayService> logger)
    {
        _flagService = flagService;
        _stripeProvider = stripeProvider;
        _paypalProvider = paypalProvider;
        _logger = logger;
    }

    public async Task<PaymentResult> ProcessPayment(decimal amount, string currency, string customerId)
    {
        // Get active payment gateway
        var gateway = await _flagService.GetValueAsync<string>("payment-gateway", "stripe");

        _logger.LogInformation(
            "Processing payment of {Amount} {Currency} via {Gateway}", 
            amount, currency, gateway
        );

        // Check gateway-specific kill switch
        var gatewayEnabled = await _flagService.IsFlagEnabledAsync($"payment-gateway-{gateway}");

        if (!gatewayEnabled)
        {
            _logger.LogWarning("Payment gateway {Gateway} is disabled, falling back to default", gateway);
            gateway = "stripe"; // Default fallback
        }

        try
        {
            return gateway.ToLowerInvariant() switch
            {
                "paypal" => await _paypalProvider.ProcessPayment(amount, currency, customerId),
                "stripe" => await _stripeProvider.ProcessPayment(amount, currency, customerId),
                _ => throw new InvalidOperationException($"Unknown payment gateway: {gateway}")
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Payment processing failed via {Gateway}", gateway);

            // Try fallback gateway
            if (gateway != "stripe")
            {
                _logger.LogInformation("Attempting fallback to Stripe");
                return await _stripeProvider.ProcessPayment(amount, currency, customerId);
            }

            throw;
        }
    }

    public async Task<Dictionary<string, bool>> GetGatewayHealth()
    {
        return new Dictionary<string, bool>
        {
            ["stripe"] = await _flagService.IsFlagEnabledAsync("payment-gateway-stripe"),
            ["paypal"] = await _flagService.IsFlagEnabledAsync("payment-gateway-paypal")
        };
    }
}

// Example providers
public class StripePaymentProvider
{
    public async Task<PaymentResult> ProcessPayment(decimal amount, string currency, string customerId)
    {
        // Stripe payment logic
        return new PaymentResult(true, "stripe-transaction-id", "Stripe");
    }
}

public class PayPalPaymentProvider
{
    public async Task<PaymentResult> ProcessPayment(decimal amount, string currency, string customerId)
    {
        // PayPal payment logic
        return new PaymentResult(true, "paypal-transaction-id", "PayPal");
    }
}

public record PaymentResult(bool Success, string TransactionId, string Gateway);

UI Theme Management

Dynamic theme switching with user preferences.

public class ThemeService
{
    private readonly IFeatureFlagService _flagService;

    public ThemeService(IFeatureFlagService flagService)
    {
        _flagService = flagService;
    }

    public async Task<ThemeConfiguration> GetThemeForUser(string userId, string? userPreference = null)
    {
        // Check if theme customization is enabled
        if (!await _flagService.IsFlagEnabledAsync("theme-customization"))
        {
            return ThemeConfiguration.Default;
        }

        // User preference takes priority
        if (!string.IsNullOrEmpty(userPreference))
        {
            return GetThemeByName(userPreference);
        }

        // Check for A/B test variant
        var abTestEnabled = await _flagService.IsFlagEnabledAsync("theme-ab-test");
        if (abTestEnabled)
        {
            var variant = await GetABTestVariant(userId);
            if (variant != null)
            {
                return variant;
            }
        }

        // Default theme from flag
        var defaultTheme = await _flagService.GetValueAsync<string>("default-theme", "light");
        return GetThemeByName(defaultTheme);
    }

    private async Task<ThemeConfiguration?> GetABTestVariant(string userId)
    {
        var variantRollout = await _flagService.GetValueAsync<int>("theme-variant-rollout", 0);

        if (variantRollout == 0)
            return null;

        var userHash = Math.Abs(userId.GetHashCode()) % 100;

        if (userHash < variantRollout)
        {
            return ThemeConfiguration.DarkModern;
        }

        return null;
    }

    private ThemeConfiguration GetThemeByName(string name)
    {
        return name.ToLowerInvariant() switch
        {
            "dark" => ThemeConfiguration.Dark,
            "dark-modern" => ThemeConfiguration.DarkModern,
            "high-contrast" => ThemeConfiguration.HighContrast,
            _ => ThemeConfiguration.Default
        };
    }
}

public record ThemeConfiguration(
    string Name,
    string PrimaryColor,
    string BackgroundColor,
    string TextColor)
{
    public static ThemeConfiguration Default => new("light", "#007bff", "#ffffff", "#000000");
    public static ThemeConfiguration Dark => new("dark", "#0d6efd", "#212529", "#ffffff");
    public static ThemeConfiguration DarkModern => new("dark-modern", "#6366f1", "#0f172a", "#f8fafc");
    public static ThemeConfiguration HighContrast => new("high-contrast", "#000000", "#ffffff", "#000000");
}

// API endpoint
app.MapGet("/api/theme", async (string userId, string? preference, ThemeService themeService) =>
{
    var theme = await themeService.GetThemeForUser(userId, preference);
    return Results.Ok(theme);
});

Rate Limiting with Feature Flags

Dynamic rate limit configuration.

public class RateLimitMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IMemoryCache _cache;

    public RateLimitMiddleware(RequestDelegate next, IMemoryCache cache)
    {
        _next = next;
        _cache = cache;
    }

    public async Task InvokeAsync(HttpContext context, IFeatureFlagService flagService)
    {
        // Check if rate limiting is enabled
        if (!await flagService.IsFlagEnabledAsync("enable-rate-limiting"))
        {
            await _next(context);
            return;
        }

        var endpoint = context.GetEndpoint();
        var rateLimitAttribute = endpoint?.Metadata.GetMetadata<RateLimitAttribute>();

        if (rateLimitAttribute != null)
        {
            var clientId = GetClientIdentifier(context);
            var limitKey = $"rate-limit:{rateLimitAttribute.Resource}:{clientId}";

            // Get limit from feature flag (allows dynamic adjustment)
            var limit = await flagService.GetValueAsync<int>(
                $"rate-limit-{rateLimitAttribute.Resource}", 
                rateLimitAttribute.DefaultLimit
            );

            var windowMinutes = await flagService.GetValueAsync<int>(
                $"rate-limit-window-{rateLimitAttribute.Resource}", 
                1
            );

            var requestCount = _cache.GetOrCreate(limitKey, entry =>
            {
                entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(windowMinutes);
                return 0;
            });

            if (requestCount >= limit)
            {
                context.Response.StatusCode = 429;
                await context.Response.WriteAsJsonAsync(new
                {
                    Error = "Rate limit exceeded",
                    Limit = limit,
                    Window = $"{windowMinutes} minutes"
                });
                return;
            }

            _cache.Set(limitKey, requestCount + 1);
        }

        await _next(context);
    }

    private string GetClientIdentifier(HttpContext context)
    {
        // Try to get user ID
        var userId = context.User?.FindFirst("sub")?.Value;
        if (!string.IsNullOrEmpty(userId))
            return $"user:{userId}";

        // Fallback to IP address
        return $"ip:{context.Connection.RemoteIpAddress}";
    }
}

[AttributeUsage(AttributeTargets.Method)]
public class RateLimitAttribute : Attribute
{
    public string Resource { get; }
    public int DefaultLimit { get; }

    public RateLimitAttribute(string resource, int defaultLimit = 100)
    {
        Resource = resource;
        DefaultLimit = defaultLimit;
    }
}

// Usage
app.UseMiddleware<RateLimitMiddleware>();

app.MapGet("/api/search")
   .WithMetadata(new RateLimitAttribute("search", 10));

Feature Deprecation Workflow

Gracefully deprecate features with warnings.

public class FeatureDeprecationMiddleware
{
    private readonly RequestDelegate _next;

    public FeatureDeprecationMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, IFeatureFlagService flagService)
    {
        var endpoint = context.GetEndpoint();
        var deprecation = endpoint?.Metadata.GetMetadata<DeprecatedFeatureAttribute>();

        if (deprecation != null)
        {
            var deprecationMode = await flagService.GetValueAsync<string>(
                $"deprecation-{deprecation.FeatureKey}", 
                "warn"
            );

            switch (deprecationMode.ToLowerInvariant())
            {
                case "block":
                    context.Response.StatusCode = 410; // Gone
                    await context.Response.WriteAsJsonAsync(new
                    {
                        Error = "This feature has been deprecated and is no longer available",
                        DeprecatedSince = deprecation.DeprecatedSince,
                        Migration = deprecation.MigrationGuide
                    });
                    return;

                case "warn":
                    context.Response.Headers.Add("X-Feature-Deprecated", "true");
                    context.Response.Headers.Add("X-Deprecation-Date", deprecation.DeprecatedSince.ToString("O"));
                    context.Response.Headers.Add("X-Migration-Guide", deprecation.MigrationGuide);
                    break;

                case "silent":
                    // Allow without warning
                    break;
            }
        }

        await _next(context);
    }
}

[AttributeUsage(AttributeTargets.Method)]
public class DeprecatedFeatureAttribute : Attribute
{
    public string FeatureKey { get; }
    public DateTime DeprecatedSince { get; }
    public string MigrationGuide { get; }

    public DeprecatedFeatureAttribute(string featureKey, string deprecatedSince, string migrationGuide)
    {
        FeatureKey = featureKey;
        DeprecatedSince = DateTime.Parse(deprecatedSince);
        MigrationGuide = migrationGuide;
    }
}

// Usage
app.MapGet("/api/v1/old-endpoint")
   .WithMetadata(new DeprecatedFeatureAttribute(
       "old-api-v1", 
       "2024-01-01", 
       "https://docs.example.com/migration/v2"
   ));

Canary Deployments

Use feature flags for canary deployments.

public class CanaryDeploymentService
{
    private readonly IFeatureFlagService _flagService;
    private readonly ILogger<CanaryDeploymentService> _logger;

    public CanaryDeploymentService(
        IFeatureFlagService flagService,
        ILogger<CanaryDeploymentService> logger)
    {
        _flagService = flagService;
        _logger = logger;
    }

    public async Task<bool> ShouldUseCanaryVersion(string userId, string feature)
    {
        // Check if canary deployment is active
        if (!await _flagService.IsFlagEnabledAsync($"canary-{feature}"))
            return false;

        // Get canary rollout percentage
        var canaryPercentage = await _flagService.GetValueAsync<int>(
            $"canary-{feature}-percentage", 
            5 // Default 5%
        );

        // Check if user is in canary group
        var userHash = Math.Abs(userId.GetHashCode()) % 100;
        var isCanary = userHash < canaryPercentage;

        if (isCanary)
        {
            _logger.LogInformation(
                "User {UserId} selected for canary deployment of {Feature}", 
                userId, feature
            );
        }

        return isCanary;
    }

    public async Task<ServiceResponse<T>> ExecuteWithCanary<T>(
        string userId,
        string feature,
        Func<Task<T>> stableVersion,
        Func<Task<T>> canaryVersion)
    {
        var useCanary = await ShouldUseCanaryVersion(userId, feature);

        try
        {
            var result = useCanary 
                ? await canaryVersion() 
                : await stableVersion();

            return ServiceResponse<T>.Success(result, useCanary ? "canary" : "stable");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error in {Version} version of {Feature}", 
                useCanary ? "canary" : "stable", feature);

            // Fallback to stable if canary fails
            if (useCanary)
            {
                _logger.LogWarning("Falling back to stable version");
                var fallbackResult = await stableVersion();
                return ServiceResponse<T>.Success(fallbackResult, "stable-fallback");
            }

            throw;
        }
    }
}

public record ServiceResponse<T>(T Data, string Version, bool Success)
{
    public static ServiceResponse<T> Success(T data, string version) => 
        new(data, version, true);
}

// Usage
var response = await canaryService.ExecuteWithCanary(
    userId,
    "recommendation-engine",
    stableVersion: async () => await GetStableRecommendations(userId),
    canaryVersion: async () => await GetMLRecommendations(userId)
);

Next Steps