.claude/skills/feature-flag-management/SKILL.md
Use when implementing feature flags for trunk-based development with incomplete features behind toggles, progressive rollouts (canary, percentage, time-window), per-tenant feature sets, optional AI or premium features, or kill switches that disable production features without redeployment. Covers Microsoft.FeatureManagement, IFeatureManager, built-in and custom feature filters, endpoint and middleware gating, feature flag constants, and flag lifecycle management from creation through removal. Domain: Release Management, Configuration, Progressive Delivery. Level: Intermediate. Tags: feature-flags, feature-management, feature-toggles, progressive-delivery, canary.
npx skillsauth add klod68/littlerae feature-flag-managementInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Without feature flags:
if checks and boolean config valuesMicrosoft.FeatureManagementUse the Microsoft.FeatureManagement library for structured feature flag evaluation with built-in filters, DI integration, and ASP.NET middleware support.
// appsettings.json
{
"FeatureManagement": {
"AiChatbot": true,
"NewCheckoutFlow": false,
"BetaDashboard": true
}
}
builder.Services.AddFeatureManagement();
public class OrderService
{
private readonly IFeatureManager _featureManager;
public async Task<OrderResult> PlaceOrderAsync(Order order, CancellationToken ct)
{
if (await _featureManager.IsEnabledAsync("NewCheckoutFlow"))
{
return await ProcessWithNewFlowAsync(order, ct);
}
return await ProcessWithLegacyFlowAsync(order, ct);
}
}
<feature name="BetaDashboard">
<div class="beta-dashboard">
<!-- Only rendered when flag is enabled -->
</div>
</feature>
{
"FeatureManagement": {
"NewCheckoutFlow": {
"EnabledFor": [
{
"Name": "Percentage",
"Parameters": { "Value": 25 }
}
]
}
}
}
{
"FeatureManagement": {
"HolidayBanner": {
"EnabledFor": [
{
"Name": "TimeWindow",
"Parameters": {
"Start": "2026-12-20T00:00:00Z",
"End": "2026-12-31T23:59:59Z"
}
}
]
}
}
}
builder.Services.AddFeatureManagement()
.WithTargeting();
// Register targeting context accessor
builder.Services.AddSingleton<ITargetingContextAccessor, HttpContextTargetingContextAccessor>();
{
"FeatureManagement": {
"BetaDashboard": {
"EnabledFor": [
{
"Name": "Targeting",
"Parameters": {
"Audience": {
"Users": ["[email protected]", "[email protected]"],
"Groups": [
{ "Name": "BetaTesters", "RolloutPercentage": 100 },
{ "Name": "InternalUsers", "RolloutPercentage": 50 }
],
"DefaultRolloutPercentage": 0
}
}
}
]
}
}
}
[FilterAlias("TenantFilter")]
public class TenantFeatureFilter : IFeatureFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public TenantFeatureFilter(IHttpContextAccessor httpContextAccessor)
=> _httpContextAccessor = httpContextAccessor;
public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
{
var settings = context.Parameters.Get<TenantFilterSettings>();
var tenantId = _httpContextAccessor.HttpContext?.User
.FindFirst("tenant_id")?.Value;
return Task.FromResult(
settings?.AllowedTenants?.Contains(tenantId ?? "") == true);
}
}
public class TenantFilterSettings
{
public string[] AllowedTenants { get; set; } = [];
}
/// <summary>
/// Centralized feature flag names — prevents typos and enables Find All References.
/// </summary>
public static class FeatureFlags
{
public const string AiChatbot = "AiChatbot";
public const string NewCheckoutFlow = "NewCheckoutFlow";
public const string BetaDashboard = "BetaDashboard";
public const string HolidayBanner = "HolidayBanner";
}
// Minimal API
app.MapGet("/beta/dashboard", DashboardHandler.Get)
.WithMetadata(new FeatureGateAttribute(FeatureFlags.BetaDashboard));
// MVC Controller
[FeatureGate(FeatureFlags.BetaDashboard)]
public class BetaDashboardController : Controller { }
app.UseForFeature(FeatureFlags.AiChatbot, appBuilder =>
{
appBuilder.UseMiddleware<AiChatbotMiddleware>();
});
| Phase | Action |
|-------|--------|
| Create | Add flag to config (disabled), add FeatureFlags constant |
| Develop | Code behind IsEnabledAsync check, merge to main |
| Test | Enable in staging, verify behavior |
| Rollout | Enable with percentage filter (10% → 50% → 100%) |
| Stabilize | Monitor metrics, confirm no regressions |
| Remove | Remove flag check from code, remove config entry, clean up constant |
IOptions<T> for parameterized config.IFeatureManager mocking in unit tests.IFeatureManager evaluates against the current configuration. If config is cached, flag changes take effect after cache refresh. Use IConfiguration.GetReloadToken() for dynamic reload.IsEnabledAsync is called with an undefined flag name, it returns false by default. This is safe but can hide typos — use FeatureFlags constants.tools
Use when cross-cutting concerns (logging, metrics, validation, authorization) are tangled into command handlers or service methods, when building database command pipelines with reorderable concerns, or when HTTP client pipelines or message handlers need composable, independently-replaceable processing stages. Covers ICommandInterceptor interface, InterceptorPipeline with reverse-chain construction, zero-cost Empty sentinel to skip overhead when no interceptors are registered, and ConfigureAwait(false) discipline for library code. Domain: Architecture, Cross-Cutting Concerns. Level: Intermediate. Tags: interceptor, pipeline, middleware, decorator, cross-cutting-concerns.
development
Use when writing integration tests for Razor Pages, MVC, or Minimal API applications to validate routing, middleware, page rendering, and HTTP behavior without a browser or live server, or when adding fast smoke tests to a CI pipeline. Covers WebApplicationFactory<Program> setup with public partial class Program, in-memory test server, AngleSharp HTML parsing, CSS selector assertions, redirect and status code testing, and a shared static fixture pattern for minimal per-test startup overhead. Domain: Testing, ASP.NET Core. Level: Intermediate. Tags: integration-testing, webapplicationfactory, razor-pages, anglesharp, http-testing.
development
Use when designing indexes for new tables, diagnosing slow queries that are not using indexes efficiently, reviewing index fragmentation and maintenance, or when the current indexing strategy results in key lookups, table scans, or missing index warnings. Covers clustered index key selection (narrow, unique, ever-increasing), non-clustered index design for query patterns, covering indexes with INCLUDE columns, filtered indexes for subset queries, composite index column ordering, DMV-based monitoring for missing and unused indexes, and rebuild vs reorganize maintenance thresholds. Domain: Database, Performance. Level: Intermediate. Tags: index, sql-server, covering-index, filtered-index, performance, dmv, maintenance.
development
Use when building a searchable in-memory catalog or registry for documentation sites, admin panels, or type/API browsers where you need keyword matching, fuzzy search, and ranked results without an external search engine or database. Covers RegistryService with weighted scoring across name, description, keywords, and method names; Levenshtein fuzzy matching; synonym expansion; category and subcategory filtering; and singleton DI registration for datasets of hundreds to low thousands of items. Domain: Search, Data Access Patterns. Level: Intermediate. Tags: search, registry, fuzzy-matching, in-memory, catalog, filtering.