Feature Flags & Progressive Delivery
Implement feature flags for safe deployments. Covers flag types, targeting rules, kill switches, flag lifecycle, A/B testing, trunk-based development, and feature flag architecture.
Feature flags decouple deployment from release. You deploy code to production that’s hidden behind a flag, then turn it on for 1% of users, then 10%, then 50%, watching metrics at each step. If something breaks, flip the flag off — instant rollback without a deployment. This is how Netflix, Google, and every high-performing engineering team ships safely.
Flag Types
| Type | Purpose | Lifetime | Example |
|---|---|---|---|
| Release | Hide incomplete features | Days to weeks | New checkout flow |
| Experiment | A/B test variations | Weeks to months | Button color test |
| Ops | Circuit breaker, kill switch | Permanent | Disable recommendations under load |
| Permission | Entitlement-based access | Permanent | Premium-only features |
Implementation
from feature_flags import FeatureFlags
flags = FeatureFlags(provider="launchdarkly") # or Unleash, Flagsmith, etc.
# Simple boolean flag
if flags.is_enabled("new-checkout", user=current_user):
return render_new_checkout()
else:
return render_current_checkout()
# Percentage rollout
flags.create_flag("new-checkout", {
"targeting": {
"rules": [
{
"name": "internal-users",
"match": {"attribute": "email", "endsWith": "@company.com"},
"percentage": 100, # All internal users
},
{
"name": "beta-users",
"match": {"attribute": "beta_opt_in", "equals": True},
"percentage": 100, # All beta users
},
{
"name": "gradual-rollout",
"match": "everyone",
"percentage": 10, # 10% of all other users
},
],
"default": False,
}
})
Progressive Delivery
Step 1: Deploy to production (flag OFF for everyone)
└── Verify: no errors, service healthy
Step 2: Enable for internal team (flag ON for @company.com)
└── Verify: team tests new feature in production
Step 3: Enable for 5% of users
└── Verify: error rate, latency, conversion rate
Step 4: Enable for 25% of users
└── Verify: metrics stable, no performance regression
Step 5: Enable for 100% of users
└── Monitor for 1 week
Step 6: Remove flag from code (clean up)
Flag Lifecycle
| Phase | Duration | Action |
|---|---|---|
| Create | Day 0 | Define flag, add to code behind flag |
| Test | Days 1-3 | Internal testing, QA validation |
| Rollout | Days 3-14 | Progressive rollout: 5% → 25% → 100% |
| Stable | Day 14+ | Flag at 100%, monitoring confirms success |
| Cleanup | Day 30 | Remove flag from code, delete from flag service |
Anti-Patterns
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Flags never cleaned up | Thousands of stale flags, unmaintainable code | Expiration date on every flag, monthly cleanup |
| Nested flags | Complex interactions, untestable combinations | Minimize flag interactions, test combinations |
| Flags in hot loops | Performance overhead per-request | Cache flag values, evaluate once per request |
| No default value | Flag service down = feature broken | Always define sensible default (usually OFF) |
| Testing only with flag ON | Flag OFF path untested, breaks on rollback | Test both flag states in CI |
Checklist
- Flag management platform selected (LaunchDarkly, Unleash, Flagsmith)
- Flag naming convention established
- Progressive delivery: rollout stages defined
- Kill switch flags for critical dependencies
- Cleanup policy: flags removed within 30 days of 100% rollout
- Testing: both flag states tested in CI
- Monitoring: flag evaluation metrics, rollout dashboards
- Default values: every flag has a safe default
:::note[Source] This guide is derived from operational intelligence at Garnet Grid Consulting. For feature flag consulting, visit garnetgrid.com. :::