AI Junior Geliştiricileri Ortadan Kaldırmadı. Fake Senior Kod Üretti.

Bariz şekilde bozuk kodu reddetmek kolaydır. Daha zor olan, düzenli görünen ama üretim kurallarını atlayan koddur.

Bu çalışma küçük bir Laravel projesi kullanıyor. Bir kullanıcı başka bir kullanıcıya bakiye gönderiyor. Aynı özellik iki kez yazıldı: biri temiz görünen ama eksik sınırları olan servis, diğeri validation, authorization, transaction, row lock, failure records, logs ve testleri açıkça uygulayan servis.

Çalışma komutu iki servisi de 16 senaryodan geçiriyor. Fake servis 9 senaryoyu geçti, 7 senaryoda kaldı ve 100 üzerinden 51.7 aldı. Gerçek servis 16 senaryonun tamamını geçti ve 100 aldı.

İddia

Sorun AI'ın kötü kod üretebilmesi değil. Kötü kod AI'dan önce de vardı. Buradaki risk, kodun üretim kurallarını karşılamadan tamamlanmış gibi görünmesi.

Bu projedeki fake implementasyon saçma yazılmış bir örnek değil. Service class var. Ortak result object var. İsimler okunabilir. Amount numeric mi diye bakıyor. Negatif amount reddediliyor. Başarılı işlem için transaction kaydı yazılıyor. Happy path çalışıyor.

Ama cüzdan transferi için bu yeterli değil. Harcama yetkisi kontrol edilmeli. Sıfır amount reddedilmeli. Kullanıcının kendisine transfer engellenmeli. Failure kaydı bırakılmalı. Sender ve receiver wallet güncellemeleri tek database transaction içinde yapılmalı.

Dış Bağlam

AI coding araçlarıyla ilgili sonuçlar tek yönde değil. GitHub Copilot productivity study, sınırlı bir JavaScript HTTP-server görevinde daha hızlı tamamlama raporladı. Buna karşılık METR'nin deneyimli open-source geliştiricilerle yaptığı çalışma, geliştiricilerin bildikleri repositorylerde AI araçları açıkken daha yavaş tamamladığını buldu.

Stack Overflow 2025 AI Developer Survey, AI çıktısının doğruluğuna güvenmeyen geliştiricilerin güvenenlerden fazla olduğunu gösterdi. DORA 2025 State of AI-assisted Software Development report da benzer bir noktaya gelir: sonuç sadece araçla değil, aracın çalıştığı mühendislik sistemiyle ilgilidir.

Bu proje daha dar bir soruya bakıyor: Tek bir Laravel servisi 16 açık backend kontrolünden geçiyor mu?

Proje Yapısı

Projenin adı fake-senior-code-laravel-wallet. Çalıştırılabilir bir Laravel uygulaması. İçinde migrations, models, services, policy, tests, scoring helper ve Artisan command var.

Domain, Laravel'in varsayılan users tablosunu ve iki ek tabloyu kullanıyor:

  • wallets: her kullanıcı için bir cüzdan ve decimal balance.

  • wallet_transactions: başarılı veya başarısız her transfer denemesi için kayıt.

İki servis de aynı public method'u kullanıyor:

public function transfer(
    User $actor,
    int $senderId,
    int $receiverId,
    mixed $amount
): TransferResult;

Karşılaştırma bu yüzden net. Aynı input. Aynı beklenen davranış. Fark implementasyonda.

Cüzdan Transfer Kontratı

Bu benchmark tam bir ödeme sistemi modellemiyor. Basit bir cüzdan transferinde eksik olmaması gereken backend kurallarını kontrol ediyor.

ÖğeKural
actorSender wallet üzerinden harcama yetkisi olmalı.
sender_idVar olan ve yeterli bakiyesi olan bir wallet'a işaret etmeli.
receiver_idVar olan bir wallet'a işaret etmeli ve sender ile aynı olmamalı.
amountNumeric, sıfırdan büyük ve iki decimal hassasiyetinde olmalı.
Balance updateDebit ve credit tek database transaction içinde yapılmalı.
Wallet rowsYeni balance hesaplanmadan önce satırlar lock edilmeli.
Failure pathBaşarısız denemeler reason ile kaydedilmeli.

İki Servis

FakeSeniorTransferService ilk bakışta kabul edilebilir görünür. Amount numeric mi diye bakar, negatif amount'u reddeder, sender ve receiver wallet'ları bulur, balance kontrol eder, bakiyeleri günceller ve başarılı transaction kaydı yazar.

Eksikler burada başlıyor:

  • Authorization yok. Bir actor başka kullanıcının wallet'ından harcayabilir.

  • 0.00 başarılı transfer kabul edilir.

  • Kullanıcının kendisine transferi serbesttir ve balance bozulabilir.

  • DB::transaction yok.

  • lockForUpdate yok.

  • Birçok failure case wallet_transactions tablosuna yazılmaz.

