DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Design Feature Flag Strategy with Claude Code: Gradual Rollout, Kill Switch, A/B Testing

Introduction

Separate deployment from release — feature flags safely roll out new features gradually and provide kill switches for immediate disable. Let Claude Code design this.

CLAUDE.md Rules

## Feature Flag Rules
- Types: Kill Switch, Rollout, Experiment, Entitlement
- Deterministic: SHA256 hash for consistent user assignment
- Kill Switch: immediate off with Redis cache invalidation
- Rollout: 1%->5%->20%->100% (no reversal allowed)
- Experiment: auto-archive after 30 days
Enter fullscreen mode Exit fullscreen mode

Generated Implementation

export class FeatureFlagService {
  async isEnabled(flagKey: string, context: { userId?: string; planId?: string }) {
    const flag = await this.getFlag(flagKey); // Redis cached, 1min TTL
    if (!flag || !flag.enabled) return false;
    if (flag.expiresAt && new Date() > flag.expiresAt) return false;

    // Whitelist: force enabled
    if (context.userId && flag.allowedUserIds?.includes(context.userId)) return true;

    // Entitlement: plan-based
    if (flag.type === 'entitlement') {
      return !!context.planId && flag.allowedPlans.includes(context.planId);
    }

    // Rollout: deterministic bucket assignment
    if (flag.rolloutPercentage !== undefined && context.userId) {
      const bucket = parseInt(createHash('sha256').update(`${context.userId}:${flagKey}`).digest('hex').slice(0, 8), 16) % 100;
      return bucket < flag.rolloutPercentage;
    }

    return flag.enabled;
  }

  // Kill switch: immediate disable
  async killSwitch(flagKey: string, reason: string) {
    await prisma.featureFlag.update({ where: { key: flagKey }, data: { enabled: false } });
    await redis.del(`flag:${flagKey}`); // Immediate cache invalidation
    await sendSlackAlert(`Kill Switch: ${flagKey}. Reason: ${reason}`);
  }

  // Rollout: prevent reversal
  async progressRollout(flagKey: string, percentage: number) {
    const current = await prisma.featureFlag.findUnique({ where: { key: flagKey } });
    if (percentage < (current?.rolloutPercentage ?? 0)) {
      throw new ValidationError('Cannot reduce rollout percentage');
    }
    await prisma.featureFlag.update({ where: { key: flagKey }, data: { rolloutPercentage: percentage } });
    await redis.del(`flag:${flagKey}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Summary

  1. SHA256 hash: deterministic bucket assignment (same user = same result)
  2. Redis 1min cache: fast evaluation; Kill Switch immediately invalidates
  3. Rollout reversal prevention: catch accidental operator errors
  4. All changes logged to audit trail

Review with **Code Review Pack (¥980)* at prompt-works.jp*

myouga (@myougatheaxo) — Axolotl VTuber.

Top comments (0)