Feature flag teknik borca nasıl dönüşür?
Tek bir müşteri için açılan feature flag uzun süre kodda kalır. Tenant bazlı flag'lerin bedeli, temizliği ve yeni flag açarken sorulacak üç soru.

4 dk okumaGüncellendi:

İçindekiler
Bir tenant’ı mutlu etmek için açılmış bir flag’imiz vardı. Ürün o tenant’ta biraz farklı çalışsın diye veritabanındaki ayar tablosuna bir satır eklenmiş, kodda da o satıra bakan bir if yazılmıştı. Sonraki her geliştirmede o if’i bozmamak için kulağı tersten tuttuk. Yeni özellik o tenant’ta nasıl davranacak, eski yol hâlâ çalışıyor mu, testte o tenant’ın ayarını da açtık mı? Sonra bir gün öğrendik ki o tenant ürünü kullanmayı çoktan bırakmış.
Feature flag, kodu yeniden yayınlamadan bir davranışı açıp kapatan anahtardır. Bu yazının iddiası şu: tek bir müşteri için açılan flag’in bir sahibi ve bir bitiş tarihi olmaz; bu yüzden kapanmaz ve zamanla teknik borca dönüşür.
Feature flag nedir, neden kapanmaz?
En basit hali bir config değeri; bizde olduğu gibi veritabanında tenant bazlı bir ayar da olabilir: “bu tenant’ta yeni ekran açık, diğerlerinde kapalı.”
Martin Fowler’ın feature toggle yazısı flag’leri ömürlerine göre ayırır. Bir özelliği yarım haliyle canlıya almak için açılan release toggle birkaç hafta yaşar ve özellik herkese açılınca silinir. Bir müşteriye ya da pakete özel davranışı açan permissioning toggle ise uzun ömürlüdür; aslında bir ürün kararıdır.
Sorun, tenant’a özel flag’in ikisinin arasında kalması. Geçici bir ihtiyaç için açılır (“şimdilik bu müşteride böyle olsun”), ama kalıcı bir ayar gibi saklanır. Kapatılacağı bir tarih yoktur, kapatma kararını verecek bir sahibi de yoktur. Ayar tablosunda bir satır olarak durur ve kimse onu silmeye cesaret edemez, çünkü kimin kullandığını bilen yoktur.
Bedeli kodda ödenir
Her flag kodda iki yol demek. İki flag dört kombinasyon, üç flag sekiz. Bunların hepsi test edilmez; genelde varsayılan yol test edilir, flag’li yol “eskiden çalışıyordu” diye kalır.
Asıl maliyet yeni geliştirmede ortaya çıkıyor. Bizdeki flag tek bir tenant içindi ama o tenant’ın yolu her yeni işin içine girdi. Tasarımı o yolu bozmayacak şekilde eğip büktük. Sonunda kodun bir kısmı, artık kimsenin kullanmadığı bir davranışı korumak için vardı.
Tenant’ın ürünü bıraktığını da sonradan öğrendik. O dalın en son ne zaman çalıştığını gösteren bir şey yoktu.
Temizlik
Bu olaydan sonra feature flag temizliği yaptık ve tenant’a özel davranışların hepsini ortak davranışa çektik: her tenant aynı yoldan geçiyor. Flag’li dallar koddan silinince, her yeni işte sorulan “o tenant’ta ne olur?” sorusu da ortadan kalktı.
Flag’li dalı silmeden önce bugünkü davranışını testlerle kilitlemek işe yarar; bunun adı characterization test.
Yeni bir flag açarken
Temizlik bir kez yapılınca bitmiyor; yeni flag’ler aynı yoldan gelir. Bir flag eklerken şu üç soruya cevap verilmiş olmalı:
- Sahibi kim? Kapatma kararını verecek kişi ya da ekip. Sahibi olmayan flag sonsuza kadar yaşar.
- Ne zaman silinecek? Geçici bir flag’in son kullanma tarihi olmalı. Fowler’ın yazısında anlatılan bir yöntem, tarihi geçen flag’de testi kırmak.
- Kim kullanıyor? Flag’li yol çalıştığında bunu tenant bilgisiyle loglamak ya da saymak. Son 90 günde hiç çalışmamış bir dal, silinmek için en güçlü argümandır.
İkinci maddenin .NET’teki basit bir hali:
public sealed record FeatureFlag(string Ad, string Sahip, DateOnly SonKullanma);
public class FlagTarihleriTesti
{
[Fact]
public void Suresi_gecmis_flag_kalmasin()
{
var bugun = DateOnly.FromDateTime(DateTime.UtcNow);
// FlagKatalogu: flag'lerin tutulduğu yer (ayar tablosu, config)
var gecmis = FlagKatalogu.Tumu
.Where(f => f.SonKullanma < bugun)
.Select(f => $"{f.Ad} ({f.Sahip})");
Assert.Empty(gecmis);
}
}
Test kırıldığında iki seçenek var: flag’i silmek ya da tarihi bilerek uzatmak. İkisi de bir karar; önemli olan, flag’in kendiliğinden yaşamaya devam etmemesi.
Bizim hikâyemizde bu üç sorudan yalnızca üçüncüsü sorulmuş olsaydı, o tenant’ın gittiğini çok daha önce öğrenirdik.
Sık sorulanlar
Feature flag teknik borç mudur?
Kendisi değil, sahipsiz kalanı. Release toggle birkaç hafta yaşar ve özellik herkese açılınca silinir. Tek bir müşteri için açılan flag’in ise sahibi ve bitiş tarihi olmaz; her yeni geliştirmede “o tenant’ta ne olur” sorusunu doğurur ve kodda artık kimsenin kullanmadığı bir yolu korur. Borca dönüşen bu.
Tenant bazlı flag ne zaman silinmeli?
Kullanılmadığı belli olduğunda; bunu bilmek için flag’li yol çalıştığında tenant bilgisiyle saymak gerekir. Son 90 günde hiç çalışmamış bir dal, silinmek için en güçlü argümandır. Bizim hikâyemizde bu sayılsaydı tenant’ın ürünü bıraktığını çok daha önce öğrenirdik.
Tarihi geçen flag’i otomatik nasıl yakalarım?
Her flag’e bir son kullanma tarihi ve sahip ver, sonra tarihi geçenlerde kırılan bir test yaz: katalogdaki flag’leri bugünle karşılaştırıp süresi dolanları Assert.Empty ile listeleyen bir xUnit testi yeter. Test kırıldığında iki seçenek kalır: flag’i silmek ya da tarihi bilerek uzatmak; ikisi de bir karar.
Benzer yazılar

12 dk
Outbox pattern nedir? .NET ve EF Core ile
Outbox pattern nedir? RabbitMQ yokken EF Core tablosu ve .NET BackgroundService ile transactional outbox: çift işleme, retry, para çıkışı.

6 dk
IMemoryCache mi, HybridCache mi?
IMemoryCache çok sunucuda tutarsız kalıyor ve cache boşalınca veritabanını yoruyor. HybridCache bunu ne kadar çözüyor? Ölçüm ve gizli bir maliyet.

13 dk
Idempotency nedir? Ödemede para iki kez çekilmesin
Idempotency, aynı istek tekrar gelse de sonucun değişmemesidir. Ödemede çift tıklamaya ve banka timeout'una karşı .NET'te idempotency key ile çözüm.