Blog
/

Feature Flag Hygiene: Preventing Technical Debt Before It Starts

Michael Torres
··
Best PracticesEngineering
Feature Flag Hygiene: Preventing Technical Debt Before It Starts

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:

  1. Celebrate removal: Make flag deletion a positive event
  2. Resist permanence: Push back on "temporary" flags that become permanent
  3. Review regularly: Make flag audits part of sprint planning
  4. Own your flags: Flags are code - they need owners
  5. 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:

  1. Stop adding to the problem (enforce expiration dates on new flags)
  2. Identify the oldest 10 flags
  3. Remove one per week
  4. 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.