agentleFS
michaelsvanbeekmichaelsvanbeek/personal-agent-skills/skills/feature-flags/SKILL.md

feature-flags

Skill1 starsChanged 7 months agoMITView on GitHub

Feature flag design, lifecycle management, and progressive rollout strategies. Use when: adding feature toggles to gate new functionality, implementing gradual rollouts, designing A/B experiments, managing flag lifecycle and cleanup, auditing an existing codebase for stale flags, or choosing between flag implementations. Covers flag types, evaluation strategies, rollout patterns, flag hygiene, and testing with flags.

20 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use background-jobs, rate-limiting and 6 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • YepAPISkill

    background-jobs

    Queue workers with BullMQ/Inngest/Trigger.dev, job retries, dead letter queues, concurrency.

    Also in 3 projects that use this

  • harperaaSkill

    rate-limiting

    Implement rate limiting to prevent brute force attacks, spam, and resource abuse. Use this skill when you need to protect endpoints from automated attacks, prevent API abuse, limit request frequency, or control infrastructure costs. Triggers include "rate limiting", "rate limit", "brute force", "prevent spam", "API abuse", "resource exhaustion", "DoS", "withRateLimit", "too many requests", "429 error".

    Also in 3 projects that use this

  • wpankSkill

    caching

    Caching strategies, invalidation, eviction policies, HTTP caching, distributed caching, and anti-patterns. Use when designing cache layers, choosing eviction policies, debugging stale data, or optimizing read-heavy workloads.

    Also in 3 projects that use this

  • JohnJBorenSkill

    ci-cd

    CI/CD pipeline design, GitHub Actions, testing automation, and deployment strategies

    Also in 4 projects that use this

  • OpenAISkill

    observability

    Vercel Observability expert guidance — Drains (logs, traces, speed insights, web analytics), Web Analytics, Speed Insights, runtime logs, custom events, OpenTelemetry integration, and monitoring dashboards. Use when instrumenting, debugging, or optimizing application performance and user experience on Vercel.

    Also in 6 projects that use this

  • AnthropicSkill

    incident-response

    Run an incident response workflow — triage, communicate, and write postmortem. Trigger with "we have an incident", "production is down", an alert that needs severity assessment, a status update mid-incident, or when writing a blameless postmortem after resolution.

    Also in 5 projects that use this

The file

We split this skill into the rules it gives an agent

11 rules. 0 are common, 10 are distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 17 edited versions we read keep these.

  • 10

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Release flags: Remove within 2 weeks of full rollout.

    line 48
  2. Distinctive:

    Experiment flags: Remove when experiment concludes and winner is implemented.

    line 49
  3. Distinctive:

    Default off: If the flag system is unavailable, features default to off (safe fallback).

    line 187
  4. Distinctive:

    Error rate: No increase for flagged users vs control.

    line 209
  5. Distinctive:

    Cons: No built-in UI, percentage rollout requires custom logic.

    line 263
  6. Distinctive:

    Remove a flag when:

    line 321
  7. Distinctive:

    Remove losing code path: Delete the old implementation.

    line 330
  8. Distinctive:

    Remove flag definition: Delete from config/flag service.

    line 331
  9. Distinctive:

    Weekly: Check for flags past their planned removal date.

    line 346
  10. Very specific:

    No generic names (flag-1, test, new-feature).

    line 370
  11. Distinctive:

    When auditing an existing codebase for feature flag practices:

    line 392

“Common” is measured: kept by at least 6 in 10 of the 17 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Feature Flags

When to Use

  • Gating new functionality behind a flag for gradual rollout
  • Implementing A/B experiments or percentage-based rollouts
  • Decoupling deployment from release (deploy dark, enable later)
  • Building kill switches for risky features
  • Managing long-running feature branches without merge conflicts
  • Designing flag evaluation for server-side and client-side use
  • Auditing an existing codebase for stale or orphaned flags
  • Cleaning up flags after full rollout

Core Principle: Flags Are Temporary

Every flag has a planned removal date. Flags that live forever become tech debt — invisible branching logic that no one understands. Ship the feature, verify it works, remove the flag.


Flag Types

TypeLifespanExampleOwner
ReleaseDays to weeksGate new checkout flow for gradual rolloutEngineering
ExperimentWeeks to monthsA/B test pricing page layoutProduct
OpsPermanent (rare)Kill switch for expensive feature under loadEngineering
PermissionPermanentFeature only for enterprise tier customersProduct

Rules

  • Release flags: Remove within 2 weeks of full rollout.
  • Experiment flags: Remove when experiment concludes and winner is implemented.
  • Ops flags: Review quarterly — convert to config if permanent.
  • Permission flags: These are entitlements, not feature flags. Model in authorization system if they grow complex.

Flag Schema

Flag Definition

interface FeatureFlag {
  key: string;              // Unique identifier: "checkout-v2"
  description: string;      // What this flag controls
  type: "release" | "experiment" | "ops" | "permission";
  owner: string;            // Team or person responsible
  createdAt: string;        // ISO date
  plannedRemovalDate: string; // When to clean up (required for release/experiment)
  defaultValue: boolean;    // Value when flag system is unavailable
  rules: FlagRule[];        // Evaluation rules (ordered, first match wins)
}

