Feature Flags: The Art of Shipping Code Without Shipping Features
How feature flags decouple deployment from release, enabling safer rollouts, A/B testing, and instant rollbacks without redeploying.
February 12, 2026 · 6 min · 1269 words · Rob Washington
Table of Contents
There’s a subtle but powerful distinction in modern software delivery: deployment is not release.
Deployment means your code is running in production. Release means your users can see it. Feature flags are the bridge between these two concepts—and mastering them changes how you think about shipping software.
In the old model, deploying code meant releasing features:
1
2
3
# Old way: deploy = releasegit push origin main
# Boom, everyone sees the new feature immediately
This creates pressure. You can’t deploy partially-complete work. You can’t test in production with real traffic. And if something breaks, your only option is another deploy to roll back.
Feature flags flip this model:
1
2
3
4
5
# New way: deploy != releasegit push origin main
# Code is live, but flag is OFF# Enable for 1% of users, watch metrics# Gradually roll out to 100%
Notice the MD5 hashing in the rollout logic. This ensures:
Consistency: The same user always gets the same experience
Stickiness: Users don’t flip-flop between variants
Determinism: No database state needed per-user
1
2
3
4
5
6
# User "alice" at 25% rollout:hash("new_checkout:alice")%100=42# > 25, sees OLDhash("new_checkout:alice")%100=42# Still 42, still OLD# Increase to 50% rollout:hash("new_checkout:alice")%100=42# < 50, now sees NEW
The user’s experience changes based on rollout percentage, not random chance per request.
# Using Unleash clientfromUnleashClientimportUnleashClientclient=UnleashClient(url="http://localhost:4242/api",app_name="my-app",custom_headers={"Authorization":os.getenv("UNLEASH_API_KEY")})client.initialize_client()@app.route("/api/dashboard")defdashboard():context={"userId":current_user.id}ifclient.is_enabled("new_dashboard",context):returnjsonify(get_new_dashboard_data())returnjsonify(get_legacy_dashboard_data())
@dataclassclassFeatureFlag:name:strenabled:boolcreated_at:datetimeexpires_at:datetime# When to remove the flagowner:str# Who's responsible for cleanupjira_ticket:str# Tracking removal
# Log flag decisions for analyticsimportstructloglogger=structlog.get_logger()defis_enabled(flag_name:str,user_id:str)->bool:result=flags.is_enabled(flag_name,user_id)logger.info("feature_flag_evaluated",flag=flag_name,user_id=user_id,result=result,rollout_pct=flags.get(flag_name).rollout_percentage)returnresult
The killer feature of feature flags: instant rollback without deployment.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Something's wrong with new checkout!# Old way:git revert abc123
git push
# Wait for CI/CD pipeline...# Wait for deployment...# 15-30 minutes of pain# With feature flags:curl -X PATCH https://api.launchdarkly.com/flags/new-checkout \
-H "Authorization: $LD_API_KEY"\
-d '{"on": false}'# Done in seconds