Automated Cleanup PRs
Let FlagShark automatically create pull requests to remove stale feature flags from your codebase.
One of FlagShark's most powerful features is automated cleanup PR generation. When a feature flag has served its purpose, FlagShark can create a pull request that removes all traces of the flag from your code.
Piranha Technology
FlagShark uses Piranha, an open-source code refactoring tool developed by Uber, to intelligently remove feature flag code. For the full pipeline — scan, stale decision, provider sync, transform, and PR regeneration — see How the Engine Works.
Unlike simple find-and-replace, Piranha understands your code's abstract syntax tree (AST) and can:
- Remove entire conditional branches — Not just the flag check, but the unreachable code path
- Simplify nested conditions — Clean up complex if/else trees
- Remove dead functions — Delete helper functions only used by the removed path
- Clean up imports — Remove imports that are no longer needed
- Preserve formatting — Maintain your code style and indentation
How Piranha Works
- 1Source Code
- 2Parse ASTtree-sitter
- 3Find Flag Usages
- 4Determine Pathkeep enabled
- 5Apply Rules
- 6Clean Code
Example: Before and After
Before Piranha cleanup:
import { useFlags } from 'launchdarkly-react-client-sdk';
import { NewCheckout } from './NewCheckout';
import { LegacyCheckout } from './LegacyCheckout';
function CheckoutPage() {
const { enableNewCheckout } = useFlags();
if (enableNewCheckout) {
return <NewCheckout />;
}
return <LegacyCheckout />;
}
After Piranha cleanup (keeping enabled path):
import { NewCheckout } from './NewCheckout';
function CheckoutPage() {
return <NewCheckout />;
}
Notice how Piranha:
- Removed the
useFlagsimport (no longer needed) - Removed the
LegacyCheckoutimport (no longer used) - Removed the
enableNewCheckoutvariable - Removed the entire conditional
- Kept only the enabled path
How It Works
FlagShark analyzes your flag usage and can generate a PR that:
// Before cleanup
- import { useFlags } from 'launchdarkly-react-client-sdk';
function CheckoutButton() {
- const { enableNewCheckout } = useFlags();
-
- if (enableNewCheckout) {
- return <NewCheckoutButton />;
- }
- return <LegacyCheckoutButton />;
+ return <NewCheckoutButton />;
}
Triggering Cleanup
From the Dashboard
The primary way to create a cleanup PR is directly from the FlagShark dashboard:

From Stale Flag Alerts
When FlagShark detects a stale flag:
Automatic Cleanup via LaunchDarkly Sync
If your workspace has Auto-removal enabled (Settings → Workspace → Auto-removal) and you use LaunchDarkly, FlagShark syncs flags marked readyForCodeRemoval in LD and automatically queues them for cleanup PR creation. Auto-removal is disabled by default; enable it only once you trust the pipeline on your codebase.
Draft vs. Ready-to-Merge
By default, cleanup PRs open as non-draft (ready for review). You can change this workspace-wide in Settings → Workspace → "Open cleanup PRs as draft". When that setting is enabled, all new cleanup PRs open as draft so you can review the diff before CI runs or teammates are notified.
The per-PR toggle in the Create Cleanup PR modal overrides the workspace default for a single PR.
Cleanup Options
Keep Enabled Path
The most common option - keep the code that runs when the flag is true:
// Original
if (flags.enableNewFeature) {
return <NewFeature />; // ← Keep this
}
return <OldFeature />; // ← Remove this
// After cleanup
return <NewFeature />;
Keep Disabled Path
Occasionally needed if you're rolling back a feature:
// Original
if (flags.enableNewFeature) {
return <NewFeature />; // ← Remove this
}
return <OldFeature />; // ← Keep this
// After cleanup
return <OldFeature />;
Remove Both Paths
For flags that only controlled logging or metrics:
// Original
if (flags.enableDetailedLogging) {
logger.debug('Detailed info:', data);
}
doActualWork();
// After cleanup (remove the entire if block)
doActualWork();
What Gets Cleaned
Flag Evaluations
All variations of flag checks are removed:
// These are all cleaned up:
const enabled = ldClient.variation('flag-key', user, false);
const { flagKey } = useFlags();
if (featureFlags.isEnabled('flag-key')) { }
Conditional Logic
The wrapping conditionals are simplified:
// Ternary operators
const Component = enabled ? <New /> : <Old />;
// Becomes:
const Component = <New />;
// If/else blocks
if (enabled) {
doNew();
} else {
doOld();
}
// Becomes:
doNew();
// Switch statements
switch (variant) {
case 'a': return <VariantA />;
case 'b': return <VariantB />;
}
// Becomes:
return <VariantA />; // (if 'a' was the winner)
Dead Code
Code that becomes unreachable is removed:
// Before: Function only used by disabled path
function legacyHelper() { /* ... */ }
// After: Function is removed if unused
Imports
Unused imports are cleaned up:
// Before
import { useFlags } from 'launchdarkly-react-client-sdk';
import { NewFeature } from './NewFeature';
import { OldFeature } from './OldFeature';
// After (keeping enabled path)
import { NewFeature } from './NewFeature';
// useFlags removed - no longer used
// OldFeature removed - no longer used
Preview Before Creating
Always preview changes before creating the PR:

The preview shows:
- All files that will be modified
- Line-by-line diff of changes
- Any potential issues or warnings
Handling Complex Cases
Multiple Files
If a flag is used across many files, FlagShark handles them all:
Cleanup PR will modify 7 files:
├── src/components/Feature.tsx
├── src/components/Feature.test.tsx
├── src/hooks/useFeature.ts
├── src/api/feature.ts
├── src/utils/featureHelpers.ts
├── src/pages/FeaturePage.tsx
└── src/pages/FeaturePage.test.tsx
Nested Conditions
FlagShark handles nested flag checks:
// Before
if (flags.enableFeature) {
if (flags.enableSubFeature) {
return <SubFeature />;
}
return <Feature />;
}
return <Legacy />;
// After (cleaning up enableFeature, keeping enabled)
if (flags.enableSubFeature) {
return <SubFeature />;
}
return <Feature />;
Flag Dependencies
FlagShark warns about dependent flags:
Review Process
Automated Checks
Cleanup PRs include:
- Syntax validation
- Import verification
- Test file updates
Manual Review
Always review cleanup PRs:
Best Practices
Clean Up Promptly
Don't let flags linger at 100%:
| Flag Age at 100% | Action |
|---|---|
| < 2 weeks | Monitor |
| 2-4 weeks | Plan cleanup |
| > 4 weeks | Create cleanup PR |
Test Both Paths First
Before cleanup, ensure you've tested:
- The path you're keeping works correctly
- Removing the other path won't break anything
- All tests pass with the flag fully enabled
Coordinate with Team
Let your team know:
📣 Cleanup planned for `enable-new-checkout`
The flag has been at 100% for 3 weeks with no issues.
Cleanup PR will be created tomorrow.
Please review PR #301 when ready.
Update Documentation
After cleanup, update any docs that mentioned the flag:
- README files
- Runbooks
- Architecture docs
- API documentation
Troubleshooting
Cleanup PR Has Conflicts
If the PR has merge conflicts:
Tests Fail After Cleanup
If tests fail:
Complex Code Pattern Not Handled
FlagShark may not handle every pattern:
Disabling Auto-Cleanup
If you want to manage cleanup manually:
Related Documentation
- Flag Lifecycle - When to clean up flags
- Activity Feed - Tracking cleanup activity
- Common Issues - Cleanup troubleshooting