interface FlagRule {
  condition: FlagCondition;
  value: boolean | string;  // Boolean for toggles, string for variants
}

type FlagCondition =
  | { type: "percentage"; value: number }          // 0-100 rollout percentage
  | { type: "user_ids"; value: string[] }          // Specific users
  | { type: "attribute"; key: string; value: string } // User attribute match
  | { type: "environment"; value: string }          // "production", "staging"
  | { type: "default" };

Flag Configuration (JSON)

{
  "checkout-v2": {
    "description": "New checkout flow with saved payment methods",
    "type": "release",
    "owner": "payments-team",
    "createdAt": "2025-01-15",
    "plannedRemovalDate": "2025-02-15",
    "defaultValue": false,
    "rules": [
      { "condition": { "type": "environment", "value": "staging" }, "value": true },
      { "condition": { "type": "user_ids", "value": ["internal-tester-1", "internal-tester-2"] }, "value": true },
      { "condition": { "type": "percentage", "value": 10 }, "value": true },
      { "condition": { "type": "default" }, "value": false }
    ]
  }
}

Evaluation

Server-Side (Python)

import hashlib
from typing import Any


def evaluate_flag(flag_key: str, user_id: str, attributes: dict[str, Any]) -> bool:
    """Evaluate a feature flag for a specific user."""
    flag = get_flag_config(flag_key)
    if flag is None:
        return False  # Flag not found — default off

    for rule in flag["rules"]:
        condition = rule["condition"]

        if condition["type"] == "environment":
            if get_environment() == condition["value"]:
                return rule["value"]

        elif condition["type"] == "user_ids":
            if user_id in condition["value"]:
                return rule["value"]

        elif condition["type"] == "attribute":
            if attributes.get(condition["key"]) == condition["value"]:
                return rule["value"]

        elif condition["type"] == "percentage":
            # Deterministic: same user always gets same result for same flag
            hash_input = f"{flag_key}:{user_id}"
            hash_value = int(hashlib.sha256(hash_input.encode()).hexdigest()[:8], 16)
            bucket = hash_value % 100
            if bucket < condition["value"]:
                return rule["value"]

        elif condition["type"] == "default":
            return rule["value"]

    return flag["defaultValue"]

Client-Side (React)

import { createContext, useContext } from "react";
import type { ReactNode } from "react";

interface FlagContextValue {
  flags: Record<string, boolean>;
}

const FlagContext = createContext<FlagContextValue>({ flags: {} });

function FlagProvider({ children, flags }: { children: ReactNode; flags: Record<string, boolean> }) {
  return <FlagContext.Provider value={{ flags }}>{children}</FlagContext.Provider>;
}

function useFlag(key: string): boolean {
  const { flags } = useContext(FlagContext);
  return flags[key] ?? false;
}

// Usage in component
function CheckoutPage() {
  const useNewCheckout = useFlag("checkout-v2");

  if (useNewCheckout) {
    return <NewCheckoutFlow />;
  }
  return <LegacyCheckoutFlow />;
}

Evaluation Rules

  • Deterministic: Same user + same flag = same result. Use hash-based bucketing, not random.
  • First match wins: Rules are evaluated in order. Put specific rules (user IDs, environments) before percentage rules.
  • Default off: If the flag system is unavailable, features default to off (safe fallback).
  • Server-authoritative: Client receives pre-evaluated flags from the server. Never send flag rules to the client.

Rollout Strategies

Gradual Percentage Rollout

Day 1:  0% → 5%    (internal team + canary users)
Day 2:  5% → 10%   (monitor error rates, latency)
Day 3:  10% → 25%  (monitor business metrics)
Day 5:  25% → 50%  (compare A/B if experiment)
Day 7:  50% → 100% (full rollout)
Day 14: Remove flag (cleanup)

Monitoring During Rollout

At each stage, verify:

  • Error rate: No increase for flagged users vs control.
  • Latency: P50/P95/P99 within acceptable bounds.
  • Business metrics: Conversion rate, revenue per user, engagement.
  • User feedback: Support tickets, rage clicks, session recordings.

Rollback

If metrics degrade:

# Instant rollback: set percentage to 0
update_flag("checkout-v2", rules=[
    {"condition": {"type": "default"}, "value": False},
])

Rollback should take seconds, not minutes. This is the primary advantage of feature flags over code rollback.


Flag Storage

Simple: Config File (Small Projects)

// flags.json — committed, deployed with application
{
  "checkout-v2": true,
  "new-search": false
}

Pros: Simple, version-controlled, no external dependency. Cons: Requires deploy to change. No gradual rollout.

Medium: SSM Parameter Store / DynamoDB

import boto3
import json

ssm = boto3.client("ssm")


