DEV Community

kol kol
kol kol

Posted on

I Left a Dead Feature Flag in Production for 6 Months — Here's What It Cost Me

Six months. That's how long a disabled feature flag sat in my production codebase before a new team member asked: "What does this ENABLE_LEGACY_CHECKOUT thing do?"

Nobody remembered. The feature had been replaced, the flag was permanently set to false, but the dead code path was still there — loading, parsing, and checking on every single request.

The Discovery

It started as a routine code review. A junior dev flagged a function that looked suspicious:

function processPayment(order) {
  if (config.flags.ENABLE_LEGACY_CHECKOUT) {
    return legacyPaymentProcessor(order);
  }
  return newPaymentProcessor(order);
}
Enter fullscreen mode Exit fullscreen mode

The flag had been false for 187 days. Every payment went through newPaymentProcessor. But every single call still evaluated the condition, loaded the legacy module (yes, it was lazy-loaded), and ran the config lookup.

The Hidden Costs

Here's what that one dead flag was doing:

1. Performance Tax — 0.3ms per request doesn't sound like much. Multiply by 2.4 million requests per month and you're burning ~12 hours of CPU time. Not catastrophic, but it adds up across dozens of flags.

2. Cognitive Load — New developers spent an average of 20 minutes trying to understand what each flag controlled. We had 47 active flags and 12 dead ones. That's 25% of our flag registry being ghost code.

3. Testing Overhead — Our test matrix had to account for flag combinations. Dead flags meant dead test branches that nobody was maintaining. We found 3 test suites that only tested dead code paths.

4. Deployment Risk — When someone finally cleaned up the flag, they accidentally removed a config validation that was shared between legacy and new paths. Caused a 15-minute outage.

What I Did About It

1. Flag Audit Script

I wrote a quick script to scan our codebase:

# Find all feature flags and check if they're always true or always false
grep -r "config\.flags\." src/ | \
  grep -o "ENABLE_[A-Z_]*" | \
  sort | uniq -c | sort -rn
Enter fullscreen mode Exit fullscreen mode

Then cross-referenced with our LaunchDarkly dashboard to find flags that hadn't changed state in 90+ days.

2. The "Flag Funeral" Process

For each dead flag:

  1. Verify — Check logs for 30 days to confirm zero true evaluations
  2. Comment — Add a @deprecated note with removal date
  3. Remove — Delete the code path in the next sprint
  4. Celebrate — Log it in our engineering changelog (yes, really)

3. Automated Cleanup Rules

We added CI checks:

  • Flag unchanged for 60 days → warning in PR
  • Flag unchanged for 90 days → auto-create cleanup ticket
  • Flag unchanged for 120 days → block new flag creation until old ones are cleaned

The Results

After 3 weeks of cleanup:

Metric Before After
Active feature flags 47 31
Dead code branches 12 0
Avg. flag understanding time 20 min 5 min
Test suite execution time 14 min 11 min

34% reduction in flag count. 75% faster onboarding for new flags. And most importantly — zero confusion about what's live and what's dead.

The Lesson

Feature flags are like garden plants. If you don't prune them, they grow wild and start choking the things you actually care about.

Every flag you add is technical debt with an expiration date. Set the date. Enforce it.

Your future self — and your team — will thank you.


Found this useful? Follow me for more production war stories and practical devops tips.

Top comments (0)