Skip to content

Basic Examples

This guide provides simple, straightforward examples of common feature flag scenarios using Flaggy.

Simple Feature Toggle

The most basic use case - enable or disable a feature.

app.MapGet("/api/products", async (IFeatureFlagService flagService) =>
{
    // Check if new UI is enabled
    if (await flagService.IsFlagEnabledAsync("new-product-ui"))
    {
        return Results.Ok(GetNewProductUI());
    }

    return Results.Ok(GetLegacyProductUI());
});

A/B Testing

Test two different versions of a feature with different users.

public class ProductController : ControllerBase
{
    private readonly IFeatureFlagService _flagService;

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

    [HttpGet("api/products/{id}")]
    public async Task<IActionResult> GetProduct(int id)
    {
        // Get A/B test variant
        var variant = await _flagService.GetValueAsync<string>("product-page-variant", "A");

        return variant switch
        {
            "B" => Ok(GetProductPageVariantB(id)),
            _ => Ok(GetProductPageVariantA(id))
        };
    }

    private object GetProductPageVariantA(int id) => new { Version = "A", ProductId = id };
    private object GetProductPageVariantB(int id) => new { Version = "B", ProductId = id, ExtraFeature = true };
}

Gradual Rollout

Roll out a feature to a percentage of users.

public class FeatureRolloutService
{
    private readonly IFeatureFlagService _flagService;

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

    public async Task<bool> IsNewCheckoutEnabledForUser(string userId)
    {
        // Get rollout percentage (0-100)
        var rolloutPercentage = await _flagService.GetValueAsync<int>("new-checkout-rollout", 0);

        if (rolloutPercentage == 0)
            return false;

        if (rolloutPercentage >= 100)
            return true;

        // Use user ID hash to determine if user is in rollout group
        var userHash = Math.Abs(userId.GetHashCode()) % 100;
        return userHash < rolloutPercentage;
    }
}

// Usage in controller
app.MapGet("/checkout", async (string userId, FeatureRolloutService rolloutService) =>
{
    if (await rolloutService.IsNewCheckoutEnabledForUser(userId))
    {
        return Results.Ok("New Checkout");
    }
    return Results.Ok("Legacy Checkout");
});

Kill Switch

Quickly disable a feature in production without redeployment.

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

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

    public async Task<PaymentResult> ProcessPayment(PaymentRequest request)
    {
        // Check if payment processing is enabled (kill switch)
        if (!await _flagService.IsFlagEnabledAsync("enable-payment-processing"))
        {
            _logger.LogWarning("Payment processing is disabled via kill switch");
            return PaymentResult.Disabled("Payment processing is temporarily disabled");
        }

        // Process payment
        return await ProcessPaymentInternal(request);
    }

    private async Task<PaymentResult> ProcessPaymentInternal(PaymentRequest request)
    {
        // Actual payment logic
        return PaymentResult.Success();
    }
}

public record PaymentRequest(string CardNumber, decimal Amount);
public record PaymentResult(bool IsSuccess, string Message)
{
    public static PaymentResult Success() => new(true, "Payment successful");
    public static PaymentResult Disabled(string message) => new(false, message);
}

Maintenance Mode

Put your application in maintenance mode.

// Middleware
public class MaintenanceModeMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IFeatureFlagService _flagService;

    public MaintenanceModeMiddleware(RequestDelegate next, IFeatureFlagService flagService)
    {
        _next = next;
        _flagService = flagService;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Skip maintenance check for admin paths
        if (context.Request.Path.StartsWithSegments("/admin"))
        {
            await _next(context);
            return;
        }

        // Check maintenance mode flag
        if (await _flagService.IsFlagEnabledAsync("maintenance-mode"))
        {
            var message = await _flagService.GetValueAsync<string>("maintenance-message", 
                "System is under maintenance. Please try again later.");

            context.Response.StatusCode = 503;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsJsonAsync(new { error = message });
            return;
        }

        await _next(context);
    }
}

// Registration
app.UseMiddleware<MaintenanceModeMiddleware>();

Dark Launches

Deploy features to production but keep them hidden from users.

public class DashboardController : ControllerBase
{
    private readonly IFeatureFlagService _flagService;

    [HttpGet("api/dashboard")]
    public async Task<IActionResult> GetDashboard()
    {
        var dashboard = new
        {
            Charts = GetBasicCharts(),
            Reports = GetBasicReports()
        };

        // Dark launch: new analytics feature is deployed but not visible
        if (await _flagService.IsFlagEnabledAsync("new-analytics"))
        {
            dashboard = dashboard with 
            { 
                Analytics = GetNewAnalytics() 
            };
        }

        // Dark launch: AI recommendations deployed but disabled
        if (await _flagService.IsFlagEnabledAsync("ai-recommendations"))
        {
            dashboard = dashboard with 
            { 
                Recommendations = GetAIRecommendations() 
            };
        }

        return Ok(dashboard);
    }

    private object GetBasicCharts() => new { Type = "Basic" };
    private object GetBasicReports() => new { Type = "Basic" };
    private object GetNewAnalytics() => new { Type = "Advanced", Features = new[] { "Real-time", "Predictions" } };
    private object GetAIRecommendations() => new { Type = "AI", Model = "GPT-4" };
}

Configuration Management

Manage application configuration dynamically.

public class ConfigurationService
{
    private readonly IFeatureFlagService _flagService;

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

    public async Task<int> GetMaxUploadSizeAsync()
    {
        // Default: 10MB
        return await _flagService.GetValueAsync<int>("max-upload-size-mb", 10);
    }

    public async Task<string> GetApiEndpointAsync()
    {
        return await _flagService.GetValueAsync<string>("api-endpoint", 
            "https://api.example.com");
    }

    public async Task<bool> IsFeatureEnabledForTenant(string tenantId, string featureKey)
    {
        // Check tenant-specific flag
        var tenantFlag = $"{featureKey}-{tenantId}";
        var isTenantEnabled = await _flagService.IsFlagEnabledAsync(tenantFlag);

        if (isTenantEnabled)
            return true;

        // Fallback to global flag
        return await _flagService.IsFlagEnabledAsync(featureKey);
    }
}

// Usage
app.MapPost("/upload", async (IFormFile file, ConfigurationService config) =>
{
    var maxSize = await config.GetMaxUploadSizeAsync();
    var maxBytes = maxSize * 1024 * 1024;

    if (file.Length > maxBytes)
    {
        return Results.BadRequest($"File size exceeds maximum of {maxSize}MB");
    }

    // Process upload
    return Results.Ok("Upload successful");
});

Seeding Initial Flags

Create default flags at application startup.

var builder = WebApplication.CreateBuilder();
builder.Services.AddFlaggy(options =>
{
    options.UsePostgreSQL(
        connectionString: "Host=localhost;..."
    );
});

var app = builder.Build();

// Seed default flags
await app.SeedFeatureFlagsAsync(
    new FeatureFlag 
    { 
        Key = "new-product-ui", 
        is_enabled = false, 
        description = "New product page UI" 
    },
    new FeatureFlag 
    { 
        Key = "maintenance-mode", 
        is_enabled = false, 
        description = "Put application in maintenance mode" 
    },
    new FeatureFlag 
    { 
        Key = "enable-payment-processing", 
        is_enabled = true, 
        description = "Enable payment processing (kill switch)" 
    },
    new FeatureFlag 
    { 
        Key = "max-upload-size-mb", 
        is_enabled = true, 
        Value = "10",
        description = "Maximum upload size in megabytes" 
    }
);

app.Run();

Next Steps