def get_flags() -> dict[str, Any]:
    """Load flags from SSM Parameter Store."""
    response = ssm.get_parameter(
        Name="/myapp/prod/feature-flags",
        WithDecryption=False,
    )
    return json.loads(response["Parameter"]["Value"])

Pros: Change flags without deploying. Cheap. Simple. Cons: No built-in UI, percentage rollout requires custom logic.

Advanced: Dedicated Flag Service (LaunchDarkly, Unleash, Flagsmith)

Use when:

  • Multiple teams manage their own flags.
  • You need a UI for non-engineers to toggle flags.
  • Sophisticated targeting (geo, device, plan tier) is required.
  • Audit log of flag changes is mandatory.

Testing with Feature Flags

Unit Tests: Test Both Paths

import pytest


@pytest.mark.parametrize("flag_value", [True, False])
def test_checkout_flow(flag_value: bool, monkeypatch: pytest.MonkeyPatch) -> None:
    """Test both checkout flows regardless of current flag state."""
    monkeypatch.setattr("features.evaluate_flag", lambda key, **kw: flag_value)

    result = process_checkout(user_id="test-user", cart=sample_cart)

    if flag_value:
        assert result.flow == "v2"
    else:
        assert result.flow == "legacy"

Integration Tests: Pin Flag Values

@pytest.fixture
def flags_all_on() -> dict[str, bool]:
    """Enable all feature flags for integration testing."""
    return {"checkout-v2": True, "new-search": True}


@pytest.fixture
def flags_all_off() -> dict[str, bool]:
    """Disable all feature flags for integration testing."""
    return {"checkout-v2": False, "new-search": False}

E2E Tests: Test Current State

E2E tests should test the current production state of flags. Don't override flags in E2E — if a flag is on in production, test with it on.


Flag Cleanup

When to Remove

Remove a flag when:

  • The feature is fully rolled out to 100% for at least 1 week.
  • The experiment has concluded and the winning variant is hardcoded.
  • The ops flag has been on for 6+ months and no one has toggled it.

Cleanup Process

  1. Verify: Confirm flag is at 100% (or permanently on) with no recent toggles.
  2. Remove evaluation calls: Replace if flag("checkout-v2") with the winning branch.
  3. Remove losing code path: Delete the old implementation.
  4. Remove flag definition: Delete from config/flag service.
  5. Commit: One commit per flag cleanup.

Detecting Stale Flags

# Find flag references in code
grep -r "checkout-v2" --include="*.py" --include="*.ts" --include="*.tsx" src/

# List flags with past planned removal dates
jq 'to_entries[] | select(.value.plannedRemovalDate < "2025-01-15") | .key' flags.json

Cleanup Cadence

  • Weekly: Check for flags past their planned removal date.
  • Monthly: Review all active flags — any that can be removed?
  • Quarterly: Audit permanent ops/permission flags — still needed?

Flag Naming Convention

<domain>-<feature>[-<variant>]
ExampleTypeDescription
checkout-v2ReleaseNew checkout flow
search-fuzzy-matchingReleaseEnable fuzzy search
pricing-annual-discountExperimentTest annual plan discount
api-rate-limit-strictOpsTighter rate limits under load
plan-enterprise-analyticsPermissionAnalytics for enterprise tier

Rules:

  • Lowercase, kebab-case.
  • Domain prefix for grouping.
  • Descriptive — someone unfamiliar should understand the purpose.
  • No generic names (flag-1, test, new-feature).

Anti-Patterns

Anti-PatternProblemFix
Flag with no planned removal dateLives forever, becomes invisible tech debtRequire removal date on creation for release/experiment flags
Nested flags (if flag_a and flag_b)Combinatorial explosion of states to testKeep flags independent; one flag per feature
Flag evaluated differently in two placesInconsistent behavior for the same userCentralize evaluation; pass result as parameter
Client-side flag evaluation with rulesExposes targeting logic and user segmentsServer evaluates; client receives boolean result
Using flags instead of authorizationFlag system becomes a permissions systemUse proper RBAC/ABAC for entitlements
No monitoring during rolloutDegradation goes unnoticed until 100%Define monitoring criteria before enabling
Random-based percentage rolloutSame user gets different experience on each requestUse hash-based deterministic bucketing
Flag config not version-controlledNo history of who changed what, whenStore in git or use a flag service with audit log
Monster flag guarding 500 linesToo much code behind one toggleBreak into smaller flags or defer merge until feature is complete

Audit Checklist

When auditing an existing codebase for feature flag practices:

  • Every flag has a documented owner and description
  • Release and experiment flags have a planned removal date
  • No flags past their planned removal date exist in the codebase
  • Flag evaluation is centralized (single function/hook, not ad-hoc checks)
  • Percentage-based rollout uses deterministic hashing, not random
  • Both code paths (flag on and off) are tested
  • Client-side flags are pre-evaluated by the server
  • Monitoring criteria defined before enabling a flag in production
  • Gradual rollout follows staged percentage increases
  • Flag changes are logged with who, when, and why
  • No nested or dependent flag conditions
  • Cleanup process exists and runs at least monthly
  • Flag naming follows a consistent convention

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.