İçeriğe geç
Muhammet Şafak
en
Günlük 3 dk okuma

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ısal cy.wait(2000), usleep, sleep(<n>) — Go, Java/Kotlin, C#, Python, JS/TS, Cypress, PHP.
  • Seed’siz rastgelelik (low): Math.random, Python random.*, Go math/rand globalleri (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.

Paylaş:

Yorumlar

Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi