Decoupling with an "If" Statement
Imagine a light switch on a wall that controls a lamp in another room. You can wire the lamp to the switch (deployment) without ever flipping the switch on (release). Feature flags are that switch — they let you ship code to production and decide later, independently, when and to whom to turn it on.
At its heart, a feature flag is just an if statement in your code that wraps new functionality:
if feature_flags.is_enabled("new-checkout-flow", user_id):
# Show the shiny new checkout page
render_new_checkout()
else:
# Show the old, stable checkout page
render_old_checkout()
The magic is that the condition of this if statement isn't hardcoded. It's controlled by a remote system — like LaunchDarkly, Split.io, Unleash, or an internal tool — that evaluates the flag at runtime and returns True or False based on rules you define.
Dynamic Control Without Redeployment
This remote evaluation is what makes feature flags powerful. Developers and product managers can change who sees a feature in milliseconds without touching code or triggering a new deployment:
- Internal Testing: "Turn this on only for users with an
@mycompany.comemail address." This lets your QA team test the live production system safely. - Beta Groups: "Turn this on for users who opted into the beta program." Gather early feedback from willing participants.
- Percentage Rollout: "Turn this on for 5% of users, then 25%, then 50%, then 100%." Gradually expand exposure while monitoring for problems.
- Kill Switch: "The new feature is causing payment failures! Turn it off for everyone right now." Instant mitigation without a rollback deployment.
- Entitlement: "Turn this on only for users on the 'Enterprise' pricing tier." Gate premium features by subscription level.
In practice, a flag evaluation SDK is initialized once at application startup, then queried per-request with a "variation" call that passes the specific user's context (their ID, email, plan type, and other targeting attributes) plus a default value to fall back on if the flag is off or the SDK is unreachable. The SDK evaluates that user against the targeting rules and percentage rollouts configured remotely, and the application branches on the result — the same conceptual if statement as before, just with the flag's true/false state resolved by a real evaluation engine instead of hardcoded.
Flag Types and Lifecycle
Not all flags are the same. Understanding their intended lifespan prevents a common failure mode — flags that live forever and become unmaintainable:
| Flag Type | Purpose | Expected Lifespan |
|---|---|---|
| Release Flag | Hide incomplete features during development | Days to weeks; remove after full rollout |
| Experiment Flag | A/B test two variants to measure business impact | Days to weeks; remove after decision |
| Ops Flag | Kill switch for risky features or third-party integrations | Months; keep as long as the risk exists |
| Permission Flag | Gate features by user tier or entitlement | Long-lived; part of the product's access model |
Release flags are the most dangerous if left in place. A flag that was meant to be temporary but stays in the codebase for a year becomes flag debt — the team forgets what it does, is afraid to remove it, and the codebase becomes a maze of conditional branches.
OpenFeature Standard
Because every company started building their own feature flag systems, it created a fragmented ecosystem of incompatible APIs. If you built against LaunchDarkly's SDK directly, switching to Unleash meant rewriting every flag evaluation call in your codebase.
OpenFeature is a CNCF (Cloud Native Computing Foundation) standard that defines a unified, vendor-neutral API for feature flagging. Your application code calls the OpenFeature SDK, and a provider plugin handles the actual communication with your chosen backend (LaunchDarkly, Flagsmith, Unleash, etc.). Switching providers becomes a configuration change, not a code rewrite.
from openfeature import api
from openfeature.contrib.provider.flagd import FlagdProvider
# Set the provider once — swap this line to change backends
api.set_provider(FlagdProvider())
client = api.get_client()
# Your application code never changes, regardless of backend
enabled = client.get_boolean_value("new-checkout-flow", False, evaluation_context)
Interviewers often ask: "What's the difference between a feature flag and a configuration value?" The key distinction is who controls it and when. A config value (like a database connection string) is set at deploy time and requires a restart to change. A feature flag is evaluated at runtime per request, can be changed without any deployment, and is typically controlled by non-engineers (product managers, on-call engineers) through a UI. Feature flags are also user-context-aware — the same flag can return different values for different users simultaneously.