2026-08-15uretim4 dk

Özellik bayrakları (feature flag) küçük ekipte gerekli mi?

Küçük ekipte bayrak gerekli, ama satıcıdan alınan hâliyle değil. Üç bayrak türünün ayrımı, elli satırlık bir uygulama ve süresi dolduğunda kırılarak sizi karar vermeye zorlayan bir test.

Özellik bayrağı denince akla önce büyük ekipler geliyor: yüzdelik dağıtım, hedef kitle segmentasyonu, deney panelleri. Üç kişilik bir ekipte bunların hiçbirine ihtiyacınız yok. Ama bayrağın kendisine ihtiyacınız olabilir. Soru "gerekli mi" değil, "hangi tür bayrak gerekli".

Üç farklı şey aynı isimle anılıyor

Karışıklığın kaynağı, birbirinden çok farklı üç mekanizmanın tek terimle anılması.

Yayın bayrağı, yarım kalmış bir işi ana dala göndermenizi sağlar. Kod üretimde durur ama kapalıdır. Ömrü kısadır, tamamlanınca silinir.

Acil kapatma anahtarı, canlıda bir şey patladığında yeniden dağıtım yapmadan devre dışı bırakmanızı sağlar. Ömrü uzundur, çünkü riskli entegrasyon durdukça durur.

Deney bayrağı, kullanıcıları gruplara ayırıp ölçüm yapmanızı sağlar. Küçük ekiplerin çoğunda istatistiksel olarak anlamlı sonuç çıkaracak trafik yoktur; bu bayrak türü genellikle erken alınmış bir karmaşıklıktır.

Küçük ekipte ilk ikisi işe yarar. Üçüncüsünü, kullanıcı sayınız gruplara bölünmeye elverişli hâle gelene kadar ertelemek makul.

Minimum uygulanabilir bayrak

Bir SaaS ürününe abone olmadan önce, bayrağın gerçekte ne olduğunu görmek yeterli: bir koşul, bir kaynak, bir de kaldırma tarihi.

// flags.ts
type Bayrak = {
  aciklama: string;
  varsayilan: boolean;
  kaldirilacak: string; // ISO tarih
};

export const BAYRAKLAR = {
  yeni_arama: {
    aciklama: "Postgres FTS yerine yeni arama katmanı",
    varsayilan: false,
    kaldirilacak: "2026-10-01",
  },
  toplu_fatura: {
    aciklama: "Toplu fatura dışa aktarımı",
    varsayilan: false,
    kaldirilacak: "2026-09-15",
  },
} as const satisfies Record<string, Bayrak>;

export type BayrakAdi = keyof typeof BAYRAKLAR;

export function acikMi(ad: BayrakAdi): boolean {
  const env = process.env[`FLAG_${ad.toUpperCase()}`];
  if (env !== undefined) return env === "1";
  return BAYRAKLAR[ad].varsayilan;
}

Ortam değişkeni kaynağı ilk aşama için yeterli. Değiştirmek yeniden dağıtım gerektirir, ki bu çoğu küçük ekipte birkaç dakikalık iştir. Yeniden dağıtım yapmadan kapatabilmek gerçekten gerekiyorsa, kaynağı bir tabloya ya da bir anahtar-değer deposuna taşıyıp önüne kısa ömürlü bir önbellek koyarsınız. Yapının geri kalanı değişmez.

Asıl maliyet bayrağı açmak değil, kaldırmak

Bayrakların küçük ekiplerde kötü ün kazanmasının sebebi, birikmeleri. Altı ay sonra kimsenin ne yaptığını bilmediği on iki bayrak, iki bin satırlık ölü kod ve test edilmemiş kombinasyonlar demek.

Bunun tek çözümü kaldırma tarihini kodun içine yazmak ve testin bunu zorlamasıdır:

// flags.test.ts
import { BAYRAKLAR } from "./flags";

test("süresi geçmiş bayrak kalmadı", () => {
  const bugun = new Date().toISOString().slice(0, 10);
  const gecmis = Object.entries(BAYRAKLAR)
    .filter(([, b]) => b.kaldirilacak < bugun)
    .map(([ad, b]) => `${ad} (${b.kaldirilacak})`);

  expect(gecmis).toEqual([]);
});

Bu test bir gün kırmızıya döner ve sizi ya bayrağı silmeye ya da tarihi bilerek uzatmaya zorlar. İkisi de karar vermektir; unutmak değildir. Acil kapatma anahtarları için tarihi ilgili entegrasyon yaşadığı sürece uzatmak meşru — yeter ki bu uzatma bir commit olarak görünsün.

Test kombinasyonları çarpar, planlayın

İki bayrak dört durum demek, üç bayrak sekiz. Hepsini test etmezsiniz, etmemelisiniz de. Pratik kural: aynı anda açık olabilecek bayrak sayısını iki ile sınırlayın ve testleri yalnız iki senaryo için yazın — üretimdeki mevcut durum ve hedeflenen durum. Ara kombinasyonlar geçici olduğu için test yatırımını hak etmez.

Bayrağı kodun mümkün olan en dış katmanında okuyun. İş mantığının içine serpiştirilen acikMi() çağrıları, silme zamanı geldiğinde işin zorlaştığı yerdir:

// iyi: tek karar noktası
const arama = acikMi("yeni_arama") ? yeniArama : eskiArama;
export const sonuclar = await arama(sorgu);

Bayrağı nerede okuyacağınız da bir karar

Aynı bayrağın hem sunucuda hem tarayıcıda okunması gerektiğinde, iki taraf farklı cevap verebilir. Sunucu kapalı sanırken arayüz açık görünür, kullanıcı var olmayan bir uca istek atar. Bunu engellemenin en ucuz yolu, kararı tek yerde vermek ve sonucu aşağı doğru taşımaktır:

// sunucu tarafında bir kez okunur, istemciye veri olarak iner
export async function sayfaVerisi() {
  return {
    kullanici: await kullaniciGetir(),
    bayraklar: { yeni_arama: acikMi("yeni_arama") },
  };
}

İstemci acikMi() çağırmaz, kendisine gelen bayraklar nesnesini okur. Böylece bayrak kaynağını ortam değişkeninden veritabanına taşıdığınızda istemci kodunda hiçbir şey değişmez, ve iki taraf asla farklı düşünmez.

Bayrağın yanlış araç olduğu yerler

Veritabanı şema göçlerini bayrakla yönetmeyin. Bayrak iki kod yolunu aynı anda yaşatır; şema tek hâlde olur. Şema değişikliği için genişlet-taşı-daralt sırası doğru araçtır, bayrak değil.

Yarım kalmış bir işi haftalarca ana dalda kapalı tutmak da genelde kötü sinyaldir. Bayrak, işi küçük parçalara bölme disiplininin yerine geçmez; o disiplinin işe yaramasını sağlayan araçtır.

Karar

Küçük ekipte özellik bayrağı gereklidir, ama üründen satın alınan hâliyle değil. Elli satırlık bir modül, bir ortam değişkeni ve süresi dolduğunda kırılan bir test — ihtiyacınızın tamamı büyük ihtimalle bu. Bayrak sayınız sürekli beşin üstünde kalmaya başladığında ya da yeniden dağıtım yapmadan kapatma ihtiyacı haftada birden fazla doğduğunda, hazır bir çözüme geçmeyi tartışın. Ondan önce tartışmaya değmez.