Invariant Driven Development

Yapay zeka ile sağlıklı ve istediğimiz yapıda uygulamalar geliştirebilmek için kullanabileceğimiz iki yöntemden bahsetmek istiyorum:

1.) Spec Driven Development

2.) Invariant Driven Development

Bir sonraki yazımda Spec Driven Development nedir ve nasıl yapılır konusuna değineceğim.

Bu yazının konusu ise IDD: Invariant Driven Development.

Testlerden başlayalım. Özellikle de birim (unit) testlerinden.

Klasik birim testlerinde test edilen birimin dış dünya ile olan ilişkileri çoğu zaman mocklanır. Test sonunda da bu mockların beklediğimiz şekilde kullanılıp kullanılmadığı verify edilir.

Sorun tam olarak burada başlar.

Bu noktadan itibaren testi sistemin davranışına değil, implementasyona bağlamış oluyoruz.

Örneğin bir para transferini test ettiğimizi düşünelim. transfer() metodunun çağrıldığını, ardından müşteri nesnesindeki değişikliklerin kalıcı hale gelmesi için customer.save() metodunun çalıştığını ve sonrasında gerekli diğer metotların belirli bir sırada çağrıldığını kontrol ediyoruz.

Peki burada gerçekte neyi test ediyoruz?

Para transferinin doğru yapılıp yapılmadığını mı?

Yoksa bizim o para transferini bugün nasıl implemente ettiğimizi mi?

Çoğu zaman ikincisini.

Bu tür testler işletme mantığından ziyade implementasyon detaylarını garanti altına alırlar. Bunun doğal sonucu olarak implementasyon değiştiğinde test de kırılır.

Halbuki sistemin davranışı değişmemiş olabilir.

Yapay zeka çağında bunun başka bir maliyeti daha var.

Bu testler yapay zekanın kodu özgürce refactor etmesini zorlaştırırlar. Ajan yaptığı her yapısal değişiklikte yüzlerce testin neden kırıldığını anlamak, mevcut mock yapılarını okumak ve yeni implementasyona adapte etmek zorunda kalır. Bu da gereksiz token, zaman ve context harcaması anlamına gelir.

Bizim istediğimiz şey başka.

Yapay zekaya:

“Bu kodu böyle yaz.”

demek yerine:

“Bu sistem hiçbir koşul altında şu kuralları ihlal edemez.”

demek istiyoruz.

Implementasyon alanını mümkün olduğunca yapay zekaya bırakırken, sistemin davranış sınırlarını kendimiz belirlemek istiyoruz.

İşte burada Invariant Driven Development (IDD) devreye giriyor.

Invariant nedir?

Invariant, sistem hangi işlemden veya kod yolundan geçerse geçsin doğru kalması gereken bir koşuldur.

Örneğin:

Bir sipariş için yapılan toplam iade tutarı, siparişin ödenmiş tutarından büyük olamaz.

Bu bir implementasyon detayı değildir.

Bu kuralın uygulanabilmesi için sistemin hangi sınıfı, hangi metodu, hangi repository’yi veya hangi algoritmayı kullandığı bizi ilgilendirmez.

Yapay zeka ister RefundService oluştursun, ister bunu aggregate içerisinde çözsün, ister mevcut yapıyı tamamen refactor etsin.

Tek bir şartımız var:

Bu invariant hiçbir zaman ihlal edilemez.

Başka örnekler verelim:

– Stok miktarı izin verilen durumlar dışında negatif olamaz.

– Tamamlanmış bir ödeme ikinci kez tamamlanamaz.

– İptal edilmiş bir sipariş tekrar teslim edilmiş duruma geçirilemez.

– Bir ödeme için yapılan toplam refund, tahsil edilen tutarı aşamaz.

– Aynı ödeme işlemi aynı idempotency key ile ikinci kez gerçekleştirilemez.

– Bir kullanıcının erişim yetkisi olmayan işletmenin verisine ulaşması mümkün değildir.

Bunların hiçbirisi bize kodun nasıl yazılması gerektiğini söylemiyor. Bize sistemin hangi sınırlar içerisinde davranması gerektiğini söylüyor.

Aradaki fark çok önemli.

Peki invariantları nasıl bulacağız?

IDD yapabilmek için öncelikle sistemimizde geçerli olan invariantları keşfetmemiz gerekiyor.

Bunun için uygulamayı geliştirmeye başlamadan önce yapay zeka ile kapsamlı analiz seansları yapılması gerektiğini düşünüyorum.

Birinci adımda uygulamanın DDD (Domain Driven Design) ile ana omurgası oluşturulur. Domain nedir? Aggregate’ler nelerdir? Entity’ler nelerdir? Value Object’ler nelerdir? Aggregate sınırları nerede başlar, nerede biter?

İkinci adımda bu domain içerisinde her zaman doğru kalması gereken kurallar araştırılır.

Burada yapay zekaya doğrudan şu soruyu bile sorabiliriz:

“Bu domain içerisinde ihlal edilmesi mümkün olmaması gereken tüm invariantları ve bunların edge-case varyantlarını çıkart.”

İşte o noktada invariantlar kendilerini göstermeye başlarlar.

Üçüncü adımda bunları örneğin bir INVARIANTS.md dosyasına kaydederiz.

Invariantlar mümkün olduğunca doğal dilde, açık, yoruma kapalı ve test edilebilir şekilde tanımlanmalıdır.

Örneğin:

INV-PAYMENT-001

Bir payment için başarılı refund işlemlerinin toplam tutarı, ödeme ile alinmis tutarını hiçbir zaman aşamaz.

Ardından bunun varyantlarını tanımlarız:

– Partial refund yapılabilir.

– Birden fazla partial refund yapılabilir.

– Son partial refund kalan tutar kadar olabilir.

– Başarısız refund toplam tutara dahil edilmez.

– Concurrency durumunda iki refund işlemi aynı anda bu invariantı ihlal edemez.

– Retry işlemi ikinci bir refund oluşturmamalıdır.

Böylece tek bir cümleden bütün bir davranış alanı ortaya çıkmaya başlar.

Ve burada çok önemli bir paradigma değişikliği gerçekleşiyor:

Test artık başlangıç noktası değil, invariantın türevidir.

Biz yüzlerce test tarif etmiyoruz. Biz sistemin değişmezlerini tarif ediyoruz. Hangi testlerin gerekli olduğunu, bu invariantların hangi seviyede test edilmesi gerektiğini ve hangi edge-case’lerin oluşturulması gerektiğini yapay zeka çıkartabilir.

Böylece insanın görevi test kodu yazmak ya da yapay zekaya hangi metodun kaç kere çağrılması gerektiğini anlatmak olmaktan çıkar.

İnsanın görevi şudur:

Sistemin hangi koşullar altında dahi bozulmaması gerektiğini tanımlamak.

Yapay zekanın görevi ise bu sınırlar içerisinde istediği implementasyonu oluşturmak, gerektiğinde refactor etmek ve invariantların hala geçerli olduğunu sürekli olarak ispatlamaktır.

IDD’nin benim için en önemli tarafı da tam olarak burada. Implementasyonu serbest bırakıyoruz, davranışı değil.

📢 Önemli Bağlantılar
▶️ YouTube Kanalım En yeni videoları kaçırmamak için abone olun Kanalı Ziyaret Et →
📘 Yapay Zeka Çağında Yazılım Geliştirme Ücretsiz e-kitabı PDF olarak indirin ve okuyun PDF'i İndir →