What Are Feature Flags?
Feature flags are a software development technique that lets you enable or disable a feature at runtime without changing code or redeploying your application. They decouple deployment (shipping code to production) from release (making a feature visible to users).
At their simplest, feature flags are conditionals:
if (featureFlags.isEnabled('new-checkout')) {
renderNewCheckout();
} else {
renderLegacyCheckout();
}
The key difference from a hardcoded if/else is that the condition is evaluated against rules managed outside the codebase — in a feature flag service, a config file, or an environment variable. This means you can change the behavior of your application without touching code.
Feature flag vs feature toggle
There is no meaningful difference. “Feature flag” and “feature toggle” refer to the same concept. “Feature flag” is the more common term in the industry today. Other synonyms you may encounter:
- Feature switch
- Feature flipper
- Release toggle
- Experiment flag
Throughout this guide, we use “feature flag” consistently.
Why feature flags matter
Without feature flags, every deploy is a release. You push code, and users immediately see the change. If something breaks, you roll back the entire deploy. If a feature is not ready, you keep it on a branch — sometimes for weeks — creating merge conflicts and integration risk.
Feature flags solve this by separating the two concerns:
| Without flags | With flags |
|---|---|
| Deploy = Release | Deploy and release are independent |
| Long-lived feature branches | Merge to main behind a flag |
| Rollback = redeploy | Rollback = toggle a flag |
| All users see the change at once | Roll out to 1%, then 10%, then 100% |
| A/B test requires separate infrastructure | A/B test with variant flags |
| Kill a broken feature = emergency deploy | Kill a broken feature = toggle off in seconds |
Types of feature flags
Not all flags serve the same purpose. Understanding the types helps you decide how to manage them.
1. Release flags (temporary)
The most common type. You wrap a new feature behind a flag, deploy to production, and gradually increase the rollout percentage. Once the feature is stable at 100%, you remove the flag from the code.
Lifecycle: 1-4 weeks. Remove after full rollout.
const showNewSearch = flagify.isEnabled('new-search-engine');
const results = showNewSearch
? await elasticSearch(query)
: await legacySearch(query);
2. Experiment flags (A/B tests)
Used to test multiple variants against each other. Each user is assigned a variant (typically via consistent hashing) and measured against a metric — conversion rate, engagement, revenue.
Lifecycle: Duration of the experiment (usually 2-6 weeks). Remove after the winner is chosen.
const variant = flagify.getVariant('pricing-page', 'control');
// 'control' | 'variant-a' | 'variant-b'
renderPricingPage(variant);
3. Operational flags (kill switches)
Safety mechanisms that let you disable a feature instantly if something goes wrong. Unlike release flags, kill switches stay in the codebase permanently. They are your circuit breakers.
Lifecycle: Permanent. Never remove.
if (!flagify.isEnabled('enable-payment-processing')) {
return showMaintenancePage();
}
processPayment(order);
4. Entitlement flags (permission gates)
Control access to features based on user attributes — plan, role, organization, geography. These are business rules expressed as flags.
Lifecycle: As long as the business rule exists.
// Using targeting rules: only users with plan === 'pro' see this
const canExportCSV = flagify.isEnabled('csv-export');
if (canExportCSV) {
showExportButton();
}
Summary
| Type | Purpose | Lifecycle | Example |
|---|---|---|---|
| Release | Ship new features gradually | 1-4 weeks | new-checkout-flow |
| Experiment | A/B test variants | 2-6 weeks | pricing-page-test |
| Operational | Kill switch / circuit breaker | Permanent | enable-payments |
| Entitlement | Gate by plan, role, attribute | Ongoing | pro-csv-export |
How feature flags work
The typical architecture has three parts:
- Flag configuration — a service (dashboard, API, or CLI) where you define flags, their states, targeting rules, and rollout percentages.
- SDK — a client library in your application that fetches flag values and evaluates them locally. Good SDKs cache the rules and evaluate without network calls.
- Real-time sync — when you change a flag, the new state propagates to all connected SDKs. This is typically done via Server-Sent Events (SSE) or webhooks.
Here is what the flow looks like in practice:
1. You create a flag in the dashboard or CLI
2. SDK connects to the service and downloads all flag rules
3. Your code calls isEnabled('flag-name')
4. SDK evaluates locally (no network call) → returns true/false
5. You toggle the flag via dashboard or CLI
6. Service pushes the update via SSE to all connected SDKs
7. Next evaluation returns the new value — in milliseconds
This is why good feature flag tools have sub-millisecond evaluation — the SDK never makes a network request per check. It reads from a local cache that is kept in sync.
When to use feature flags
Use flags for:
- Gradual rollouts — release to 1% of users, monitor, ramp to 100%
- A/B testing — compare variants with real user behavior
- Kill switches — disable risky features without a redeploy
- Trunk-based development — merge incomplete features to main behind a flag
- Entitlement gating — show features by plan, role, or attribute
- Infrastructure migrations — gradually shift traffic to a new backend
- Canary releases — test with internal users before public launch
- Holiday/event features — enable seasonal content at a specific time
Do NOT use flags for:
- Static configuration — values that change once a year belong in config files, not flags
- Secrets management — use a secrets manager (Vault, AWS Secrets Manager), not flags
- Database migrations — flags control runtime behavior, not schema changes
- Every single if/else — not every conditional needs to be a flag. Use flags for decisions you want to change without a deploy
Feature flag best practices
Name flags with intent
A flag name should tell you what it does without reading the code.
| Bad | Good |
|---|---|
flag-123 | new-checkout-flow |
test | search-v2-rollout |
feature_a | pro-plan-csv-export |
Use lowercase with hyphens (kebab-case). Include the feature or context. Avoid abbreviations that only one person understands.
Define a lifecycle for every flag
Every flag should have a planned end state. When you create a flag, decide: is this temporary (remove after rollout), permanent (kill switch), or ongoing (entitlement)? Temporary flags that are never cleaned up become technical debt.
Clean up aggressively
Set a rule: when a temporary flag reaches 100% rollout and has been stable for one sprint, remove it. Not “someday” — next sprint. Use your flag tool’s evaluation counts to identify flags that always return the same value. Those are candidates for cleanup.
Avoid nesting flags
If you have 3 nested boolean flags, that is 8 possible states. Use a single multivariate flag instead of nesting booleans:
// Bad: 8 possible states
if (isEnabled('new-checkout')) {
if (isEnabled('checkout-upsell')) {
if (isEnabled('upsell-v2')) {
// Which combination are we testing?
}
}
}
// Good: 1 flag, explicit variants
const experience = flagify.getVariant('checkout-experience');
renderCheckout(experience); // 'legacy' | 'new' | 'new-with-upsell'
Always provide a fallback value
The SDK should never be a single point of failure. If the flag service is unreachable, your application should still work with a sensible default:
const showBanner = flagify.isEnabled('promo-banner', false);
Separate concerns
Do not use a single flag for multiple purposes. If “new dashboard” also changes the API response format, those are two flags — one for UI, one for API. They may roll out at different speeds.
For a deeper dive, read our feature flag best practices guide.
Feature flags and technical debt
The biggest criticism of feature flags is that they create technical debt. This is true — if you don’t manage them. A codebase with 200 stale flags and nested conditionals is worse than one with no flags at all.
The solution is discipline:
- Track flag age. If a release flag is older than 30 days, it should be reviewed.
- Monitor evaluation patterns. If a flag returns
truefor 100% of users for 2+ weeks, it is ready to remove. - Automate reminders. Integrate flag expiration into your sprint workflow.
- Limit flag count. Set a team ceiling (e.g., 20 active flags). When you hit the limit, clean up before creating new ones.
Feature flags are infrastructure. Treat them with the same discipline as database migrations or CI pipelines.
How to implement feature flags
There are three approaches, from simplest to most robust:
1. Environment variables or config files
The simplest approach. Store flag values in .env or a JSON config file. Changing a flag requires a redeploy.
# .env
FEATURE_NEW_CHECKOUT=true
Pros: Zero dependencies. Works everywhere.
Cons: No targeting. No gradual rollout. Requires redeploy.
2. Build your own
Store flags in a database table. Build an admin UI. Evaluate flags per request.
Pros: Full control.
Cons: You have to build targeting, caching, real-time sync, audit logging, and SDKs yourself. What starts as a 50-line table becomes a multi-month project.
3. Use a feature flag platform
Dedicated platforms like Flagify, LaunchDarkly, or Unleash provide SDKs, dashboards, targeting rules, real-time sync, and audit logs out of the box.
Pros: Production-ready from day one. Targeting, rollouts, environments, and audit included.
Cons: External dependency. Cost (though most have free tiers).
For most teams, option 1 is fine for a single flag. But once you need targeting, environments, or more than a handful of flags, option 3 saves significant time.
Feature flags in practice: code examples
Node.js / TypeScript
import { Flagify } from '@flagify/node';
const flagify = new Flagify({
projectKey: 'my-project',
publicKey: process.env.FLAGIFY_PUBLIC_KEY,
options: { realtime: true },
});
await flagify.ready();
// Boolean flag
const isEnabled = flagify.isEnabled('new-checkout');
// Typed value flag
const maxRetries = flagify.getValue<number>('api-max-retries', 3);
// A/B variant
const variant = flagify.getVariant('pricing-page', 'control');
See the full Node.js SDK documentation.
React
import {
FlagifyProvider,
useFlag,
useVariant,
} from '@flagify/react';
function App() {
return (
<FlagifyProvider
projectKey="my-project"
publicKey="pk_dev_..."
>
<Checkout />
</FlagifyProvider>
);
}
function Checkout() {
const showNew = useFlag('new-checkout');
return showNew
? <NewCheckout />
: <LegacyCheckout />;
}
See the full React SDK documentation.
CLI
# Create a flag
flagify flags create new-checkout --type boolean
# Toggle in production
flagify flags toggle new-checkout --env production
# Check current state
flagify flags get new-checkout
See the full CLI reference.
Feature flags and CI/CD
Feature flags are the natural complement to continuous integration and continuous deployment. CI/CD gives you the ability to ship code to production frequently. Feature flags give you the ability to control what users actually see.
The combined workflow:
- Developer merges a feature to
mainbehind a flag (trunk-based development) - CI runs tests, builds, deploys to production
- Feature is deployed but not released — flag is off
- Product/engineering enables the flag for internal users (canary)
- Flag is ramped to 5% → 25% → 100% based on metrics
- Flag is removed from code in a follow-up PR
This eliminates the “deploy freeze” problem. You can deploy at any time because incomplete features are behind flags.
Feature flags and environments
In a well-designed system, flag state is per-environment. A flag might be enabled in development, disabled in staging, and enabled for 10% of users in production. Each environment has its own:
- Flag states (on/off per environment)
- Targeting rules (different rules per environment)
- API keys (scoped to prevent cross-contamination)
This prevents the common mistake of testing with production flags or accidentally toggling a staging flag in production. Read more about environment isolation.
Frequently asked questions
What is a feature flag?
A feature flag is a software development technique that lets you enable or disable a feature at runtime without changing code or redeploying. At its core, it is a conditional that wraps a code path. The condition is evaluated against a set of rules managed outside the codebase — typically in a feature flag service.
What is the difference between a feature flag and a feature toggle?
There is no meaningful difference. “Feature flag” and “feature toggle” refer to the same concept. “Feature flag” is the more common term today. Other synonyms include feature switch, feature flipper, and release toggle.
Do feature flags cause technical debt?
Feature flags can cause technical debt if not managed properly. Temporary flags that are never removed accumulate as dead code paths. The solution is to define a lifecycle for every flag: set an expiration, remove flags once they reach 100% rollout, and use tooling that tracks flag age and usage.
When should I use feature flags?
Use feature flags for gradual rollouts, A/B testing, kill switches for risky deployments, entitlement gating (showing features by plan or role), trunk-based development (merging incomplete features behind flags), and infrastructure migrations. Do not use them for static configuration or secrets management.
Are feature flags a security risk?
Feature flags themselves are not a security risk, but they require the same discipline as any access control system. Use scoped API keys (separate keys per environment), never expose server-side secret keys in client code, and audit flag changes. Do not use feature flags as a substitute for authentication or authorization.
How many feature flags is too many?
There is no universal number, but a useful rule of thumb: if more than half your flags are temporary and older than 30 days, you have a cleanup problem. Most teams operate well with 15-30 active flags. The issue is not the count — it is stale flags that nobody owns.
Ready to try feature flags? Flagify gives you feature flags with sub-millisecond evaluation, CLI-first workflow, and native AI tool integration. Set up your first flag in under five minutes.
Start for free — no credit card required. Or explore the quick start guide, React SDK, and Node.js SDK.