Feature flags are the infrastructure that makes continuous deployment safe. Without them, releasing a feature means deploying all the code for that feature in one shot, hoping it works. With them, the code ships whenever it's ready, the feature activates on a schedule you control, and you can turn it off in 30 seconds if something goes wrong. Here is the complete 2026 guide.
What changed in 2026
- OpenFeature 1.4 became the industry standard SDK — a vendor-neutral specification for feature flag evaluation means you can swap providers without rewriting application code.
- AI-driven flag targeting is now a feature of enterprise providers — LaunchDarkly and Statsig use model-inferred segments to automatically target flags at users likely to benefit.
- GitOps-driven flag management (flags defined in YAML, reviewed in PRs) is now a common pattern in engineering orgs that want auditability without a GUI dependency.
- Edge flag evaluation (at CDN/WAF) is production-stable — Cloudflare Workers, Vercel Edge Middleware, and Fastly Compute can evaluate flags before a request hits your origin.
Types of feature flags
| Type |
Lifespan |
Purpose |
Example |
| Release toggle |
Days–weeks |
Decouple deploy from release |
New checkout flow |
| Experiment / A/B |
Days–weeks |
Measure user behaviour |
Button colour test |
| Ops toggle / kill switch |
Indefinite |
Emergency disable |
Disable heavy query |
| Permission toggle |
Indefinite |
Control access by tier |
Beta user access |
| Infrastructure toggle |
Days–weeks |
Migrate between systems |
New DB read replica |
Keep short-lived flags (release, experiment) aggressively cleaned up. Treat ops kill switches as permanent infrastructure.
Implementation with OpenFeature SDK
import { OpenFeature } from "@openfeature/server-sdk";
// Configure provider once at startup (LaunchDarkly, Unleash, etc.)
OpenFeature.setProvider(new LaunchDarklyProvider(process.env.LD_SDK_KEY));
const client = OpenFeature.getClient("my-service");
// Evaluate a flag with context
async function renderCheckout(userId: string, isPremium: boolean) {
const ctx = { targetingKey: userId, isPremium };
const useNewFlow = await client.getBooleanValue("new-checkout-flow", false, ctx);
return useNewFlow ? renderNewCheckout() : renderLegacyCheckout();
}
The false second argument is the default — always safe to evaluate a flag even when the flag service is unreachable.
Gradual rollout pattern
| Stage |
Audience |
Goal |
| Internal only |
0.1% (employees, test accounts) |
Smoke test, check for crashes |
| Canary |
1–5% |
Catch errors before wide exposure |
| Staged rollout |
10% → 25% → 50% |
Monitor metrics per cohort |
| Full release |
100% |
All users |
| Flag removal |
— |
Clean up code and flag |
Automate stage progression tied to error rate and p99 latency metrics. If either spikes above baseline, roll back automatically.
Kill switches
An ops kill switch is a permanent boolean flag that can disable a feature or a heavy subsystem in production without a deployment.
async def get_recommendations(user_id: str):
if not await flags.get_bool("recommendations-enabled", default=True):
return [] # Fast path during incidents
return await recommendation_engine.fetch(user_id)
Every feature that could cause an incident under load (ML inference, expensive queries, third-party API integrations) should have a kill switch. Document them in your runbook.
Flag debt and hygiene
Flags that outlive their purpose increase code complexity and create logical branches that are never tested. Rules:
- Every release flag has a removal date in its metadata.
- Automated linting flags (pun intended) flags that are older than 30 days with 100% rollout — the feature is shipped, remove the toggle.
- Flag removal is a first-class task tracked in the same sprint as the feature launch.
- Stale flags log a warning at evaluation time if their removal date has passed.
How to pick a flag service
| Service |
Best for |
Self-hosted option |
Free tier |
| LaunchDarkly |
Enterprise, edge evaluation |
No |
Yes (limited) |
| Unleash |
Teams wanting self-hosted control |
Yes |
Yes (OSS) |
| Statsig |
Experiment-first teams, stats engine |
No |
Yes (generous) |
| GrowthBook |
Open-source, A/B + feature flags |
Yes |
Yes (OSS) |
| Flipt |
GitOps-native, Kubernetes-native |
Yes |
Yes (OSS) |
| OpenFeature + custom |
Provider-neutral, bring your own |
Yes |
— |
For most product teams: start with Unleash OSS or Statsig — both have a free tier that handles the majority of use cases.
Common mistakes
Nested flag conditions. if flagA and flagB and not flagC creates testing combinations that explode exponentially. Keep flags independent.
No default value. If the flag service is unreachable, what happens? Always supply a safe default in the evaluation call — usually the old behaviour (false for new features).
Flags in the database instead of a flag service. A feature_flags table you query on every request adds DB latency and doesn't give you targeting rules, audit logs, or SDK integration.
Releasing flags to 100% and never removing them. This is the most common failure mode. The flag stays, the dead code branch stays, and a year later nobody knows if removing the flag is safe.
Using flags for secrets or credentials. Flags are often evaluated client-side. Never put sensitive values in flag payloads.
What to skip
- Building your own flag evaluation service — the targeting rule engine, audit log, and SDK ecosystem take months to build properly. Use existing OSS or managed options.
- Feature flags for configuration — base URLs, timeouts, and connection pool sizes belong in environment variables or a config service with different change management.
- Long-lived experiment flags without a hypothesis — A/B tests without a defined metric and end date become permanent dead weight.
FAQ
Can I use feature flags for database migrations?
Yes — the "expand/contract" pattern: deploy the new schema (expand), flag-gate the new code path, roll out gradually, monitor, then remove the old schema (contract). It's one of the safest migration strategies.
Should flags be evaluated server-side or client-side?
Server-side by default — it hides your targeting logic and prevents manipulation. Client-side evaluation is fine for UI customisation where latency matters and the rules aren't sensitive.
How do I handle flags in automated tests?
Always evaluate flags in tests against a seeded test configuration (all-true or all-false as appropriate per test). Never call the live flag service in unit or integration tests — it couples tests to external state.
What is a flag flicker, and how do I prevent it?
A flag flicker happens on the client when the page renders with the default value before the flag service responds, then re-renders with the real value. Fix it by server-rendering the flag state into the initial HTML or using a blocking flag evaluation on the server.
Where to go next
See CI/CD pipeline basics in 2026 for integrating flag-controlled deployments into your pipeline, and Observability vs monitoring in 2026 for measuring the metrics that drive flag progression decisions.