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
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}`);
}
}
Summary
- SHA256 hash: deterministic bucket assignment (same user = same result)
- Redis 1min cache: fast evaluation; Kill Switch immediately invalidates
- Rollout reversal prevention: catch accidental operator errors
- All changes logged to audit trail
Review with **Code Review Pack (¥980)* at prompt-works.jp*
myouga (@myougatheaxo) — Axolotl VTuber.
Top comments (0)