Feature flags let you deploy code without immediately exposing new features to all users. Claude Code can design a complete feature flag system — from definition to percentage rollouts — when given the right patterns.
CLAUDE.md for Feature Flag Standards
## Feature Flag Rules
### When to use (required)
- Any new feature affecting production behavior
- A/B tests
- Staged rollouts (5% → 50% → 100%)
### Implementation rules
- Flag definitions: src/config/flags.ts (single source of truth)
- Flag names: snake_case (new_checkout_flow, ai_recommendations)
- Each flag must have: creation date, owner, expiry date in comment
- Default value: always false (opt-in)
- Maximum lifetime: 3 months → then delete
### Flag types
1. Release flags: turn feature on/off (delete when fully released)
2. Experiment flags: A/B tests (delete after experiment ends)
3. Operational flags: emergency kill switches (may persist longer)
### Prohibited
- if (process.env.NODE_ENV === 'development') branching (use flags instead)
- Version numbers in flag names (new_checkout_v2 → new_checkout)
- Flags abandoned for > 90 days
Generating the Feature Flag System
Generate a simple feature flag system.
Requirements:
- Environment-variable based (no Redis dependency)
- TypeScript with full type safety
- Per-user enablement (enable for specific user IDs)
- Percentage rollout (enable for X% of users)
Generate these files:
- src/config/flags.ts (flag definitions)
- src/lib/featureFlag.ts (flag engine)
- src/middleware/flagContext.ts (attach flag context to requests)
Generated Flag Definitions
// src/config/flags.ts
export const FLAGS = {
/**
* New single-page checkout flow
* Created: 2026-01-15 @developer1
* Expires: 2026-04-15
*/
new_checkout_flow: {
defaultValue: false,
description: 'New checkout UI (3-step → 1-page)',
rolloutPercentage: 20, // Enable for 20% of users
},
/**
* AI-powered product recommendations
* Created: 2026-02-01 @developer2
* Expires: 2026-05-01
*/
ai_recommendations: {
defaultValue: false,
description: 'Use AI model for product recommendations',
enabledUserIds: ['user_123', 'user_456'], // Specific users only
},
} as const;
export type FeatureFlag = keyof typeof FLAGS;
// src/lib/featureFlag.ts
export class FeatureFlagService {
isEnabled(flag: FeatureFlag, userId?: string): boolean {
const config = FLAGS[flag];
// Specific user list
if ('enabledUserIds' in config && userId) {
if (config.enabledUserIds.includes(userId)) return true;
}
// Percentage rollout (deterministic by userId)
if ('rolloutPercentage' in config && userId) {
const hash = [...userId].reduce((acc, c) => acc + c.charCodeAt(0), 0);
return (hash % 100) < config.rolloutPercentage;
}
return config.defaultValue;
}
}
export const featureFlags = new FeatureFlagService();
Branching with Feature Flags
Use feature flags to switch between old and new recommendation logic.
Old: GET /products/:id → legacy recommendation service
New: when ai_recommendations flag is ON → call AI recommendation service
Requirements:
- Flag OFF has zero performance impact
- Response format stays identical regardless of flag
- Log flag value as metric
Generated:
router.get('/products/:id', async (req, res) => {
const useAI = featureFlags.isEnabled('ai_recommendations', req.user.id);
const recommendations = useAI
? await aiRecommendationService.get(req.params.id)
: await legacyRecommendationService.get(req.params.id);
// Track flag usage as metric
metrics.increment('feature_flag.evaluated', {
flag: 'ai_recommendations',
enabled: String(useAI),
});
res.json({
product: await productService.get(req.params.id),
recommendations,
});
});
Auto-Detecting Expired Flags with Claude Code Hooks
# .claude/hooks/check_flag_expiry.py
import json, re, sys
from datetime import datetime
data = json.load(sys.stdin)
content = data.get("tool_input", {}).get("content", "") or ""
fp = data.get("tool_input", {}).get("file_path", "")
if "flags.ts" not in (fp or ""):
sys.exit(0)
today = datetime.now().date()
for match in re.finditer(r'Expires: (\d{4}-\d{2}-\d{2})', content):
deadline = datetime.strptime(match.group(1), '%Y-%m-%d').date()
if deadline < today:
print(f"[FLAG] Expired flag detected (Expires: {match.group(1)})", file=sys.stderr)
print("[FLAG] Remove this flag and the conditional code it controls", file=sys.stderr)
sys.exit(1) # Warning only — doesn't block
sys.exit(0)
This runs every time flags.ts is modified, preventing expired flags from accumulating.
Summary
Design feature flags with Claude Code:
- CLAUDE.md — Define flag lifecycle, naming, expiry requirement
- Typed flag definitions — Expiry dates and owners in comments
- Percentage rollout — Deterministic user-hash based rollout
- Clean branching — Same response shape regardless of flag state
- Hook enforcement — Auto-detect expired flags on file edit
Code Review Pack (¥980) includes /code-review for detecting flag misuse — missing expiry dates, dead code from old flags, incorrect branching patterns.
Myouga (@myougatheaxo) — Claude Code engineer focused on safe deployment patterns.
Top comments (0)