RealSeniorTransferService aynı özelliği açık kurallarla uygular: amount normalization, self-transfer rejection, actor ownership, wallet policy, wallet existence checks, transaction wrapper, row locking, failed transaction rows ve warning logs.

Senaryo Protokolü

php artisan wallet:fake-senior-study komutu her case için yeni users ve wallets oluşturur. Sonra iki servisi aynı senaryodan geçirir ve sonucu skorlar.

Ölçülen senaryo sayısı:

S = 16

Ana senaryolar:

  • successful transfer

  • insufficient balance

  • transfer to self

  • zero amount

  • negative amount

  • missing sender wallet

  • missing receiver wallet

  • unauthorized transfer attempt

  • repeated transfer attempt

  • transaction history consistency

  • failure reason logging

  • balance unchanged after failed transfer

Skorlama

Production Readiness Score ağırlıklı bir checklist kullanır:

PRS =
0.20 Validation
+ 0.20 Authorization
+ 0.20 Database Consistency
+ 0.15 Failure Handling
+ 0.15 Test Coverage
+ 0.10 Maintainability

Her kategori şu şekilde hesaplanır:

Category Score = Passed Checks / Total Checks x 100

Ölçülen Sonuçlar

Proje yerelde şu komutlarla çalıştırıldı:

php artisan test
php artisan wallet:fake-senior-study --json

Test sonucu: 20 test ve 72 assertion geçti.

ServisGeçtiKaldıSkor
Fake Senior Code9751.7 / 100
Real Senior Code160100 / 100

Ana senaryo sonucu:

SenaryoFakeReal
Happy path resultPASSPASS
Transfer to selfFAILPASS
Zero amountFAILPASS
Negative amountPASSPASS
Insufficient balancePASSPASS
Missing sender walletFAILPASS
Missing receiver walletFAILPASS
Unauthorized transfer attemptFAILPASS
Repeated transfer attemptPASSPASS
Transaction history consistencyFAILPASS
Failure reason loggingFAILPASS
Balance unchanged on failed transferPASSPASS

Bazı Skorlar Neden Aynı?

Fake servis ölçülen database consistency kategorisinde 100% aldı. Bu, production traffic altında güvenli olduğu anlamına gelmez.

Bu kategori sadece sequential case'leri kontrol etti: başarı sonrası balance, insufficient balance, repeated transfer ve failed transfer. Fake servis bunları geçti. Ama hâlâ DB::transaction ve lockForUpdate yok. Bu benchmark aynı sender wallet'a paralel worker çalıştırmıyor.

Sonraki benchmark bu kategoriyi ikiye ayırmalı:

  • sequential consistency

  • concurrency safety

Test coverage ve maintainability de eşit görünüyor çünkü iki servis de aynı method'u açıyor ve TransferResult döndürüyor. Bu önemli: fake senior code yapılı görünebilir, ama yine de kritik kuralları uygulamayabilir.

Sayıları Okumak

Fake servis 16 senaryonun 9'unu geçti. Bu geçişler gerçek. Happy path çalışıyor. Negative amount reddediliyor. Basit insufficient-balance case'i balance'ları değiştirmiyor.

Ship etmek için daha önemli olan failure'lar. Unauthorized transfer çalışıyor. Zero amount başarılı kabul ediliyor. Self-transfer balance'ı bozabiliyor. Missing-wallet failure kaydedilmiyor. Failure reason saklanmıyor. Update path'te transaction veya row lock yok.

Real servis 16 senaryonun tamamını geçti. Bu onu tam bir ödeme sistemi yapmaz. Sadece bu projede tanımlanan kontratı karşıladığını gösterir.

Pratik Notlar

Benchmark SQLite kullanıyor. Bu pattern production'a taşınacaksa locking davranışını ve performansı MySQL veya PostgreSQL üzerinde ayrıca kontrol etmek gerekir.

Concurrency senaryosu tam paralel load testi değil. Real serviste transaction ve row-lock boundary olduğunu doğruluyor, repeated transfer davranışını sequential olarak kontrol ediyor. Sonraki versiyon aynı wallet'a paralel request çalıştırmalı.

Gerçek bir wallet sistemi ayrıca idempotency keys, immutable ledger rows, currency handling, retry behavior, reconciliation ve daha güçlü audit kuralları ister.

Sonuç

Ölçülen sonuç basit: fake servis 16 senaryonun 9'unu geçti ve 51.7 aldı. Real servis 16 senaryonun tamamını geçti ve 100 aldı.

Fake servis iyi bir örnek çünkü her yerde kötü değil. Yapısı var. Basit kontrolleri geçiyor. Test ownership, invalid amounts, failure history ve database boundary sorduğunda düşüyor.

AI destekli backend kodunda service class ve success response yeterli değil. Production kurallarını tanımlamak ve test etmek gerekiyor. Bu Laravel projesinde farkı göstermek için 16 senaryo yeterli oldu.

Daha ayrıntılı incelemek isteyenler için çalışmanın tüm detaylarını içeren PDF dosyasını burada ekledim.