patternModeratepending
Pattern: Feature flags for safe deployments
Viewed 0 times
feature-flagsgradual-rolloutdeploymenttogglecanary
Problem
Deploying new features is all-or-nothing. Rolling back requires a new deployment. Can't do gradual rollouts or A/B tests.
Solution
Use feature flags to decouple deployment from release:
# Simple implementation
class FeatureFlags:
def __init__(self, config_source):
self._flags = config_source.load()
def is_enabled(self, flag_name, user=None):
flag = self._flags.get(flag_name)
if not flag:
return False
# Global kill switch
if not flag.get('enabled', False):
return False
# Percentage rollout
if 'percentage' in flag and user:
return hash(f"{flag_name}:{user.id}") % 100 < flag['percentage']
# User allowlist
if 'allowed_users' in flag and user:
return user.id in flag['allowed_users']
return flag['enabled']
# Usage in code:
if features.is_enabled('new_checkout', current_user):
return new_checkout_flow()
else:
return legacy_checkout_flow()
Best practices:
# Simple implementation
class FeatureFlags:
def __init__(self, config_source):
self._flags = config_source.load()
def is_enabled(self, flag_name, user=None):
flag = self._flags.get(flag_name)
if not flag:
return False
# Global kill switch
if not flag.get('enabled', False):
return False
# Percentage rollout
if 'percentage' in flag and user:
return hash(f"{flag_name}:{user.id}") % 100 < flag['percentage']
# User allowlist
if 'allowed_users' in flag and user:
return user.id in flag['allowed_users']
return flag['enabled']
# Usage in code:
if features.is_enabled('new_checkout', current_user):
return new_checkout_flow()
else:
return legacy_checkout_flow()
Best practices:
- Clean up old flags regularly (tech debt)
- Log flag evaluations for debugging
- Have a kill switch for every flag
- Test both paths in CI
Why
Feature flags let you deploy code without releasing features, enabling gradual rollouts, instant rollbacks, and experimentation.
Revisions (0)
No revisions yet.