How to Evaluate Feature Flags in Python
A Python function that evaluates boolean feature flags with user-specific overrides, returning whether a flag is enabled and the reason for the decision.
Python code
54 linesimport json
def evaluate_feature_flag(feature_name, context, flag_configs):
"""
Evaluates a boolean feature flag given a context dictionary.
Args:
feature_name: The name of the feature flag.
context: A dictionary of user/request context (e.g., {"user_id": "123"}).
flag_configs: A dictionary of flag configurations.
Returns:
A tuple (enabled, reason) where enabled is bool and reason explains the decision.
"""
# Flag not found → disabled with a clear reason
if feature_name not in flag_configs:
return False, f"flag '{feature_name}' not found"
config = flag_configs[feature_name]
enabled = config.get("enabled", False)
# Check for user-based override rules
for rule in config.get("overrides", []):
if rule.get("user_id") == context.get("user_id"):
return rule["enabled"], f"override matched user_id={context['user_id']}"
return enabled, "standard evaluation"
if __name__ == "__main__":
flags = {
"dark_mode": {"enabled": True, "overrides": [
{"user_id": "beta_1", "enabled": False}
]},
"new_checkout": {"enabled": False}
}
# Simulate mock evaluation for a normal user
context_normal = {"user_id": "user_123"}
result_normal = evaluate_feature_flag("dark_mode", context_normal, flags)
print(f"dark_mode (normal): {result_normal}")
# Simulate mock evaluation for an overridden user
context_beta = {"user_id": "beta_1"}
result_beta = evaluate_feature_flag("dark_mode", context_beta, flags)
print(f"dark_mode (beta): {result_beta}")
# Non-existent flag
result_missing = evaluate_feature_flag("unknown_flag", context_normal, flags)
print(f"unknown_flag: {result_missing}")
# Disabled flag
result_disabled = evaluate_feature_flag("new_checkout", context_normal, flags)
print(f"new_checkout: {result_disabled}")
Output
dark_mode (normal): (True, 'standard evaluation')
dark_mode (beta): (False, 'override matched user_id=beta_1')
unknown_flag: (False, "flag 'unknown_flag' not found")
new_checkout: (False, 'standard evaluation')
How it works
The evaluate_feature_flag function first checks if the flag exists in the config dictionary, returning a disabled result with a clear reason if not found. It then reads the enabled value with .get() so missing keys default to False instead of raising an error. User-specific overrides are checked in order, returning the override result immediately when a matching user_id is found — this makes it easy to test beta users or roll back a feature for specific accounts. The function always returns a tuple of (bool, str), which keeps the decision logic transportable and easy to unit test.
Common mistakes
- Forgetting to handle the case where the flag isn't in the config, causing a KeyError
- Using `config["enabled"]` directly instead of `.get("enabled", False)` for safe defaults
- Checking overrides after evaluating the default enabled state, which can accidentally apply overrides to flags that should always be off
Variations
- Use percentage-based rollout with `hash(user_id) % 100 < rollout_percentage`
- Store configs in a database and cache them in memory for faster lookups
Real-world use cases
- Rolling out a new UI feature gradually to specific user segments while keeping control over edge-case accounts.
- A/B testing different checkout flows where a test group gets a new experience and control group stays on the old one.
- Emergency kill-switch automation — disable a buggy flag for all users instantly without redeploying code.
Sponsored
More from A/B testing & experimentation
Keep learning
Related tutorials and quizzes for this topic.