ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

Feature Flag Architectures

Design feature flag systems that enable safe, controlled feature rollouts. Covers flag types, targeting, evaluation, lifecycle management, technical debt, and the patterns that make feature flags a superpower instead of a liability.

Feature flags decouple deployment from release. You deploy code that is hidden behind a flag, then enable it for specific users, percentages, or segments. This means you can deploy to production at any time without exposing unfinished features, gradually roll out to catch issues early, and instantly disable features without redeploying.


Flag Types

Release Flags:
  Purpose: Hide unfinished features
  Lifetime: Days to weeks
  Example: new_checkout_flow = false (enable when ready)
  
Experiment Flags:
  Purpose: A/B testing
  Lifetime: Weeks to months
  Example: checkout_variant = "A" | "B" | "C"
  
Ops Flags:
  Purpose: Operational control (kill switches)
  Lifetime: Permanent
  Example: enable_external_payment_processor = true
  
Permission Flags:
  Purpose: Entitlement / access control
  Lifetime: Permanent
  Example: premium_analytics = user.plan == "enterprise"

Evaluation Architecture

class FeatureFlagService:
    def __init__(self, config_store):
        self.store = config_store
        self.cache = {}
    
    def is_enabled(self, flag_name: str, context: dict) -> bool:
        flag = self.store.get(flag_name)
        if not flag:
            return False
        
        # Kill switch: globally on/off
        if flag.state == "ON":
            return True
        if flag.state == "OFF":
            return False
        
        # Targeting rules (evaluated in order)
        for rule in flag.targeting_rules:
            if self._matches(rule.conditions, context):
                return self._evaluate_rollout(rule.rollout, context)
        
        # Default rule
        return self._evaluate_rollout(flag.default_rollout, context)
    
    def _evaluate_rollout(self, rollout, context):
        if rollout.type == "percentage":
            # Consistent hashing: same user always gets same result
            hash_val = hash(f"{context['user_id']}:{rollout.flag_name}")
            return (hash_val % 100) < rollout.percentage
        
        return rollout.value

# Usage
flags = FeatureFlagService(config_store)

if flags.is_enabled("new_checkout", {"user_id": "123", "plan": "pro"}):
    render_new_checkout()
else:
    render_old_checkout()

Targeting Rules

flag:
  name: "new_checkout"
  state: "TARGETED"  # Evaluate targeting rules
  
  targeting_rules:
    # Rule 1: Internal team gets it first
    - conditions:
        - attribute: "email"
          operator: "ends_with"
          value: "@company.com"
      rollout:
        type: "boolean"
        value: true
    
    # Rule 2: 10% of premium users
    - conditions:
        - attribute: "plan"
          operator: "in"
          value: ["premium", "enterprise"]
      rollout:
        type: "percentage"
        value: 10
    
    # Rule 3: Specific beta users
    - conditions:
        - attribute: "user_id"
          operator: "in"
          value: ["user_123", "user_456"]
      rollout:
        type: "boolean"
        value: true
  
  default_rollout:
    type: "boolean"
    value: false

Flag Lifecycle

1. CREATE
   Developer creates flag with default OFF
   Code deployed behind flag

2. DEVELOP
   Flag ON for developers and internal team
   Testing in production environment

3. ROLLOUT
   Gradual: 1% → 5% → 25% → 50% → 100%
   Monitor metrics at each step

4. RELEASE
   Flag set to ON for everyone
   Old code path is now dead code

5. CLEANUP ← Most teams skip this!
   Remove flag from code
   Remove old code path
   Remove flag configuration
   
   CRITICAL: Set cleanup deadline at creation time
   "This flag must be removed by March 15"

Anti-Patterns

Anti-PatternConsequenceFix
Never cleaning up flagsTechnical debt, code complexityMandatory expiration dates
Nested flag checksCombinatorial explosionFlat flags, no nesting
Flags in data layerHard to reason about data flowFlags at service/UI layer only
No monitoring per flagCan’t attribute issues to flag changesMetric correlation with flag state
Hardcoded flag evaluationsCannot change without deployExternal flag config service

Feature flags are debt with an expiration date. Every flag you create must have a plan for removal. The flag system that has 500 stale flags is worse than no flag system at all.

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →