patternModeratepending
Pattern: Feature toggle lifecycle management
Viewed 0 times
feature-flaglifecyclecleanuptech-debttoggledeprecation
Problem
Feature flags accumulate as tech debt. Old flags remain in code forever because nobody remembers to clean them up.
Solution
Manage feature flag lifecycle:
const flags = {
NEW_CHECKOUT: {
description: 'New checkout flow with Apple Pay',
owner: 'payments-team',
created: '2026-01-15',
jira: 'PAY-123',
cleanup_by: '2026-04-15', // 90 day deadline
status: 'rolling-out', // created, testing, rolling-out, ga, deprecated
},
};
CREATED -> TESTING -> ROLLING_OUT -> GA -> DEPRECATED -> REMOVED
- Created: Flag added, code behind flag
- Testing: QA and internal testing
- Rolling out: Gradual % rollout
- GA (Generally Available): 100% enabled
- Deprecated: Flag marked for removal
- Removed: Dead code and flag removed
# CI check for expired flags:
for flag in flags:
if flag.status == 'ga' and now > flag.cleanup_by:
warn(f'Flag {flag.name} is past cleanup date!')
# Lint rule: flag references should have expiry comment
# // FEATURE_FLAG: NEW_CHECKOUT expires 2026-04-15
- Remove flag checks (keep the enabled code path)
- Remove the disabled code path
- Remove flag from configuration
- Deploy
- Delete flag from flag service
- Total active flags (should stay bounded)
- Average flag age
- Flags past cleanup date
- Create with metadata:
const flags = {
NEW_CHECKOUT: {
description: 'New checkout flow with Apple Pay',
owner: 'payments-team',
created: '2026-01-15',
jira: 'PAY-123',
cleanup_by: '2026-04-15', // 90 day deadline
status: 'rolling-out', // created, testing, rolling-out, ga, deprecated
},
};
- Lifecycle stages:
CREATED -> TESTING -> ROLLING_OUT -> GA -> DEPRECATED -> REMOVED
- Created: Flag added, code behind flag
- Testing: QA and internal testing
- Rolling out: Gradual % rollout
- GA (Generally Available): 100% enabled
- Deprecated: Flag marked for removal
- Removed: Dead code and flag removed
- Automated cleanup:
# CI check for expired flags:
for flag in flags:
if flag.status == 'ga' and now > flag.cleanup_by:
warn(f'Flag {flag.name} is past cleanup date!')
# Lint rule: flag references should have expiry comment
# // FEATURE_FLAG: NEW_CHECKOUT expires 2026-04-15
- Removal process:
- Remove flag checks (keep the enabled code path)
- Remove the disabled code path
- Remove flag from configuration
- Deploy
- Delete flag from flag service
- Metrics:
- Total active flags (should stay bounded)
- Average flag age
- Flags past cleanup date
Why
Feature flags are temporary by design. Without a cleanup process, they become permanent tech debt that obscures code and slows development.
Revisions (0)
No revisions yet.