- AI Agents
AI Kod Asistanları Frontend'de "Çalışıyor Ama Yanlış" Kod Üretebiliyor: 6 Örnek
Derleniyor, ekranda görünüyor, PR yeşil geçiyor — ve yine de yanlış olabiliyor.
Zeynep Baş4 dk okuma
1. "Çalışıyor" Frontend'de Yanıltıcı Bir Kelimedir
Backend'de hata patlar, frontend'de sessiz kalır
Backend'de yanlış kod genellikle bir exception fırlatır, bir status code döner, bir log satırı bırakır. Frontend'de öyle değil. Yanlış bir dependency array, gereksiz bir re-render, index'i key olarak kullanmak — hiçbiri hata fırlatmaz. Ekran doğru görünür, ta ki kullanıcı belirli bir sırayla belirli bir şey yapana kadar.
AI'ın ürettiği frontend kodunda sık karşılaşılan durum şu: syntax temiz, isimlendirme makul, bazen yorum satırı bile var. Ama React'in render modeliyle ilgili bir varsayım yanlış olabiliyor. Bunu fark etmek için kodu çalıştırmak yetmiyor, neden öyle çalıştığını anlamak gerekiyor.
2. Örnek 1: key={index} Kullanımı
Liste render eden promptlarda sık görülen bir kısayol
Bir listeyi map'lemesini isteyince AI çoğunlukla key olarak index verir. Statik bir listede bu sorun yaratmaz. Ama sıralanabilir, silinebilir veya filtrelenebilir bir listede component state'i yanlış elemanın üzerinde kalabilir — bir input'a yazılan değer, silinen satırdan sonra başka bir satırda görünebilir.
Bu genelde manuel testte fark edilmiyor, çünkü test senaryosu çoğunlukla "listeyi ekle, ekranda gör" ile sınırlı kalıyor. Silme, yeniden sıralama ve input state'i olan bir satır aynı anda denenmediği sürece sorun ortaya çıkmıyor. Review'da bakılması faydalı olan nokta şu: `key={index}` görülürse, listenin mutasyona uğrayıp uğramadığını kontrol etmek.
3. Örnek 2: useEffect'i Susturmak İçin eslint-disable
Kırmızı çizgiyi kapatmak, sorunu çözmek değildir
"Bu effect sonsuz döngüye giriyor, düzelt" dendiğinde en sık verilen cevap dependency array'i eksiltmek ve üstüne `eslint-disable-next-line react-hooks/exhaustive-deps` eklemek oluyor. Bu döngüyü durdurur ama asıl sorunu çözmez; stale closure kod tabanında kalmaya devam eder.
Buradaki asıl mesele genelde effect'in yanlış şeye bağımlı olması — `onSuccess` her render'da yeniden oluşan bir fonksiyon, ve çözüm onu dependency'den çıkarmak değil, neden her render'da yeniden yaratıldığını sormak. AI genelde en yakın "kod çalışsın" çözümüne gidiyor; kök neden component ağacının üst katmanında olabileceği ve modelin context'inde bulunmadığı için oraya inilmiyor.
4. Örnek 3: Her Şeyi Context'e Taşımak
"Prop drilling kötüdür" cümlesinin yanlış anlaşılması
"Prop drilling'i azalt" dediğinizde AI genelde her state'i bir Context Provider'a taşır. Ama bir Context'in value'su değiştiğinde, o Context'i tüketen her component — memo'lanmış olsa bile — yeniden render olur. Sonuç: 3 seviye prop geçirmek yerine, ilgisiz 15 component'i her tuş vuruşunda yeniden render eden bir yapı.
Buradaki genel kural "state'i mümkün olduğunca aşağıda tut, yukarı sadece gerektiğinde kaldır" şeklinde özetlenebilir. AI bunu bilmiyor değil, ama "prop drilling'i çöz" gibi bir istekte en genel çözüme (global context) gidiyor. "Bu state gerçekten kaç component tarafından paylaşılıyor?" sorusunu sormak hâlâ insanın işi gibi görünüyor.
5. Örnek 4: div onClick — Erişilebilirlik Sessizce Kayboluyor
Görsel olarak buton, semantik olarak değil
"Tıklanabilir bir kart yap" dediğinizde AI çoğu zaman `<div onClick={...}>` üretir, üstüne cursor: pointer ekler. Ekranda tam istediğiniz gibi görünür. Ama klavye ile Tab'layamazsınız, Enter/Space ile tetiklenmez, ekran okuyucu bunu bir buton olarak duyurmaz.
Bu hatanın sinsiliği şurada: code review'da kimse manuel olarak Tab tuşuna basıp erişilebilirliği test etmiyor. Görsel QA'den geçiyor, PR merge oluyor. Aylar sonra bir erişilebilirlik denetiminde ya da gerçek bir kullanıcı şikayetinde ortaya çıkıyor.
6. Örnek 5: Inline Fonksiyon ve Objelerle memo'yu Anlamsızlaştırmak
React.memo yazılmış ama hiçbir işe yaramıyor
AI bir performans sorunu için `React.memo` önerir — doğru refleks. Ama aynı component'e prop olarak inline bir fonksiyon veya obje geçmeye devam eder. Her parent render'ında bu prop yeniden oluşturulduğu için memo'nun referans eşitliği kontrolü hep "farklı" der ve component yine de re-render olur.
Bunun tehlikeli tarafı: kod "performans için optimize edilmiş" görünüyor, memo orada duruyor, review'u geçen kişi "tamam, memo eklenmiş" diyip onaylıyor. Gerçek etkiyi görmek için React DevTools Profiler açıp gerçekten re-render sayısına bakmak gerekiyor — bunu kimse her PR'da yapmıyor.
7. Örnek 6: Veri Çekmeyi Component'e Değil, En Yakın İhtiyaca Göre Konumlandırmamak
"Çalışan" data flow, yanlış sorumluluk sınırı
"Bu sayfaya kullanıcı bilgisini getir" dediğinizde AI genelde en üst sayfa component'inde fetch atıp veriyi prop olarak aşağı geçirir. Sayfa çalışır. Ama altı ay sonra o veriye ihtiyacı olan yeni bir component eklendiğinde, kimse fetch'in nerede olduğunu hatırlamaz ve aynı veri ikinci kez, farklı bir yerden çekilir.
React Query / SWR gibi cache'li fetch kütüphaneleriyle bu artık gerçek bir maliyet olmuyor — aynı query key aynı veriyi paylaşıyor. AI ise genelde "prop geçir" varsayımıyla başladığı için eski, prop-drilling'e dayalı data flow'u üretmeye devam ediyor. "Bu veri nereye ait" sorusunu component ağacına göre değil, sorumluluğa göre cevaplamak faydalı olabiliyor.
8. Peki Sonuç Ne? AI'ı Kullanmayı Bırakmak mı?
Hayır — ama review'un yerini alacağını sanmayı bırakmak gerekiyor
Bu araçlar boilerplate, tekrarlayan CRUD ekranları ve test yazımında zaman kazandırabiliyor. Sorun aracın kendisinden çok "kod çalışıyorsa doğrudur" varsayımı gibi görünüyor. Frontend'de bu varsayım özellikle sorunlu, çünkü çalışan kod ile doğru kod, yukarıdaki örneklerde görüldüğü gibi çoğu zaman birbirinden ayrışabiliyor.
Pratikte işe yarayabilecek bir yaklaşım: AI'ın ürettiği her PR'da üç soruyu sormak — bu liste mutasyona uğrayabilir mi (key kontrolü), bu state kaç component tarafından paylaşılıyor (context/prop kontrolü), bu tıklanabilir eleman klavye ile ulaşılabilir mi (erişilebilirlik kontrolü). Bu üç soru, yukarıdaki hataların büyük kısmını review aşamasında yakalamaya yardımcı olabiliyor.