CommitBrief'e flaky-test dedektörü: AI'ın yazdığı kararsız testleri commit'te durdurmak
CommitBrief'e deterministik bir flaky-test süzgeci ekledim. LLM'e sormak yerine, değişen test dosyalarındaki sabit sleep'leri ve seed'siz rastgeleliği commit aşamasında — modelsiz — yakalıyor. Niye LLM prompt'u değil de statik analiz seçtiğimi anlatıyorum.
CommitBrief bugüne kadar tek bir şey yapıyordu: kendi diff’imi, kimse görmeden, bir LLM’e inceletmek. İlk yazıda niye işe yaradığını, summary ve remote yazısında ekibe nasıl taşındığını anlatmıştım. Hepsinin ortak noktası: bulguyu model üretiyordu.
Bu yazıdaki ekleme o noktayı bilerek kırıyor. CommitBrief artık modeli çağırmadan önce, değişen test dosyalarını deterministik bir statik süzgeçten geçiriyor — kararsızlığa (flaky) en sık yol açan kalıpları yakalamak için. Niye bu ekleme LLM’e bir madde daha eklemekle yapılmadı; asıl yazı o.
Kararsızlığın neden AI çağında patladığını ve neden retry/quarantine’in bir koşu bandı olduğunu sade.dev’de ayrı yazdım: Kararsız Testler: Retry Çözüm Değil. Bu yazı o tezin araç tarafı.
Kolay yol LLM’di — onu seçmedim
CommitBrief baştan sona LLM-güdümlü bir araç. En kolay yol belliydi: prompt’a “test dosyalarında sabit sleep, kırılgan selector, seed’siz random ararsan işaretle” diye bir rubrik daha eklemek. Bir akşamlık iş.
Sorun, bu çözümün olasılıksal olması. Modele bağlı, çalıştırmadan çalıştırmaya kayabilir ve — daha önemlisi — farklılaşmıyor: herhangi bir LLM review aracı aynı prompt’la aynı şeyi yapabilir. Oysa kararsızlığın asıl can sıkıcı tarafı, çözümün deterministik olmasını istemen: aynı diff her zaman aynı sonucu versin, model olmadan da çalışsın, commit aşamasında bir saniyede dönsün.
O yüzden statik yolu seçtim. Yeni bir paket (internal/flaky), değişen test dosyalarının yalnızca eklenen satırlarını tarıyor, bilinen anti-pattern’leri muhafazakâr regex’lerle yakalıyor ve standart bir bulgu üretiyor. Aynı diff → aynı bulgu. Model yok, network yok.
Ne yakalıyor
İlk sürümde iki yüksek-isabetli kural var. İsabet, recall’dan önce geliyor: commit aşamasındaki gürültülü bir kapı yok sayılır, yok sayılan kapı da hiç olmamasından kötüdür.
- Sabit bekleme / sleep (
medium):time.Sleep,Thread.sleep,Task.Delay,asyncio.sleep,*.waitForTimeout, sayısalcy.wait(2000),usleep,sleep(<n>)— Go, Java/Kotlin, C#, Python, JS/TS, Cypress, PHP. - Seed’siz rastgelelik (
low):Math.random, Pythonrandom.*, Gomath/randgloballeri (rand.Intn, …).
Test dosyasını yoldan tanıyor (*_test.go, *.test.*/*.spec.*, test_*.py, *Test.java, *_spec.rb, __tests__/e2e/cypress dizinleri…). Bulgu metni en/tr yerelleşiyor. Çıktı, modelin bulgularıyla aynı kart:
● medium worker/job_test.go:41
Test senkronizasyon için sabit bir bekleme (sleep) kullanıyor
Sabit beklemeler testi zamana bağımlı kılar: hızlı makinede geçer, CI
yükü altında patlar; süite olan güveni aşındıran flaky sonuçlar üretir.
→ Sabit gecikmeyi koşula dayalı bir beklemeyle değiştir…
Modelin bulgularıyla nasıl birleşiyor
Statik bulgular, filtreden sonra hesaplanıp LLM bulgularıyla merge ediliyor — render, --fail-on ve --copy’den önce. İki kural:
- Her LLM bulgusu korunur. Statik süzgeç hiçbir şeyi bastırmıyor.
- Aynı
file:line’da çakışma olursa statik bulgu eklenmiyor — modelin o satır için zaten söyleyecek sözü varsa, ikinci kez söylemiyoruz.
Güzel bir yan etki: model bozuk JSON döndürüp degrade olsa bile statik bulgular hayatta kalıyor. Deterministik kısım, olasılıksal kısma bağımlı değil.
Ve JSON şeması v1 değişmedi. Statik bulgular mevcut render.Finding şeklini kullanıyor; yeni alan yok, yeni exit-code yüzeyi yok. --fail-on=medium artık sabit bir sleep’te de kapıyı kapatıyor — ekstra bir flag öğrenmene gerek kalmadan.
Kullanımı
API/mock sağlayıcılarda varsayılan açık. Kapatmak istersen:
commitbrief # flaky pre-pass açık (varsayılan)
commitbrief --no-flaky # bu çalıştırmada atla
commitbrief config set review.flaky false # kalıcı kapat
Öncelik --no-flaky > review.flaky > yerleşik (true). CLI-tabanlı (claude-cli/gemini-cli) sağlayıcılar düz metin ürettiği için bu yolda şimdilik devre dışı — onu ayrı bir adımda ele alacağım.
”Yerini almak” değil, yine “önünden gitmek”
Bu ekleme de serinin aynı cümlesinin etrafında: üretim ucuzladıysa, üretileni denetlemek pahalı kalmamalı. AI test yazmayı bedavalaştırdı; o testlerin bariz kararsızlığını yakalamak da bedava olmalı — bir model çağrısı, bir CI dakikası harcamadan, commit’in olduğu yerde.
Burada model bir lükse dönüşüyor, zorunluluğa değil: bariz olanı deterministik kısım topluyor, modelin dikkatini gerçekten muhakeme isteyen şeylere saklıyorum.
Sınırlar — yine dürüst liste
- İsabet-öncelikli. Bariz anti-pattern’leri yakalar; kendi kodunun içine gömülü bir race condition’ı değil. Akıllı kararsızlığı kaçırır ve tahmin etmesine izin vermiyorum.
- CLI sağlayıcı yolu kapsam dışı. Düz metin üreten sağlayıcılarda merge henüz yok; takip adımı.
- Statik, AST değil (henüz). Regex tabanlı; kapsamı/semantiği görmüyor. Brittle selector, over-mock ve zaman-bağımlı assertion kuralları sıradaki additive eklemeler.
- Tespit emniyet ağı, çözüm değil. Asıl çözüm testi en baştan deterministik yazmak — onu sade.dev yazısında anlattım.
Denemek için
Bir test dosyasına sabit bir sleep koyup commitbrief’i çalıştırın — modelden önce, deterministik olarak yakalandığını görün. Hangi anti-pattern’i bir sonraki sürümde görmek istediğinizi yazarsanız, sıraya koyarım.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.