Feature Flags Aren't a Strategy. They're an Excuse.
Feature flags have great marketing. Ship continuously. Test in production. Roll back instantly. Decouple deploy from release. The pitch is clean, the tooling is polished, and the case studies make it look like a free lunch.
And in the right hands, flags really do deliver on that promise. Canary releases, A/B tests, gradual rollouts — these are legitimate techniques that real engineering teams use to reduce risk in meaningful ways.
But watch how most teams actually use feature flags in practice and a different picture emerges. The flags multiply. They accumulate. Nobody's quite sure which ones are still active or what they're guarding. The codebase starts to look like a choose-your-own-adventure novel written by a committee. And the feature that was supposed to ship "when it's ready" has been sitting behind a flag for six months while three engineers debate whether it's ready.
That's not continuous delivery. That's deployment anxiety with a dashboard.
What Flags Are Actually Good At
Let's be fair before we get critical. Feature flags solve real problems when they're used for the right reasons.
Gradual rollouts are the strongest use case. You've built something significant, you're not sure how it'll behave under production load, and you want to expose it to 1% of traffic before you expose it to everyone. A flag lets you do that without maintaining a separate deployment. This is genuinely useful.
Operational kill switches are another solid use case. A flag that lets you disable a non-critical feature instantly if it's causing problems — without a deploy — is a reasonable safeguard, especially for integrations with third-party services that might behave unexpectedly.
A/B testing and experimentation are the textbook case. If you're running a controlled experiment where you need to measure user behavior across two variations, flags are the right tool. This is what the Netflixes and Facebooks of the world use them for at scale.
Trunk-based development is where flags enable a real workflow change. If your team commits directly to main and uses flags to hide incomplete features from production users, that's a legitimate technique for keeping long-lived branches out of your repo.
Notice what these have in common: they're all intentional design decisions, not reactions to uncertainty.
The Patterns That Signal Trouble
Here's how flags go wrong, and most of these will probably look familiar:
The indefinite flag. A feature ships behind a flag and the flag never gets removed because removing it requires touching the code again and nobody wants to do that right now. Six months later the flag is load-bearing in ways nobody fully understands. This is just technical debt with extra steps.
The confidence substitute. "We'll put it behind a flag" becomes the answer to every concern about a feature's readiness. But a flag doesn't make incomplete work complete. If the feature isn't ready, the flag is delaying a conversation that needs to happen, not solving a technical problem.
The proliferating kill switch. Every new integration, every new third-party dependency, every new endpoint gets a flag. The intention is good — operational resilience — but the result is a configuration space that's impossible to reason about. What's the current state of production? Nobody knows without checking the flag dashboard.
The flag-as-branch. Teams use flags to avoid merge conflicts instead of improving their branching strategy. The flag grows to encompass weeks of work. Now you have a long-lived branch that's technically in main but functionally isolated. You've solved nothing.
Structuring Flags for Maintainability
If you're going to use flags — and you should, selectively — the operational discipline around them matters as much as the tooling.
Every flag needs an expiration intent. When you create a flag, document when and how it gets removed. Is it a temporary rollout flag that goes away after full rollout? Is it a permanent experiment flag? Is it a kill switch that stays forever? These have different maintenance implications and should be tracked differently.
Flags should be countable. If you can't tell a new engineer how many active feature flags are in production right now, you have a governance problem. Keep the count low enough that it's knowable. LaunchDarkly, Flagsmith, Unleash — whatever you're using, your flag inventory should be a first-class artifact that someone owns.
Dead flags are technical debt. The code paths guarded by a flag that's been at 100% rollout for three months are dead code waiting to be cleaned up. Schedule the cleanup. If the cleanup never happens, the flag was never really temporary.
Don't flag at the infrastructure layer if you can flag at the application layer. Flags that require infrastructure changes to toggle are expensive to operate. Keep them in application code where they're fast to change and easy to audit.
The Hard Truth About When to Just Commit
Sometimes the right answer is to ship the feature or don't ship the feature. Not to ship it behind a flag while you figure it out.
If a feature isn't ready for users, a flag doesn't make it ready — it just hides it. The work still needs to happen. The decision still needs to be made. Flags can give you time to validate, but they can't substitute for the validation itself.
And if you're rolling back more often than you're rolling forward, flags aren't your problem. Your problem is that you're shipping things that aren't ready, and the flag is a pressure valve that's letting you avoid fixing that.
The teams that use feature flags well are the ones who treat them as a deployment tool, not a decision-making tool. The flag controls when users see something. The team controls whether the thing is worth seeing.
Build the thing. Test it properly. Make the call. Then use a flag to control the rollout if the scale or risk warrants it. That's the sequence.
Flags are infrastructure. Like all infrastructure, they serve the work — they don't replace it.