Skip to content
Development
Skill

/feature-flag-patterns

Gradual rollout strategies, kill switches, A/B testing integration, flag lifecycle management, and technical debt prevention.

From plugin
vibecosystem
534200 skills138 agents7 hooks
Install
$ npx -y skills add vibeeval/vibecosystem --skill feature-flag-patterns --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/feature-flag-patterns

Context preview

The summary Claude sees to decide when to auto-load this skill.

Gradual rollout strategies, kill switches, A/B testing integration, flag lifecycle management, and technical debt prevention.

SKILL.md

feature-flag-patterns.SKILL.md
name: feature-flag-patterns
description: Gradual rollout strategies, kill switches, A/B testing integration, flag lifecycle management, and technical debt prevention.

Feature Flag Patterns

Feature flag strategies for safe deployments, experimentation, and operational control.

Flag Types and Structure

interface FeatureFlag {
  key: string
  type: 'boolean' | 'percentage' | 'variant' | 'segment'
  enabled: boolean
  description: string
  owner: string              // Team/person responsible
  createdAt: Date
  expiresAt?: Date           // Auto-cleanup reminder
  tags: string[]             // 'release', 'experiment', 'ops', 'permission'
}

// Flag categories by lifecycle
enum FlagCategory {
  RELEASE = 'release',       // Temporary: ship behind flag, remove after rollout
  EXPERIMENT = 'experiment', // Temporary: A/B test, remove after analysis
  OPS = 'ops',               // Long-lived: kill switches, circuit breakers
  PERMISSION = 'permission', // Long-lived: premium features, entitlements
}

// Flag configuration
const FLAGS: Record<string, FeatureFlag> = {
  'new-checkout-flow': {
    key: 'new-checkout-flow',
    type: 'percentage',
    enabled: true,
    description: 'Redesigned checkout with fewer steps',
    owner: 'checkout-team',
    createdAt: new Date('2025-01-15'),
    expiresAt: new Date('2025-04-15'),   // 3 month max
    tags: ['release'],
  },
  'emergency-read-only': {
    key: 'emergency-read-only',
    type: 'boolean',
    enabled: false,
    description: 'Kill switch: disable all write operations',
    owner: 'platform-team',
    createdAt: new Date('2024-06-01'),
    tags: ['ops'],
  },
}

Gradual Rollout

class FeatureFlagService {
  constructor(private store: FlagStore) {}

  // Percentage-based rollout using consistent hashing
  isEnabled(flagKey: string, userId: string): boolean {
    const flag = this.store.getFlag(flagKey)
    if (!flag || !flag.enabled) return false

    switch (flag.type) {
      case 'boolean':
        return flag.enabled

      case 'percentage': {
        // Consistent: same user always gets same result for same flag
        const hash = this.consistentHash(flagKey, userId)
        return hash < (flag.percentage ?? 0)
      }

      case 'segment':
        return this.isInSegment(userId, flag.segments ?? [])

      case 'variant':
        return true  // Use getVariant() instead
    }
  }

  // Deterministic hash: user always gets same bucket for same flag
  private consistentHash(flagKey: string, userId: string): number {
    const input = `${flagKey}:${userId}`
    let hash = 0
    for (let i = 0; i < input.length; i++) {
      const char = input.charCodeAt(i)
      hash = ((hash << 5) - hash) + char
      hash = hash & hash  // Convert to 32-bit integer
    }
    return Math.abs(hash) % 100  // 0-99
  }

  // Multi-variant for A/B/C testing
  getVariant(flagKey: string, userId: string): string {
    const flag = this.store.getFlag(flagKey)
    if (!flag?.enabled || !flag.variants) return 'control'

    const hash = this.consistentHash(flagKey, userId)
    let cumulative = 0
    for (const variant of flag.variants) {
      cumulative += variant.weight
      if (hash < cumulative) return variant.name
    }
    return 'control'
  }

  // Segment-based targeting
  private isInSegment(userId: string, segments: Segment[]): boolean {
    const user = this.store.getUser(userId)
    return segments.some(segment => {
      switch (segment.type) {
        case 'user_list': return segment.userIds.includes(userId)
        case 'attribute': return user?.[segment.attribute] === segment.value
        case 'percentage': return this.consistentHash(segment.id, userId) < segment.value
      }
    })
  }
}

Kill Switch Pattern

// Operational flags: immediately disable features under load
class KillSwitch {
  private cache = new Map<string, { value: boolean; expiry: number }>()
  private pollInterval: NodeJS.Timeout

  constructor(private store: FlagStore, pollMs: number = 5000) {
    // Poll every 5s for kill switch changes (fast response)
    this.pollInterval = setInterval(() => this.refresh(), pollMs)
  }

  isKilled(switchKey: string): boolean {
    const cached = this.cache.get(switchKey)
    if (cached && cached.expiry > Date.now()) {
      return cached.value
    }
    // Fallback: if cache miss, assume NOT killed (fail open)
    return false
  }

  private async refresh(): Promise<void> {
    try {
      const switches = await this.store.getKillSwitches()
      for (const [key, value] of Object.entries(switches)) {
        this.cache.set(key, { value, expiry: Date.now() + 10_000 })
      }
    } catch {
      // On error, extend existing cache TTL (don't flip switches on failure)
    }
  }

  destroy(): void {
    clearInterval(this.pollInterval)
  }
}

// Usage in request handler
async function handleCheckout(req: Request, res: Response) {
  if (killSwitch.isKilled('checkout-disabled')) {
    return res.status(503).json({
      error: 'Checkout temporarily unavailable',
      retryAfter: 300,
    })
  }
  // ... normal checkout logic
}

A/B Testing Integration

// Track experiment exposure and outcomes
interface ExperimentEvent {
  experimentKey: string
  variant: string
  userId: string
  timestamp: Date
  eventType: 'exposure' | 'conversion'
  metadata?: Record<string, unknown>
}

class ExperimentTracker {
  constructor(private analytics: AnalyticsClient) {}

  trackExposure(flagKey: string, variant: string, userId: string): void {
    this.analytics.track({
      experimentKey: flagKey,
      variant,
      userId,
      timestamp: new Date(),
      eventType: 'exposure',
    })
  }

  trackConversion(flagKey: string, userId: string, value?: number): void {
    this.analytics.track({
      experimentKey: flagKey,
      variant: featureFlags.getVariant(flagKey, userId),
      userId,
      timestamp: new Date(),
      eventType: 'conversion',
      meta
Read more
Ships withvibecosystem

Your AI software team. Built on Claude Code. vibecosystem turns Claude Code into a full AI software team — 138 specialized agents that plan, build, review, test, and learn from every mistake. No configuration needed — just install and code.

Get the whole plugin

Other skills on vibecosystem.