Ana Sayfa / Blog / Feature flag sistemi kurma: 4 aşamalı adoption rehberi

Feature flag sistemi kurma: 4 aşamalı adoption rehberi

Feature flag'ler deploy'u feature release'den ayırıyor. A/B test, gradual rollout, kill switch. Kendi sisteminizi nasıl kurarsınız?

Feature flag (feature toggle) modern software development’in temel araçlarından. Deploy ile feature release’i ayırıyor. Production’da kodunuz var ama kullanıcıya gösterilmiyor. Switch’i aç, feature live.

A/B test, canary deploy, gradual rollout, kill switch – hepsi feature flag ile mümkün. Bu yazıda kendi flag sisteminizi 4 aşamada nasıl kurabileceğinizi anlatacağım.

Aşama 1: Simple config flags

Başlangıç: config file’da basit boolean flag’ler.

# config/features.yml
features:
  new_checkout: false
  dark_mode: true
  experimental_search: false

Code’da kullanım:

if config.features.new_checkout:
    render_new_checkout()
else:
    render_old_checkout()

Avantaj:
– Super simple setup
– No external dependency
– Version controlled (config Git’te)

Dezavantaj:
– Flag değiştirmek için deploy gerekiyor
– User-level targeting yok
– A/B test imkansız
– Real-time toggle yok

Bu aşama küçük proje için OK. Büyüdükçe yetersiz.

Aşama 2: Database-backed flags

Flag’leri database’de tut. Admin panel’den değiştirilebilir.

Schema:

CREATE TABLE feature_flags (
    key VARCHAR(100) PRIMARY KEY,
    enabled BOOLEAN DEFAULT FALSE,
    description TEXT,
    updated_at TIMESTAMP
);

Code:

def is_enabled(flag_key: str) -> bool:
    flag = db.query("SELECT enabled FROM feature_flags WHERE key = ?", flag_key)
    return flag and flag.enabled

Admin panel: Basit UI, flag toggle.

Avantaj:
– Real-time toggle (deploy olmadan)
– Admin panel control
– Audit log (kim ne zaman toggle etti)

Dezavantaj:
– Her flag check database call (performans issue)
– User-level targeting hâlâ yok
– A/B test hâlâ yok

Fix: caching layer. Redis’te flag state’leri cache’le, 30 saniye TTL.

Aşama 3: User-targeted flags

Bazı user’lara show, bazılarına don’t. Beta testers, internal team, premium users.

Schema:

CREATE TABLE feature_flags (
    key VARCHAR(100) PRIMARY KEY,
    enabled BOOLEAN,
    rollout_percentage INT,  -- 0-100
    user_groups JSON,  -- ["beta", "internal"]
    user_whitelist JSON  -- specific user_ids
);

Code:

def is_enabled(flag_key: str, user_id: str, user_groups: list) -> bool:
    flag = get_flag(flag_key)
    if not flag.enabled:
        return False
    
    # Whitelist check
    if user_id in flag.user_whitelist:
        return True
    
    # Group check
    if any(g in flag.user_groups for g in user_groups):
        return True
    
    # Rollout percentage (deterministic hash)
    user_hash = int(md5(flag_key + user_id).hexdigest(), 16)
    if (user_hash % 100) < flag.rollout_percentage:
        return True
    
    return False

Gradual rollout:
– Start: 1% rollout
– If no issues: 5%, 10%, 25%, 50%, 100%
– Issue detected: rollback instantly

Avantaj:
– User targeting
– Gradual rollout
– Kill switch
– A/B test foundations

Dezavantaj:
– Complexity yüksek
– Analytics integration lazım A/B test için

Aşama 4: Full A/B testing

A/B testing için feature flag’ler + analytics + statistical significance.

Enhanced flag:

CREATE TABLE experiments (
    key VARCHAR(100) PRIMARY KEY,
    variants JSON,  -- [{"name": "control", "weight": 50}, {"name": "new_ui", "weight": 50}]
    active BOOLEAN,
    started_at TIMESTAMP
);

CREATE TABLE experiment_exposures (
    experiment_key VARCHAR(100),
    user_id VARCHAR(100),
    variant VARCHAR(50),
    exposed_at TIMESTAMP,
    INDEX (experiment_key, user_id)
);

Code:

def get_variant(experiment_key: str, user_id: str) -> str:
    experiment = get_experiment(experiment_key)
    
    # Existing assignment?
    existing = get_exposure(experiment_key, user_id)
    if existing:
        return existing.variant
    
    # New assignment (hash-based)
    user_hash = int(md5(experiment_key + user_id).hexdigest(), 16) % 100
    cumulative = 0
    for variant in experiment.variants:
        cumulative += variant.weight
        if user_hash < cumulative:
            record_exposure(experiment_key, user_id, variant.name)
            return variant.name

Analytics integration:

# Track experiment metric
analytics.track("purchase", {
    "user_id": user_id,
    "amount": 100,
    "experiment_new_checkout": get_variant("new_checkout", user_id)
})

Later, analyze:
– Variant A conversion rate: 5.2%
– Variant B conversion rate: 6.8%
– Statistical significance: p < 0.01
– Variant B wins, roll out fully

Third-party solutions

Kendi sistemini kurmak istemiyorsan:

LaunchDarkly: Enterprise-grade feature flag management. $$$.
Unleash: Open source, self-hosted.
Split.io: Feature flag + A/B test. Mid-market.
Flagsmith: Open source + managed options.
Firebase Remote Config: Google’ın ücretsiz seçeneği.

Trade-off: vendor dependency, pricing, customization.

Küçük projede Firebase Remote Config yeter. Orta ölçek LaunchDarkly. Enterprise self-hosted Unleash.

Flag hygiene

Feature flag’lerin yaşam döngüsü var. Ignoring → flag debt.

Types:
Release flags: Feature deployment için. Short-lived (weeks).
Experiment flags: A/B test için. Medium-lived (months).
Ops flags: Kill switch, circuit breaker. Long-lived.
Permission flags: Premium features. Permanent.

Cleanup discipline:
– Release flag successful → merge code, remove flag within 2 weeks
– Experiment concluded → make winning variant default, remove flag
– Ops flags review quarterly

Flag debt: 6 ay önce eklenen flag’ler hâlâ code’da. Her flag cognitive overhead.

Performance considerations

Flag check her request’te happening. Overhead minimize:

  1. In-memory cache. Flag state’leri app memory’de, periodic refresh.
  2. Batch fetching. User login’de tüm flag’leri fetch, session boyunca kullan.
  3. Edge-deployed flags. CDN level flag evaluation.

Cache TTL balance: uzun TTL → stale flag, kısa TTL → DB pressure.

Testing with flags

Flag’li kod test etme:

@pytest.fixture
def enable_flag(flag_key):
    with mock.patch('features.is_enabled', return_value=True):
        yield

def test_new_checkout_flow(enable_flag):
    # Test with flag ON
    ...

Every major flag için her iki path’i test et. “Flag on” ve “flag off” scenarios.

Sonuç

Feature flag’ler deploy risk’ini azaltıyor. Gradual rollout, kill switch, A/B test – hepsini enable ediyor.

4 aşamalı adoption: config → DB → user-targeted → full A/B. Her aşama önceki’yi genişletiyor. Projenizin büyüklüğüne göre başlayın.

Flag hygiene unutmayın. Flag’ler deprecated olmadığında code complexity artıyor. Cleanup discipline gerekli.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç