Every engineering team that uses feature flags eventually arrives at the same uncomfortable realization: the flags are accumulating faster than they are being removed. Flags pile up not because engineers are lazy, but because there is no shared agreement about when a flag should expire, who is responsible for removing it, and what happens when nobody does.
The absence of a formal policy creates a predictable outcome. In our experience, the vast majority of feature flags are never properly removed. Enterprise applications routinely carry hundreds of active flags, with a significant portion stale for months. These are not observations about negligent teams -- they reflect what happens when teams lack clear, enforceable rules.
A feature flag expiration policy solves this problem by establishing explicit expectations before the first flag is created. It answers the questions that cause flags to rot: How long should this flag live? Who removes it? What happens if nobody does?
This article provides a complete, ready-to-use policy template that you can copy, customize, and implement this week. It also covers how to get buy-in, enforce the policy without creating bureaucratic overhead, and handle the inevitable exceptions.
Why informal agreements fail
Most teams start with informal agreements about flag cleanup. "We'll remove flags within two weeks of full rollout." "The person who creates the flag is responsible for removing it." "We'll do a cleanup sprint every quarter."
These agreements fail for structural reasons:
| Informal Agreement | Why It Fails |
|---|---|
| "Remove within two weeks" | No enforcement mechanism; new priorities always take precedence |
| "Creator removes it" | Creator leaves the team, changes roles, or forgets |
| "Quarterly cleanup sprint" | Gets deprioritized when deadlines approach; scope is overwhelming |
| "Flag owner is responsible" | Ownership is not tracked or transferred when people leave |
| "We'll clean up when it becomes a problem" | By the time it is a problem, the effort required is enormous |
Informal agreements rely on individual memory and motivation. Formal policies create systemic accountability. The difference is not philosophical -- it is visible in every codebase we have examined. Teams with documented flag policies consistently carry far fewer stale flags than teams relying on informal norms.
The feature flag expiration policy template
Below is a complete policy template. Copy it, customize the values in brackets, and distribute it to your team. Every section includes notes on what to customize and why.
FEATURE FLAG LIFECYCLE POLICY
Version: 1.0 Effective Date: [Date] Policy Owner: [Engineering Manager / Tech Lead Name] Review Cadence: Quarterly Last Reviewed: [Date]
1. Purpose
This policy establishes rules for the creation, management, and removal of feature flags in [Organization/Team Name]'s codebase(s). Its goal is to prevent flag accumulation, ensure clear ownership, and maintain codebase health without impeding development velocity.
2. Scope
This policy applies to all feature flags in the following repositories: [list repositories or "all repositories owned by this team"]. It covers flags managed through [LaunchDarkly / Unleash / Split / custom system] and any flags implemented directly in application code.
3. Flag categories and expiration rules
Every feature flag must be assigned a category at creation. Each category has a maximum lifespan. Flags that exceed their maximum lifespan enter the escalation process defined in Section 6.
| Category | Definition | Maximum Lifespan | Renewal Allowed | Renewal Limit |
|---|---|---|---|---|
| Release | Controls rollout of a new feature | 90 days | Yes, once | 90 days (180 max) |
| Experiment | Controls an A/B test or experiment | 60 days | Yes, once | 30 days (90 max) |
| Ops / Kill Switch | Emergency shutoff for a service or feature | No expiration | N/A | N/A |
| Permission | Controls access for specific user segments | 180 days | Yes, unlimited | 90 days per renewal |
| Migration | Controls traffic during a system migration | 120 days | Yes, twice | 60 days (240 max) |
| Temporary Fix | Hotfix or workaround with a planned permanent solution | 30 days | Yes, once | 30 days (60 max) |
Customization notes:
- Adjust lifespans based on your team's release cadence. Teams with weekly releases can use shorter windows; teams with monthly releases may need longer ones.
- If your team does not run experiments, remove the Experiment category.
- The "Temporary Fix" category is intentionally strict. Temporary fixes that survive past 60 days are no longer temporary -- they are permanent code that should be implemented properly.
4. Flag creation requirements
Every feature flag must include the following metadata at creation:
| Required Field | Description | Example |
|---|---|---|
| Flag key | Unique identifier following naming convention (Section 5) | release-new-checkout-flow |
| Category | One of the categories from Section 3 | Release |
| Owner | The engineer responsible for the flag's lifecycle | @jane.doe |
| Expiration date | Calculated from creation date using Section 3 rules | 2025-12-15 |
| Description | One paragraph explaining what the flag controls and why | "Controls rollout of the new checkout flow that replaces the legacy three-step process with a single-page experience." |
| Removal plan | Brief description of how to remove the flag when it expires | "Remove flag, keep new checkout code, delete legacy checkout components." |
| Linked ticket | Link to the feature/experiment/migration ticket | JIRA-1234 |
Flags created without these fields will be flagged for immediate review by the policy owner.
5. Naming conventions
Flag keys must follow this naming pattern:
{category}-{feature-description}
| Category | Prefix | Example |
|---|---|---|
| Release | release- | release-new-checkout-flow |
| Experiment | exp- | exp-pricing-page-variant-b |
| Ops / Kill Switch | ops- | ops-disable-payment-processing |
| Permission | perm- | perm-beta-users-analytics |
| Migration | migration- | migration-postgres-to-dynamodb |
| Temporary Fix | temp- | temp-fix-api-timeout-fallback |
Rules:
- Use lowercase with hyphens as separators
- Keep keys under 50 characters
- Include enough description to understand purpose without looking up documentation
- Never use version numbers in flag keys (
checkout-v2will becomecheckout-v7and nobody will know what the versions mean)
6. Escalation procedures
When a flag reaches its expiration date without being removed, the following escalation process activates automatically.
Week 1 past expiration: Warning
- Automated notification sent to the flag owner via [Slack / email / ticketing system]
- Message: "Flag
{flag-key}expired on {date}. Please remove it within 7 days or request a renewal." - A cleanup ticket is auto-created and assigned to the flag owner
Week 2 past expiration: Escalation
- Notification sent to the flag owner AND their engineering manager
- Message: "Flag
{flag-key}is 14 days past expiration. This flag is blocking codebase health metrics." - The cleanup ticket is marked as high priority
Week 3 past expiration: Team escalation
- The flag is added to the team's sprint planning as a mandatory item
- The engineering manager is responsible for ensuring it is scheduled in the current or next sprint
- Flag appears in the team's health dashboard as a critical issue
Week 4+ past expiration: Policy owner intervention
- The policy owner schedules a 15-minute review with the flag owner and their manager
- The outcome is one of: immediate removal, approved renewal with justification, or reclassification to Ops/Kill Switch with documentation
Customization notes:
- Adjust timelines based on your sprint cadence. Two-week sprints may need faster escalation.
- The goal is not to punish -- it is to ensure flags do not get forgotten. Keep the tone professional and helpful in all automated messages.
- Some teams prefer Slack channel notifications for Week 1 and direct messages for Week 2+. Choose what fits your communication culture.
7. Renewal process
Flag owners may request a renewal if they need additional time. Renewals are not automatic and require justification.
To request a renewal:
- Update the flag's description in the management platform with the reason for renewal
- Post in [#feature-flags Slack channel / team standup] with the flag key and justification
- Get approval from [engineering manager / tech lead / policy owner]
- Update the expiration date in the management platform
Valid renewal reasons:
- Feature rollout is in progress but not yet at 100%
- Experiment is still collecting data and has not reached statistical significance
- Migration has dependencies that are not yet complete
- External blocker prevents removal (document the blocker)
Invalid renewal reasons:
- "We haven't gotten to it yet" (this is what the escalation process addresses)
- "It's not hurting anything" (stale flags always have a cost, even if not immediately visible)
- "We might need it again" (if you need it again, you can re-create it)
8. Ownership transfer
When a flag owner leaves the team, changes roles, or goes on extended leave, ownership must be transferred.
Transfer process:
- The departing flag owner identifies all flags they own (search the management platform by owner)
- Each flag is reassigned to a current team member who understands the flag's purpose
- The new owner reviews the flag's expiration date and removal plan
- The management platform is updated with the new owner
- If no suitable owner is found, the engineering manager assumes temporary ownership
During team reorganizations, the engineering manager is responsible for ensuring all flags have valid owners. Flags without owners after a reorg are escalated to Week 3 of the escalation process immediately.
9. Ops/Kill Switch exceptions
Ops and Kill Switch flags are exempt from expiration because they serve an ongoing operational purpose. However, they are subject to additional requirements:
| Requirement | Details |
|---|---|
| Annual review | Every Ops/Kill Switch flag must be reviewed annually to confirm it is still needed |
| Documentation | Must include runbook documentation explaining when and how to use the switch |
| Testing | Must be tested quarterly to verify it functions correctly in both ON and OFF states |
| Ownership | Must have a primary and secondary owner |
| Alerting | State changes must trigger an alert to the on-call team |
If an annual review determines that a Kill Switch is no longer needed, it follows the standard removal process.
10. Enforcement mechanisms
This policy is enforced through a combination of automated checks and team processes.
Automated enforcement:
| Mechanism | Implementation | What It Catches |
|---|---|---|
| CI/CD check | Fail build if new flag lacks required metadata | Missing metadata at creation |
| Scheduled scan | Weekly scan of management platform for expired flags | Flags past expiration |
| PR comment | Automated comment on PRs that add new flags | Ensure developer awareness |
| Dashboard | Team health dashboard showing flag metrics | Visibility into overall health |
Process enforcement:
| Mechanism | Cadence | Who |
|---|---|---|
| Sprint planning review | Every sprint | Scrum master / tech lead |
| Flag health review | Monthly | Engineering manager |
| Policy review | Quarterly | Policy owner + team leads |
| Onboarding module | New hire first week | Hiring manager |
Customizing the policy for your team
The template above is intentionally detailed. Most teams will not need every section on day one. Here is how to adapt it based on your team size and maturity.
For small teams (1-10 engineers)
Start with sections 3, 5, and 6 only. Small teams can enforce naming conventions and expiration dates through code review without automated tooling. The escalation process can be as simple as a Slack reminder.
Simplified policy for small teams:
- Every flag must have a category prefix in its name
- Every flag must have an owner (default: the creator)
- Release flags expire in 90 days, experiment flags in 60 days
- A weekly 5-minute check during standup: "Does anyone have flags expiring this week?"
For mid-size teams (10-50 engineers)
Implement sections 3-8. At this size, ownership transfer becomes critical because team composition changes frequently. Automated expiration notifications save significant time.
Key additions over small team policy:
- Formal metadata requirements at creation
- Automated expiration notifications (Slack bot or platform integration)
- Ownership transfer process
- Monthly flag health review by engineering manager
For large teams (50+ engineers)
Implement the full policy. At this scale, automated enforcement is not optional -- manual processes cannot keep up with the volume of flags created and expired. Invest in CI/CD integration and a team health dashboard.
Key additions over mid-size team policy:
- CI/CD checks that block PRs with non-compliant flags
- Automated cleanup ticket creation
- Cross-team flag ownership registry
- Quarterly policy review with data-driven adjustments
Getting buy-in for the policy
A policy that nobody follows is worse than no policy at all. Getting buy-in requires addressing different concerns for different stakeholders.
Engineers: "This will slow us down"
Address this directly with data:
| Concern | Response |
|---|---|
| "Adding metadata takes time" | 2 minutes at creation saves 2-4 hours of debugging stale flags later |
| "Expiration deadlines add pressure" | The escalation process has a 4-week grace period -- this is not about punishment |
| "Kill switches should never expire" | They don't -- the Ops/Kill Switch category is explicitly exempt |
| "What if I need the flag longer?" | Renewals are straightforward and always approved with valid justification |
Engineering managers: "We have higher priorities"
Frame the policy in terms of metrics they care about:
- Developer velocity: Teams with flag policies ship faster because they spend less time navigating flag complexity
- Incident reduction: Flag-related incidents drop significantly when stale flags are systematically removed
- Onboarding time: New hires ramp up noticeably faster in codebases with fewer stale flags
- Technical debt ratio: Flag policy compliance is a leading indicator of overall codebase health
Product managers: "Will this affect feature rollouts?"
Reassure them that the policy supports feature development, not hinders it:
- Release flags get 90 days, with a 90-day renewal option (180 days total)
- Experiments get 60 days, with a 30-day renewal (90 days total)
- No flag will be forcibly removed -- the escalation process ensures discussion before action
- Kill switches for production stability are exempt from expiration
Enforcement strategies that actually work
The policy is only as good as its enforcement. Here are strategies that work in practice, ranked by effectiveness.
Strategy 1: Automated expiration alerts (high effectiveness, low effort)
Set up a scheduled job that checks your flag management platform for expired flags and sends notifications. This can be as simple as a cron job or as sophisticated as a platform integration.
# Example: Simple bash script to check for expired flags
# Run as a weekly cron job
#!/bin/bash
TODAY=$(date +%s)
# Fetch flags from your platform's API
# This is pseudocode -- adapt to your platform's API
curl -s "https://your-platform.com/api/flags" | \
jq -r '.items[] | select(.expiration_date != null) |
select((.expiration_date | strptime("%Y-%m-%d") | mktime) < now) |
"\(.key)\t\(.owner)\t\(.expiration_date)"' | \
while IFS=$'\t' read -r key owner expiry; do
echo "EXPIRED: $key (owner: $owner, expired: $expiry)"
# Send Slack notification, create ticket, etc.
done
Strategy 2: PR-level enforcement (high effectiveness, medium effort)
Add a CI check that validates new flags against the policy. This catches non-compliance at creation time, before the flag enters the codebase.
# Example: GitHub Actions check for flag compliance
# Runs on PRs that add new flag references
name: Flag Policy Check
on: [pull_request]
jobs:
flag-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check for new flag references
run: |
# Diff against base branch to find new flag additions
NEW_FLAGS=$(git diff origin/main...HEAD | grep "^+" | grep -oP '"(release-|exp-|ops-|perm-|migration-|temp-)[a-z0-9-]+"' | sort -u)
if [ -n "$NEW_FLAGS" ]; then
echo "New flags detected. Verify compliance with flag policy."
echo "$NEW_FLAGS"
# Additional checks: naming convention, metadata, etc.
fi
Strategy 3: Team dashboard (medium effectiveness, low effort)
Create a visible dashboard showing flag health metrics. Public visibility creates social accountability without requiring formal enforcement.
Dashboard metrics to display:
| Metric | Target | Red Threshold |
|---|---|---|
| Total active flags | Trending down or stable | Increasing for 3+ months |
| Stale flag percentage (90+ days) | < 20% | > 40% |
| Average flag age | < 60 days | > 120 days |
| Flags past expiration | 0 | > 5 |
| Flags without owners | 0 | > 3 |
| Renewal rate | < 25% | > 50% |
Strategy 4: Gamification (medium effectiveness, medium effort)
Some teams have success turning flag cleanup into a positive competition:
- Cleanup leaderboard: Track who removes the most flags per sprint
- Flag hygiene score: Each team gets a score based on compliance metrics
- "Flag Hunter" recognition: Monthly shoutout for the engineer who removes the most stale flags
- Cleanup sprints: Dedicated half-day events where the team focuses exclusively on flag removal
This works best as a supplement to automated enforcement, not a replacement.
Measuring policy compliance
A policy without measurement is a suggestion. Track these metrics to know whether your policy is working.
Leading indicators (predict future problems)
| Metric | What It Tells You | Measurement Frequency |
|---|---|---|
| New flags created per sprint | Rate of flag accumulation | Every sprint |
| Percentage of new flags with complete metadata | Policy adoption at creation time | Every sprint |
| Flags approaching expiration (within 14 days) | Upcoming cleanup workload | Weekly |
| Average flag age at removal | How quickly flags are cleaned up | Monthly |
Lagging indicators (measure outcomes)
| Metric | What It Tells You | Measurement Frequency |
|---|---|---|
| Total active flag count | Overall flag debt | Monthly |
| Stale flag percentage | Effectiveness of expiration rules | Monthly |
| Flag-related incidents | Safety impact of the policy | Quarterly |
| Developer satisfaction with flag management | Cultural impact | Quarterly (survey) |
The monthly compliance report
Generate a monthly report for your engineering manager with these sections:
- Flag inventory summary: Total flags, breakdown by category, net change from last month
- Expiration compliance: Flags removed on time vs. flags that entered escalation
- Top offenders: Flags with the longest overdue expiration (anonymized if your culture requires it)
- Renewal analysis: How many renewals were requested, approved, and denied
- Recommendations: Policy adjustments based on the data
Tools like FlagShark can generate this data automatically by tracking flag additions and removals across your pull requests, giving you a real-time view of flag lifecycle without manual auditing.
Handling policy exceptions gracefully
No policy survives contact with reality unchanged. You will encounter legitimate exceptions. Handle them with clear processes rather than ad-hoc decisions.
Exception 1: Long-lived permission flags
Some flags control access for specific user segments and need to exist for the lifetime of the feature. These should be reclassified as Permission flags with 180-day renewal cycles, not granted blanket exemptions.
Exception 2: Regulatory or compliance flags
Flags that control behavior required by regulation (GDPR consent flows, regional feature restrictions) may need to persist indefinitely. Reclassify these as Ops/Kill Switch with documentation explaining the regulatory requirement.
Exception 3: Flags shared across teams
When a flag is used by multiple teams, removal requires coordination. Assign a single owner (usually the team that created the flag) and extend the expiration by 30 days to allow cross-team coordination.
Exception 4: "We might need this flag back"
This is the most common objection and the hardest to handle. The answer is clear: if you need the flag again, re-create it. Re-creating a flag takes minutes. Maintaining a stale flag for months "just in case" costs hours of accumulated complexity.
Document the flag's configuration before removal. Store it in a "flag archive" document or wiki page. If the flag is ever needed again, the archive provides the blueprint for re-creation.
Rolling out the policy
Do not try to enforce the full policy on day one. A phased rollout gives your team time to adjust and gives you data to refine the policy.
Phase 1: Awareness (Week 1-2)
- Share the policy document with the team
- Walk through it in a team meeting (15 minutes is sufficient)
- Answer questions and incorporate feedback
- Announce the effective date (at least 2 weeks out)
Phase 2: Soft enforcement (Week 3-6)
- Enable automated expiration alerts (warnings only, no consequences)
- Start tracking compliance metrics
- Address questions and edge cases as they arise
- Adjust lifespans or escalation timelines based on early data
Phase 3: Full enforcement (Week 7+)
- Enable all escalation procedures
- Activate CI/CD checks (if implemented)
- Begin monthly compliance reporting
- Schedule the first quarterly policy review
The quarterly policy review
Every quarter, review the policy with these questions:
- Are the expiration timelines realistic? (If 50%+ of flags need renewals, the timelines are too short.)
- Is the escalation process working? (If flags regularly reach Week 4, the earlier stages are not effective.)
- Are there new flag categories needed? (New use cases may require new categories.)
- Is the team satisfied with the policy? (Survey or standup discussion.)
- What is the stale flag percentage? (This is the ultimate success metric.)
Feature flags are powerful tools that become liabilities without discipline. A formal expiration policy is not bureaucracy -- it is the mechanism that lets your team use flags aggressively for deployment safety and experimentation while preventing the gradual accumulation that turns codebases into unmaintainable flag graveyards.
Copy the template. Customize it for your team. Roll it out this quarter. The flags you prevent from going stale will never need a cleanup sprint, an incident postmortem, or a late-night debugging session. That is the return on 30 minutes of policy writing.