Feature flags are like credit cards - incredibly useful when used responsibly, potentially disastrous when you lose track of them. Every feature flag added to your codebase is a mini if-statement, and too many create a maze of conditional logic that's hard to understand and maintain.
The solution isn't to avoid feature flags. It's to practice good flag hygiene.
The Technical Debt Problem
Imagine this codebase:
function processOrder(order: Order) {
if (flags.newOrderFlow) {
if (flags.optimizedValidation) {
return validateOrderV3(order)
}
return validateOrderV2(order)
}
if (flags.legacyPathOptimization) {
return processLegacyOrder(order)
}
return originalProcessOrder(order)
}
Four flags controlling order processing. Which combinations have been tested? What happens when you remove one? Nobody knows, and nobody wants to touch this code.
This is technical debt disguised as flexibility.
The Flag Lifecycle
Every flag should progress through these stages:
1. Creation
A flag is created for a specific purpose: gradual rollout, A/B test, kill switch, or operational toggle.
2. Active Use
The flag is actively controlling behavior in production. Regular monitoring and adjustment.
3. Decision Point
The experiment concludes, the rollout completes, or the temporary need ends. A decision is made: keep or remove?
4. Deprecation
If keeping the behavior, the flag is removed and the winning variant becomes the default code path.
5. Removal
The flag is deleted from the codebase and the flag management system.
The problem occurs when flags skip steps 3-5 and live forever in step 2.
Best Practices for Flag Hygiene
1. Set Expiration Dates
Every flag should have an expiration date:
const flag = {
name: 'new-checkout-flow',
created: '2024-10-01',
expires: '2024-11-01', // 30 days from creation
purpose: 'A/B test new checkout design',
owner: 'growth-team',
}
When the expiration date approaches, automated systems should:
- Alert the owner
- Create a ticket to remove the flag
- Eventually disable the flag automatically
2. Categorize Your Flags
Different flag types have different lifecycles:
Release Flags: Short-lived (days to weeks)
- Purpose: Gradual rollout of new features
- Lifecycle: Remove once at 100% for 2 weeks
Experiment Flags: Medium-lived (weeks to months)
- Purpose: A/B testing
- Lifecycle: Remove when experiment concludes
Operational Flags: Long-lived (months to years)
- Purpose: Kill switches, circuit breakers
- Lifecycle: Review quarterly, remove if no longer needed
Permission Flags: Permanent
- Purpose: Feature access based on user tier
- Lifecycle: These are business logic, not temporary flags
3. Document Everything
Every flag needs documentation:
/**
* Flag: enhanced-search-algorithm
* Type: Release flag
* Created: 2024-10-01
* Owner: search-team
* Purpose: Gradual rollout of ML-powered search
* Success Criteria:
* - Error rate < 0.1%
* - Search completion time < 100ms
* - User satisfaction > control group
* Removal Plan:
* - 100% rollout by 2024-10-15
* - Monitor for 2 weeks
* - Remove flag by 2024-11-01
*/
4. Make Removal Part of "Done"
A feature isn't done until its flag is removed. Update your definition of done:
- [ ] Feature implemented
- [ ] Tests passing
- [ ] Rolled out to 100%
- [ ] Monitored for 2 weeks
- [ ] Flag removed from codebase
- [ ] Flag removed from flag management system
5. Automate Flag Audits
Run weekly reports on:
- Flags older than 90 days
- Flags at 100% rollout for 30+ days
- Flags with no recent changes
- Flags with expired dates
- Flags with no documented owner
6. Implement Flag Removal Days
Schedule quarterly "flag removal days" where the team focuses on cleaning up old flags. Make it fun: gamify it, offer prizes for most flags removed.
7. Review Before Adding
Before creating a new flag, ask:
- Is this flag necessary?
- What's the removal criteria?
- Who owns this flag?
- When will this flag be removed?
- Can we reuse an existing flag?
The Removal Process
Removing a flag safely:
Step 1: Verify 100% Rollout
Ensure the flag has been at 100% for at least 2 weeks without issues.
Step 2: Remove Losing Variants
Delete unused code paths first:
// Before
if (flags.newCheckout) {
return newCheckoutFlow()
}
return oldCheckoutFlow() // Delete this first
// After
if (flags.newCheckout) {
return newCheckoutFlow()
}
Step 3: Remove Flag Check
Replace flag check with winning variant:
// Before
if (flags.newCheckout) {
return newCheckoutFlow()
}
// After
return newCheckoutFlow()
Step 4: Remove Flag Definition
Delete the flag from your flag management system.
Step 5: Update Documentation
Document what was decided and why.
Measuring Flag Hygiene
Track these metrics:
- Flag count: Total active flags
- Average flag age: How long flags live
- Flags > 90 days: Old flags that might be debt
- Flags at 100%: Candidates for removal
- Flags without owners: Orphaned flags
Good targets:
- < 50 total flags per 100,000 lines of code
- < 60 day average flag age
- < 10% of flags older than 90 days
- All flags have documented owners
When to Keep Flags Forever
Some flags should never be removed:
- Operational toggles: Kill switches for external services
- Permission flags: Feature access by user tier
- Regional flags: Features that vary by geography
- Compliance flags: Required for regulatory reasons
These are business logic, not temporary toggles. Document them as permanent flags.
Cultural Practices
Good flag hygiene requires culture change:
- Celebrate removal: Make flag deletion a positive event
- Resist permanence: Push back on "temporary" flags that become permanent
- Review regularly: Make flag audits part of sprint planning
- Own your flags: Flags are code - they need owners
- Measure and improve: Track metrics and set goals
The Payoff
Teams with good flag hygiene experience:
- Faster feature development (less conditional logic to navigate)
- Easier debugging (fewer code paths to consider)
- Better onboarding (new engineers aren't lost in flag maze)
- Higher confidence (less fear of touching old code)
- Improved performance (fewer runtime flag checks)
Start Small
If you have 200 old flags, don't try to remove them all at once:
- Stop adding to the problem (enforce expiration dates on new flags)
- Identify the oldest 10 flags
- Remove one per week
- Build momentum and make it routine
The Bottom Line
Feature flags are a powerful tool, but like any tool, they require maintenance. Practice good flag hygiene:
- Set expiration dates
- Document everything
- Make removal part of done
- Audit regularly
- Build a culture of cleanup
Your future self (and your teammates) will thank you. A clean codebase is a productive codebase.
