staleflags.
your feature flags served their purpose. staleflags finds the dead code they left behind.
You already lint your code. staleflags audits your feature flags. It scans your .env files, compares values across environments, and tells you which flags are dead, aging, or still doing their job.
npx staleflagsrun in any project with .env files. zero config.
staleflags v0.1.0 — scanning for feature flags
Found 5 flags across 4 files · 17 lines of dead code
⚰️ DEAD FLAGS (same value in every environment)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ENABLE_NEW_CHECKOUT
Value: true in .env.development, .env.staging, .env.production
Introduced: 14 months ago
Value unchanged: 14 months ago
Dead code: src/checkout.ts:4-15 (else branch, 12 lines)
Total: 12 lines of dead code
USE_V2_PARSER
Value: const USE_V2_PARSER = true (hardcoded in src/config.ts:1)
Dead code: src/config.ts:7-9 (else branch, 3 lines)
Total: 3 lines of dead code
✅ ACTIVE FLAGS (doing their job)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FEATURE_DARK_MODE true in .env.development · false in .env.production
👻 PHANTOM FLAGS (read in code, never defined)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FF_SECRET_FEATURE
Referenced in code but never defined in any environment file
SUMMARY: 5 flags · 2 dead · 1 active
DEAD CODE: 17 lines across 2 files (removable today)How it works
One command. Every flag classified.
Scans your codebase
Finds env var flags, hardcoded constants, and config flags across JS/TS/Python/Go/Ruby.
Compares across environments
Checks if each flag has the same value in every .env file. Same everywhere = dead.
Quantifies the damage
Finds the if/else blocks controlled by dead flags, counts the dead branch lines, gives you the total.
What it checks
7 checks. Zero config.
Cross-environment consistency
the novel oneIs this flag the same value in dev, staging, and prod? Same everywhere = dead. The other branch is unreachable.
Hardcoded constants
const ENABLE_X = true unchanged for months. Not a flag anymore — it's dead code with a name.
Flag age
How long since this flag was introduced (git history). Older flags are more likely to be done.
Value age
How long since the value last changed. A flag that hasn't been toggled in 6 months is suspicious.
Dead code quantification
Exact line ranges of unreachable code behind dead flags. Not just "this flag is stale" — how many lines can you delete.
Phantom detection
Code reads a flag that no config defines. Evaluates to undefined everywhere.
Orphan detection
Config defines a flag that no code reads. Leftover config clutter.
Why not X?
Nothing else does this.
Removes stale flag code
Only works with specific SDKs. YOU tell it which flags are stale.
Tracks flag lifecycle
Tied to LaunchDarkly/Unleash/Split. SaaS.
Finds LaunchDarkly SDK calls
Only LaunchDarkly. Not homegrown flags.
Lints code quality
Doesn't know your .env files or flag values.
Cross-refs env vars
Doesn't quantify dead code. Doesn't check cross-env consistency.
Classifications
What each status means.
Dead
Same value in every environment. The other branch is unreachable. Remove the flag and the dead code.
Aging
Different in at least one env, but code behind it hasn't changed in months. Probably done.
Phantom
Code reads the flag but no config defines it. Evaluates to undefined everywhere.
Orphan
Config defines it but no code reads it. Leftover config clutter.
Active
Different values across environments, recently toggled. Doing its job.
Get started
Pick how you want to use it.
Terminal
Run once in any project. Nothing to install, nothing to configure.
npx staleflagsGitHub Action
Add one file. Runs on every PR and weekly on schedule. Uses fetch-depth: 0 for git history.
name: staleflags
on:
pull_request:
schedule:
- cron: '0 9 * * 1'
jobs:
flags:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: Ijtihed/staleflags@v1
with:
fail-on: deadAI Prompt
Paste into Cursor, Claude Code, or Copilot. AI does the rest.
Add staleflags to this project. Create .github/workflows/staleflags.yml that runs on every PR using Ijtihed/staleflags@v1. Then run npx staleflags locally and clean up any dead flags it finds.