Yeni ödeme sağlayıcısı entegrasyonu üç haftadır ayrı bir dalda bekliyor. Birleştirme kavgası büyüdükçe büyüyor, kimse dokunmak istemiyor ve yayın günü yaklaştıkça herkes geriliyor. Feature flag, yani özellik bayrağı, bu tabloyu ortadan kaldıran basit bir fikirdir: kodu yayınlamak ile özelliği kullanıcıya açmak birbirinden ayrı iki karardır. Kod canlıya çıkar ama kapalı durur; sen hazır olduğunda bir ayarı değiştirerek açarsın, ters giderse aynı ayarla saniyeler içinde kapatırsın.
Bu rehberde feature flag'in ne olduğunu, hangi bayrak türlerinin hangi ömre sahip olduğunu, en basit haliyle nasıl uygulanacağını, kademeli açma ve kullanıcı hedefleme mantığını, acil durum kill switch'ini, bayrakların yarattığı teknik borcu ve gerçek hayatta insanı en çok yakan hataları anlatacağım. Amacım, yayın gününü heyecanlı bir olaydan sıradan bir işleme dönüştürmen.
Feature Flag Nedir ve Neyi Değiştirir#
Feature flag, kodun içindeki bir koşuldan ibarettir: "bu özellik açık mı?" diye sorar ve cevaba göre yeni ya da eski davranışı çalıştırır. Teknik olarak sıradan bir if bloğudur; değeri ise koddan değil, dışarıdan bir yapılandırma kaynağından gelir. Değişikliği yayınlamadan değiştirebilmen, tüm gücün kaynağıdır.
Bu ayrımın pratikte üç sonucu vardır. Birincisi, yarım kalmış iş ana dala girebilir. Özelliğin tamamlanmamış hali kapalı bayrak arkasında canlıda durur, kimse görmez, ama uzun ömürlü dallar ve dev birleştirme kavgaları biter. İkincisi, yayın riski dağılır. Yayın günü sadece kodu taşırsın; asıl riskli an olan "özelliği açma" işlemini ayrı bir zamana, tercihen trafiğin düşük olduğu bir saate alırsın. Üçüncüsü, geri dönüş anlıktır. Yeni sürümü geri almak yerine bayrağı kapatırsın; yeniden derleme, imaj çekme ve yeniden başlatma yoktur.
Bu yaklaşımı canary deployment ile karıştırmamak gerekir. Canary, aynı kodun farklı sürümlerini farklı kullanıcı dilimlerine dağıtır; feature flag ise tek bir sürüm içinde farklı davranışlar arasında geçiş yapar. İkisi birbirinin alternatifi değildir, sıklıkla birlikte kullanılır: canary ile altyapı riskini, bayrakla iş mantığı riskini yönetirsin.
Bayrak Türleri ve Ömürleri#
Tüm bayrakları aynı torbaya koymak, ilerideki temizlik işini imkânsız hale getirir. Bir bayrağı yazarken türünü ve tahmini ömrünü baştan belirlemek, altı ay sonra kodun içinde ne olduğunu kimsenin bilmediği yüzlerce koşul bırakmanı engeller.
| Tür | Amaç | Tipik ömür | Kim değiştirir |
|---|---|---|---|
| Yayın bayrağı | Tamamlanmamış özelliği gizlemek | Günler – haftalar | Geliştirici |
| Deney bayrağı | A/B testi, ölçüm | Haftalar | Ürün ekibi |
| İşletme bayrağı | Kampanya, bakım modu, özellik paketi | Aylar – kalıcı | İş birimi |
| Kill switch | Sorunlu bileşeni acilen kapatmak | Kalıcı | Nöbetçi mühendis |
Bunların içinde en tehlikeli olanı yayın bayrağıdır, çünkü doğası gereği geçici olmasına rağmen en çok unutulanıdır. Kill switch ise tam tersi: kalıcı olması gerekir ve genelde yeterince yazılmaz. Her dış bağımlılığın (ödeme sağlayıcısı, e-posta servisi, öneri motoru) arkasında bir kill switch olması, o bağımlılık çöktüğünde tüm sitenin değil yalnızca o parçanın devre dışı kalmasını sağlar.
En Basit Uygulama: Ortam Değişkeni ve Yapılandırma#
Feature flag için hemen bir hizmet satın alman gerekmez. Küçük ve orta ölçekli bir sistemde ortam değişkeni ya da merkezi bir yapılandırma anahtarı fazlasıyla yeterlidir. Önemli olan, bayrağı okuyan tek bir yer bulunması ve kodun her yerine dağılmamasıdır.
En yalın haliyle bir ortam değişkeni:
# .env dosyasında ya da servis tanımında
FEATURE_YENI_ODEME=false
FEATURE_ONERI_MOTORU=true
import os
def bayrak_acik(ad: str, varsayilan: bool = False) -> bool:
"""Bayrak okumanın TEK yeri burasıdır; kod başka yerde os.environ okumaz."""
ham = os.environ.get(f"FEATURE_{ad.upper()}")
if ham is None:
return varsayilan
return ham.strip().lower() in ("1", "true", "on", "evet")
# Kullanım
if bayrak_acik("yeni_odeme"):
sonuc = yeni_odeme_akisi(siparis)
else:
sonuc = eski_odeme_akisi(siparis)
Ortam değişkeninin tek dezavantajı, değiştirmek için süreci yeniden başlatman gerekmesidir; yani "anlık kapatma" özelliğini kaybedersin. Bunu çözmek için bayrakları Redis, veritabanı ya da paylaşılan bir dosyada tutup kısa aralıklı önbellekle okuyabilirsin:
import time, json
_onbellek, _zaman = {}, 0.0
TTL = 10 # saniye
def bayraklar() -> dict:
global _onbellek, _zaman
if time.time() - _zaman > TTL:
# Redis'ten oku; hata olursa son bilinen değeri kullan (fail-safe)
try:
_onbellek = json.loads(redis.get("feature_flags") or "{}")
_zaman = time.time()
except Exception:
pass # bilinçli: bayrak deposu çökerse davranış değişmesin
return _onbellek
Buradaki except bloğu bilinçlidir ve kritiktir: bayrak deposuna ulaşılamadığında sistem son bilinen değerlerle çalışmaya devam etmeli, asla tüm bayrakları kapatıp özellikleri topluca devre dışı bırakmamalıdır. Yapılandırmanın koddan ayrılması ilkesinin genel biçimini Twelve-Factor App yazısında bulabilirsin.
Kademeli Açma ve Kullanıcı Hedefleme#
Bir bayrağı yalnızca açık/kapalı olarak düşünürsen gücünün yarısını kullanmamış olursun. Asıl değer, kimin için açık sorusunu cevaplayabilmektir. Tipik açma sırası şudur ve her adım bir öncekinden öğrendiğini kullanır:
- Yalnızca geliştirme ekibinin hesapları (iç test).
- Gönüllü erken erişim kullanıcıları.
- Kullanıcıların %1'i, ardından %5, %25, %50.
- Tüm kullanıcılar.
- Bayrağın kaldırılması, kodun sadeleştirilmesi.
Yüzdelik açmanın en kritik kuralı tutarlılıktır: aynı kullanıcı her istekte aynı tarafa düşmelidir. Rastgele sayı kullanırsan kullanıcı sayfayı her yenilediğinde arayüz değişir ve bu, hatanın kendisinden daha kötü bir deneyimdir. Doğru yöntem, kullanıcı kimliğinin kararlı bir özetini almaktır:
import hashlib
def yuzde_icinde(kullanici_id: str, bayrak: str, yuzde: int) -> bool:
"""Aynı kullanıcı + aynı bayrak her zaman aynı sonucu verir."""
ham = f"{bayrak}:{kullanici_id}".encode("utf-8")
kova = int(hashlib.sha256(ham).hexdigest()[:8], 16) % 100
return kova < yuzde
# %5'e açmak için
if yuzde_icinde(kullanici.id, "yeni_odeme", 5):
...
Bayrak adını da özete katmak önemlidir; katmazsan aynı %5'lik dilim her bayrakta aynı kullanıcılara denk gelir ve o talihsiz grup her deneyin kobayı olur. Kullanıcı kimliği yoksa (oturum açmamış ziyaretçi) kalıcı bir çerez kimliği üret ve onu kullan; IP adresi kullanma, çünkü aynı kurumsal ağdaki yüzlerce kişi tek IP'den gelir ve dağılım bozulur.
Kill Switch: Acil Kapatma Anahtarı#
Kill switch, feature flag'in en az yazılan ama en çok işe yarayan biçimidir. Amacı bir özelliği yavaşça açmak değil, çalışan bir şeyi hemen durdurmaktır. Dış servis çöktüğünde, bir sorgu veritabanını boğduğunda ya da bir iş yükü beklenmedik maliyet ürettiğinde saniyeler içinde devreye girer.
İyi bir kill switch üç özelliğe sahiptir. Yeniden başlatma gerektirmez, çünkü panik anında yeniden başlatmak yeni riskler doğurur. Nöbetçi mühendis tarafından tek başına kullanılabilir; onay zinciri gerektirmez. Ve kapalı durumda uygulama anlamlı bir davranışa düşer, hata vermez. Örneğin öneri motoru kapandığında sayfa boş kalmaz, statik bir liste gösterir.
def onerileri_getir(kullanici):
if not bayrak_acik("oneri_motoru", varsayilan=True):
# Kapalıyken hata verme; makul bir yedeğe düş
return populer_urunler_onbellekten()
try:
return oneri_servisi.getir(kullanici.id, zaman_asimi=0.4)
except (TimeoutError, ConnectionError):
return populer_urunler_onbellekten()
Kill switch'lerin bir listesi olmalı ve bu liste olay müdahale belgende yer almalıdır: hangi bayrak neyi kapatır, kapatıldığında kullanıcı ne görür, kimin haberi olmalıdır. Bu listeyi ilk kez bir gece yarısı olayında hazırlamaya çalışmak, hazırlamamaktan sadece biraz daha iyidir. Hata bütçesi ve olay müdahalesi arasındaki ilişkiyi SRE, SLO ve error budget yazısında ele aldım.
Bayrak Borcu: Temizlemezsen Kodun Boğulur#
Feature flag'in bedeli koşullu karmaşıklıktır. Her bayrak kodda iki yol demektir; on bayrak teorik olarak binden fazla kombinasyon demektir. Bu kombinasyonların hepsini test edemezsin, dolayısıyla bir süre sonra hangi kombinasyonun çalıştığını kimse bilmez. Bu yüzden bayrak eklemek kadar kaldırmak da işin parçasıdır.
Sahada işe yarayan kurallar şunlardır:
- Her yayın bayrağına oluşturulduğu anda bir son kullanma tarihi ve bir sahip yaz.
- Bayrak tam açıldıktan sonra kaldırma işini biriktirme; aynı sprint içinde temizle.
- Bayrak sayısını görünür bir yerde takip et; artıyorsa borç büyüyordur.
- İç içe bayrak yazma. İki bayrağın birbirine bağlı olması, test edilemez bir durum uzayı yaratır.
- Kalıcı işletme bayraklarını geçici yayın bayraklarından ayrı adlandır (
ops_verel_öneki gibi).
Eski bayrakları bulmak için basit bir arama bile çok şey söyler:
# Kodda geçen tüm bayrak adlarını çıkar
grep -rhoE 'bayrak_acik\("([a-z0-9_]+)"' src/ | sed -E 's/.*"(.*)"/\1/' | sort -u > /tmp/koddaki.txt
# Yapılandırmada tanımlı olanlar
grep -oE '^FEATURE_[A-Z0-9_]+' .env | sed 's/FEATURE_//' | tr 'A-Z' 'a-z' | sort -u > /tmp/tanimli.txt
# Kodda var ama tanımsız (varsayılana düşüyor) olanlar
comm -23 /tmp/koddaki.txt /tmp/tanimli.txt
Kaldırma işini yaparken bayrağın her iki tarafını da temizlediğinden emin ol: koşulu silmek yetmez, artık ulaşılamayan eski kod yolunu ve ona ait testleri de kaldır. Yarım temizlik, ölü kodu sonsuza kadar depoda tutar.
Sık Yapılan Hatalar#
Bayrağı iş mantığının ortasına serpiştirmek. Aynı bayrağı on iki farklı dosyada sormak, kaldırma işini imkânsızlaştırır. Bayrağı sınıra yakın tut: giriş noktasında bir kez sor, sonra farklı uygulamaları çağır.
Veri yazma yolunda bayrak açıp geri dönüşü unutmak. Yeni kod verileri farklı bir biçimde yazıyorsa, bayrağı kapattığında eski kod o veriyi okuyamaz. Yazma yolundaki bayraklarda her iki biçimi de okuyabilen bir geçiş dönemi kur; bu, veritabanı göçlerindeki genişlet-daralt düzeninin aynısıdır ve rolling update ve rollback yazısında ayrıntılı örneği var.
Bayrak deposunu tek hata noktası yapmak. Redis çöktüğünde tüm bayrakların kapanması, küçük bir arızayı topyekûn kesintiye çevirir. Son bilinen değeri bellekte tut ve depo erişilemezken onunla devam et.
Yüzdeyi rastgele hesaplamak. Kararlı özet kullanmazsan kullanıcı her sayfa yenilemesinde farklı sürüm görür; ölçümün de anlamsızlaşır çünkü aynı kişi hem kontrol hem deney grubunda sayılır.
Bayrağı test etmemek. Kapalı yol da bir koddur ve o da bozulabilir. Kritik bayrakların her iki durumunu da otomatik testlerinde çalıştır; en azından açık ve kapalı hallerini kapsayan birer test yazmak, "kapattık ama kapalıyken çöküyor" sürprizini önler.
Sıkça Sorulan Sorular#
Feature flag için ayrı bir servis şart mı#
Hayır. Küçük ve orta ölçekli ekiplerde ortam değişkeni ya da Redis'te tutulan basit bir anahtar-değer haritası fazlasıyla yeterlidir. Hazır bir servise ihtiyaç duyduğun nokta genellikle kullanıcı bazlı hedefleme, denetim kaydı ve teknik olmayan ekiplerin de bayrak açıp kapatması gerektiğinde başlar. Önce en basitiyle başla, ihtiyaç somutlaştığında büyüt.
Feature flag performansı yavaşlatır mı#
Doğru kurulduğunda ölçülebilir bir etkisi olmaz. Bayrak değerlerini istek başına uzak bir servisten sorgularsan her isteğe ağ gecikmesi eklersin; bunun yerine kısa ömürlü bir bellek önbelleğiyle oku, on saniyelik bir tazeleme çoğu senaryoda yeterlidir. Asıl performans riski bayrağın kendisi değil, koşullu kodun kontrolsüz çoğalmasıyla oluşan karmaşıklıktır.
Kaç tane feature flag fazla sayılır#
Mutlak bir sayı yoktur ama geçici yayın bayraklarının sayısı sürekli artıyorsa borç birikiyor demektir. Sağlıklı bir kodda aktif yayın bayrağı sayısı, aynı anda geliştirilen özellik sayısı kadar olmalı ve her yayın döngüsünde birkaçı kaldırılmalıdır. Kalıcı işletme bayrakları ve kill switch'ler bu sayıma dahil değildir; onların uzun ömürlü olması normaldir.
Feature flag ile A/B testi aynı şey mi#
Aynı altyapıyı kullanırlar ama amaçları farklıdır. Feature flag riski yönetmek için vardır: özelliği açar, sorun çıkarsa kapatırsın. A/B testi ise iki farklı davranışı ölçüp hangisinin daha iyi sonuç verdiğini istatistiksel olarak karşılaştırmak için vardır ve belirli bir örneklem büyüklüğü, süre ve analiz gerektirir. Bir bayrak her ikisine de hizmet edebilir, ama A/B testinde ölçüm disiplini şarttır.
Bayrak kapalıyken kod yine de canlıya çıkmalı mı#
Evet, feature flag'in tüm amacı budur. Tamamlanmamış özelliğin kodu ana dalda ve canlıda durur ama kapalıdır. Böylece uzun ömürlü dallar ve büyük birleştirme kavgaları ortadan kalkar, her değişiklik küçük parçalar halinde sürekli entegre olur. Tek koşul, kapalı yolun mevcut davranışı hiçbir şekilde etkilememesidir.
Bayrağı ne zaman kaldırmalıyım#
Özellik tüm kullanıcılara açıldıktan ve birkaç yayın döngüsü boyunca sorunsuz çalıştıktan sonra, geri dönme ihtimalinin gerçekçi olmaktan çıktığı anda. Pratikte bu genellikle bir ile dört hafta arasıdır. Kaldırırken koşulu, artık ulaşılamayan eski kod yolunu ve o yola ait testleri birlikte temizle; yarım kalan temizlik, ölü kodu kalıcı hale getirir.
Kapanış#
Feature flag, yayın gününü riskli bir olay olmaktan çıkarıp sıradan bir işleme dönüştürür; çünkü kodu taşımak ile özelliği açmak artık iki ayrı karardır. Aklında kalması gereken dört alışkanlık şunlar: bayrağı okuyan tek bir yer bırak, yüzdelik açmayı kararlı bir özetle yap, her dış bağımlılığın arkasına bir kill switch koy ve her geçici bayrağa doğduğu anda bir son kullanma tarihi yaz. Bu dördü olmadan bayraklar bir süre sonra çözdükleri sorundan büyük bir karmaşa üretir.
Bayrak deposunu, yayın hattını ve gözlemlemeyi kendi altyapında kurmak istiyorsan VDS ve bulut sunucu paketlerimiz tam root erişimiyle esneklik verir. Kurulum ve bakım işini devretmeyi tercih edersen sunucu yönetimi hizmetimiz süreci üstlenir; uygulamanı dış saldırılara karşı korurken kill switch'lerine ek bir katman istiyorsan WAF çözümümüze göz atabilirsin.