Feature Flags Guardrails
Enforce feature flag hygiene across your codebase. Ensure feature flags don't become permanent fixtures by detecting flags older than a configurable threshold, helping teams maintain clean, manageable codebases.
feature-flags to your lunar-config.yml:uses: github://earthly/lunar-lib/policies/feature-flags@v1.0.0
Included Guardrails
This policy includes 1 guardrail that enforce standards for your testing and quality.
feature-flags-age
Detects feature flags that have existed longer than a configurable threshold. Feature flags should be temporary and removed once a feature is fully rolled out. Stale flags add complexity and technical debt to the codebase.
How Guardrails Fit into Lunar
Lunar guardrails define your engineering standards as code. They evaluate data collected by integrations and produce pass/fail checks with actionable feedback.
Policies support gradual enforcement—from silent scoring to blocking PRs or deployments—letting you roll out standards at your own pace without disrupting existing workflows.
Learn How Lunar Works →Required Integrations
This policy evaluates data gathered by one or more of the following integration(s).
Make sure to enable them in your lunar-config.yml.
Configuration
Configure this policy in your lunar-config.yml.
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
max_days
|
Optional |
90
|
Maximum number of days a feature flag should exist before failing the check |
Documentation
View on GitHubFeature Flags Guardrails
Enforces feature flag hygiene by detecting flags that have exceeded their expected lifespan.
Overview
This plugin helps teams manage feature flag lifecycle by identifying flags that have existed longer than a configurable threshold. Feature flags should be temporary—once a feature is fully rolled out and stable, the flag should be removed. Stale feature flags add unnecessary complexity, increase cognitive load, and contribute to technical debt.
Policies
This plugin provides the following guardrails (use include to select a subset):
| Policy | Description | Failure Meaning |
|---|---|---|
feature-flags-age |
Detects feature flags older than threshold | Feature flag has existed too long and should be cleaned up |
Required Data
This plugin reads from the following Component JSON paths:
| Path | Type | Provided By |
|---|---|---|
.code_patterns.feature_flags |
object | ast-grep collector |
.code_patterns.feature_flags.flags |
array | ast-grep collector |
.code_patterns.feature_flags.flags[].key |
string | ast-grep collector |
.code_patterns.feature_flags.flags[].file |
string | ast-grep collector |
.code_patterns.feature_flags.flags[].line |
number | ast-grep collector |
.code_patterns.feature_flags.flags[].created_at |
number (Unix timestamp) | ast-grep collector |
Note: Ensure the corresponding collector is configured before enabling this policy. The collector must provide feature flag metadata including creation timestamps (e.g., via git blame).
Installation
Add to your lunar-config.yml:
policies:
- uses: github://earthly/lunar-lib/policies/feature-flags@v1.0.0
on: ["domain:your-domain"] # Or use tags like [backend, frontend]
enforcement: report-pr # Options: draft, score, report-pr, block-pr, block-release, block-pr-and-release
# include: [feature-flags-age] # Only run specific checks (omit to run all)
# with:
# max_days: "60" # Override default 90-day threshold
Examples
Passing Example
Feature flags within the allowed age threshold:
{
"code_patterns": {
"feature_flags": {
"library": "launchdarkly",
"flags": [
{
"key": "new_checkout_flow",
"file": "src/checkout/index.ts",
"line": 42,
"created_at": 1735689600
},
{
"key": "dark_mode_beta",
"file": "src/theme/provider.tsx",
"line": 15,
"created_at": 1733097600
}
],
"count": 2
}
}
}
All flags are less than 90 days old (default threshold), so the check passes.
Failing Example
Feature flag that has exceeded the age threshold:
{
"code_patterns": {
"feature_flags": {
"library": "launchdarkly",
"flags": [
{
"key": "legacy_payment_system",
"file": "src/payments/processor.ts",
"line": 128,
"created_at": 1704067200
}
],
"count": 1
}
}
}
Failure message: "Feature flag 'legacy_payment_system' at src/payments/processor.ts:128 is 395 days old (max: 90 days)"
Remediation
When this policy fails, you should:
- Review the feature flag: Determine if the feature is fully rolled out and stable
- Remove the flag: If the feature is complete, remove the flag and associated conditional logic
- Extend if needed: If the flag is still necessary, consider whether the threshold should be adjusted via configuration, or document why the flag needs to remain
- Clean up dead code: Remove any code paths that were only used during the rollout
Example cleanup
Before (with flag):
if (featureFlags.isEnabled('new_checkout_flow')) {
return <NewCheckout />;
} else {
return <LegacyCheckout />;
}
After (flag removed):
return <NewCheckout />;
Adjusting the threshold
If your team needs a different default threshold, configure it in lunar-config.yml:
policies:
- uses: github://earthly/lunar-lib/policies/feature-flags@v1.0.0
on: ["all"]
enforcement: block-pr
with:
max_days: "120" # Allow flags up to 120 days old
Open Source
This policy is open source and available on GitHub. Contribute improvements, report issues, or fork it for your own use.
Common Use Cases
Explore how individual guardrails work with specific integrations.
Ready to Automate Your Standards?
See how Lunar can turn your engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 100+ built-in guardrails.