Feature flags (also called feature toggles) are one of the most powerful tools for modern software delivery. They let you deploy code to production without exposing it to users, enabling progressive rollouts, A/B testing, and instant rollbacks.
A feature flag is a conditional in your code that controls whether a feature is active:
if (featureEnabled('new-checkout-flow')) {
renderNewCheckout();
} else {
renderOldCheckout();
}
The flag can be flipped without deploying code. This separates deployment (getting code to production) from release (exposing features to users).
Deploy and Release Separately
Without feature flags, deploying code means releasing it. If something breaks, you need to deploy again to fix it.
With feature flags, you deploy frequently (even continuously), but control when features become visible. Broken feature? Flip the flag off. No deployment needed.
Instead of all-or-nothing releases:
3.
If stable, roll out to 25%, then 50%, then 100%
This catches problems early, when they affect few users.
Compare feature variants:
•
Show variant A to 50% of users
•
Show variant B to 50% of users
•
Measure which performs better
Feature flags make this trivial.
Critical feature causing problems? Flip the flag off instantly. No deployment, no waiting for CI/CD, no stressful hotfixes.
Gradual Feature Development
Merge code that isn’t complete behind a flag. Multiple engineers can work on the same feature without blocking each other. When it’s ready, flip the flag.
Control whether a feature is visible to users. Usually temporary—removed once the feature is fully launched.
A/B test variants. Tied to specific experiments with start and end dates.
Control operational aspects: load shedding, circuit breakers, maintenance mode. Usually long-lived.
Control access to features based on user attributes: subscription tier, geographic location, account type.
const newPricingEnabled = await featureFlags.isEnabled('new-pricing', { userId });
if (newPricingEnabled) {
showNewPricing();
} else {
showOldPricing();
}
// Configure: 'new-checkout' enabled for 20% of users
const enabled = await featureFlags.isEnabled('new-checkout', { userId });
The flag service handles randomization consistently per user.
// Enable for specific users, teams, or segments
const enabled = await featureFlags.isEnabled('beta-feature', {
userId,
email: user.email,
plan: user.plan,
});
// Return specific variants instead of boolean
const variant = await featureFlags.getVariant('checkout-layout', { userId });
switch (variant) {
case 'single-page':
renderSinglePageCheckout();
break;
case 'multi-step':
renderMultiStepCheckout();
break;
default:
renderOriginalCheckout();
}
•
LaunchDarkly: Industry leader. Full-featured, enterprise-grade.
•
PostHog: Open source with feature flags + analytics.
•
Statsig: Strong experimentation focus.
•
Flagsmith: Open source, self-hostable.
•
Split: Enterprise-focused.
•
ConfigCat: Simple and affordable.
For most startups, buy. Feature flag infrastructure is:
•
Surprisingly complex to build well
•
Critical infrastructure that needs high reliability
Building your own makes sense only if you have unusual requirements or strong opinions about implementation.
If you must build it yourself, start simple:
// Simple feature flag service
const flags = {
'new-checkout': {
enabled: true,
percentage: 20,
userIds: ['user-123', 'user-456'], // Force-enable for these users
},
};
function isEnabled(flagName: string, context: { userId: string }): boolean {
const flag = flags[flagName];
if (!flag?.enabled) return false;
// Force-enabled users
if (flag.userIds?.includes(context.userId)) return true;
// Percentage rollout
const hash = hashString(flagName + context.userId);
return (hash % 100) < flag.percentage;
}
Store flag configuration in a database or config file. Update without deploys.
Use clear, consistent naming:
•
enable-new-checkout (release flag)
•
experiment-pricing-page-v2 (experiment)
•
ops-maintenance-mode (ops flag)
•
permission-enterprise-analytics (permission flag)
Include the type in the name.
New flags should default to off. This ensures incomplete features don’t accidentally get exposed.
Wrap the minimum amount of code. Don’t put entire pages behind flags when you only need to hide a button.
Flags left in code become tech debt. When a flag is fully rolled out:
3.
Remove the flag from your flag service
Set calendar reminders to clean up after rollouts.
Your test suite should test both flag states. A bug in the “flag off” path is still a bug.
Maintain a registry of active flags:
Don’t nest flags or create complex dependencies between them. If feature B only works when feature A is enabled, either:
•
Ensure B gracefully handles A being off
Progressive Rollout Strategy
Enable for your team. Catch obvious bugs.
Phase 2: Beta Users (1-5%)
Enable for users who’ve opted into early access. Get feedback.
Phase 3: Early Rollout (5-25%)
Start rolling out to general users. Monitor closely.
Phase 4: Wider Rollout (25-75%)
If stable, continue expanding. Keep monitoring.
Phase 5: Full Rollout (100%)
Feature is live for everyone. Monitor for a few days, then clean up the flag.
Define when you’ll roll back:
•
Error rate increases by X%
•
Latency increases by Y ms
•
Conversion rate drops by Z%
Don’t wait for things to get catastrophic. Roll back early, investigate, fix, re-roll out.
Monitoring and Observability
Feature flags need observability:
Log which flags are evaluated and their results. This helps debug issues.
Your analytics should filter by feature flag state. Compare metrics for users with the flag on vs. off.
Alert when flag changes cause metric changes. Automated detection catches problems you might miss.
Too many flags: Flag proliferation makes code hard to reason about. Clean up aggressively.
Flags that never get removed: Every flag should have an expiration date.
Testing only the happy path: Test both flag states in your CI.
No monitoring: Rolling out blind is dangerous. Always monitor.
Complex flag dependencies: Keep flags independent.
Using flags for configuration: Feature flags are for feature control, not app configuration. Use config management for settings.
•
Feature flags separate deployment from release
•
Use them for progressive rollouts, A/B tests, kill switches
•
Start with a SaaS solution (LaunchDarkly, PostHog, etc.)
•
Clean up old flags aggressively—they’re tech debt
•
Test both flag states, monitor rollouts, define rollback criteria
•
Roll out progressively: internal → beta → 5% → 25% → 100%