Yeniden kodu elle yazmaya dönmek istiyorum
(blog.k10s.dev)- k10s, Claude ile yapılan vibe-coding sayesinde hızla geliştirilen, GPU farkındalığına sahip bir Kubernetes TUI'siydi; ancak fleet view eklendikten sonra birçok ekran durumu bozuldu
model.go, 1690 satırlık tek birModelve 500 satırlıkUpdate()ile büyüdü; UI, istemci, önbellek, gezinme ve view durumlarının tamamını üstlenir hale geldi- Yapay zeka özellikleri hızlıca ekledi ama god object ve global key handler yapısını da büyüttü; her yeni view ile mevcut handler'a yeni branch'ler eklenen bir yapıya dönüştü
- Konuma dayalı
[]stringverisi ve arka plandakitea.Cmdiçinde doğrudan mutation yapılması, column hataları ve açık veri yarışları üretebiliyordu - Yeni k10s, Rust ile baştan yazılırken ilk prompt'tan önce interface, message type, ownership rule ve scope'un CLAUDE.md içinde sabitlenmesine karar verildi
k10s'i yeniden yazma kararı neden alındı
- k10s, GPU farkındalığına sahip bir Kubernetes panosu olarak başladı; NVIDIA küme operatörlerinin GPU kullanımını, DCGM metriklerini, boşta duran nodeları ve saatlik
$32/hrmaliyet gibi bilgileri doğrudan görebilmesi için yapılmış bir TUI aracıydı - Go ve Bubble Tea ile yazıldı; yaklaşık 7 ay, 234 commit ve yaklaşık 30 hafta sonu boyunca Claude ile yapılan vibe-coding oturumlarında geliştirildi
- İlk aşamada pods, nodes, deployments, services, command palette, watch tabanlı canlı güncellemeler ve Vim keybindings gibi temel k9s benzeri işlevler yaklaşık 3 hafta sonu içinde çalışır hale geldi
- Temel özellik olan GPU fleet view, her node'un GPU tahsisini, kullanımını, DCGM tabanlı metriklerini, sıcaklık, güç, bellek ve renge dayalı durum bilgilerini gösteren ekrandı; Claude tek seferde
FleetViewstruct'ını, GPU/CPU/All sekme filtrelerini ve allocation bar render işlemini oluşturdu - Ancak fleet view eklendikten sonra
:rs podsile pods view'a dönüldüğünde tablo boşalıyordu, canlı güncellemeler duruyordu, nodes view içinde fleet view filtresinden kalmış eski veri görünüyordu ve fleet sekme sayıları da yanlış çıkıyordu - Sorun izlenirken Claude'un oluşturduğu
model.godosyasının tamamı, yani 1690 satır, ilk kez okundu; tek birModelstruct'ı UI widget'larını, Kubernetes client'ını, logs/describe/fleet durumunu, navigation history'yi, cache'i ve mouse handling'i birlikte taşıyordu Update()metodu yaklaşık 500 satırlık birmsg.(type)dispatch fonksiyonuydu ve içinde 110 adet switch/case branch vardı- Yapay zeka özellikleri hızlı geliştirebilir ama sınırsız şekilde yön verirseniz mimari dağılır; hız hissi de her şey aynı anda çökene kadar başarı gibi görünür
Enkazdan çıkan beş ilke
-
İlke 1: Yapay zeka özellik üretir ama mimari üretmez
- Claude fleet view, log streaming ve mouse desteği gibi tekil özellikleri iyi oluşturdu; ancak her özellik “şu an çalışsın” bağlamında yazıldığı için aynı durumu paylaşan diğer özelliklerle ilişkisini hesaba katmadı
resourcesLoadedMsghandler'ındamsg.gvr.Resource == k8s.ResourceNodes && m.fleetView != nilgibi koşullar vardı ve generic resource loading yolunun içine fleet view'a özel mantık karışıyordu- Her yeni view özel davranış gerektirdiğinde aynı handler'a bir branch daha ekleniyor, önceki view'ın verisinin yeni view'a sızmaması için çeşitli alanların elde temizlenmesi gerekiyordu
model.goiçindem.logLines = nil,m.allResources = nil,m.resources = nilgibi elde yapılan cleanup işlemleri 9 farklı yere dağılmıştı; biri unutulursa önceki view'dan kalan hayalet veri görünüyordu- Alternatif, kod yazmadan önce somut interface'leri, message type'ları ve ownership rule'ları bizzat yazıp bunları
CLAUDE.mdiçine mimari invariant olarak koymak - Örnek kurallar: her view bir
Viewtrait/interface'ini uygulamalı, hiçbir view başka bir view'ın state'ine erişmemeli, async veri yalnızcaAppMsgvaryantlarıyla gelmeli veAppstruct'ı sadece navigation ile message dispatch'ten sorumlu olmalı
-
İlke 2: god object, yapay zekanın varsayılan çıktısıdır
- Yapay zeka, anlık prompt'u en az ek yapı ile karşılamak için her şeyi taşıyan tek bir struct yapısına yöneldi
- Key handling de view bazında ayrılmamıştı;
stuşu logs view'da autoscroll, pods view'da shell, containers view'da ise container shell olarak çalışıyordu - “pods'a shell support ekle” isteği, mevcut global key handler çevresine yeni bir branch sıkıştırılarak uygulandı
Entertuşu da contexts view, namespaces view, logs view ve generic drill-down mantığını tek bir düz dispatch içindem.currentGVR.Resourcestring karşılaştırmalarıyla ayırıyordumodel.goiçindem.currentGVR.Resource ==ifadesi tür ayırıcı gibi 20'den fazla kez kullanılmıştı ve yeni bir view eklemek için birçok handler'a dokunmak gerekiyordu- Alternatif,
App/Modeliçine view'a özgü state alanları eklememek, her view'ı ayrı bir struct yapmak ve key binding'leri aktif view'ın keymap'inde tutma kuralınıCLAUDE.mdiçine koymak - “View eklemek dosya eklemek olmalı; mevcut bir view'ı değiştirmek gerekiyorsa dur ve sor” gibi guardrail'ler olmadığında yapay zeka en kısa yol olarak yeni branch eklemeye yöneliyor
-
İlke 3: Hız hissinin yarattığı illüzyon scope'u büyütür
- k10s başlangıçta GPU training cluster işleten dar bir kitle için tasarlanmıştı; ancak vibe-coding, pods, deployments, services, command palette, mouse support, contexts ve namespaces gibi özelliklerin “bedava” gibi hissedilmesine yol açtı
- Sonuçta araç, GPU odaklı bir araç olmaktan çıkıp tüm Kubernetes kullanıcılarına hitap eden genel amaçlı bir TUI'ye, yani fiilen k9s'i yeniden yapma yönüne kaydı
- Düz bir
keyMapiçindeFullscreen,Autoscroll,ToggleTime,WrapText,CopyLogs,ToggleLineNums,Describe,YamlView,Edit,Shell,FilterLogs,FleetTabNext,FleetTabPrevgibi farklı view'lara özgü binding'ler tek yapıda karışmış durumdaydı AutoscrollveShellikisi destuşuna bağlıydı; dispatch geçerli resource'a bakarak “çalışmasını” sağlıyordu ama keybinding'leri yerel bağlamda anlamayı imkânsız hale getiriyordu- Kod yazma hızı “shipping” gibi görünse de her özellik, god object içine bir branch daha ekleme maliyeti yaratıyordu
- Alternatif,
CLAUDE.mdiçinde scope sınırını açıkça belirtmek: k10s, GPU cluster operatörleri için olmalı; desteklenen view'lar fleet, node-detail, gpu-detail ve workload ile sınırlanmalı; generic resource view'lar veya k9s'in tekrar eden işlevleri eklenmemeli - Yapay zeka sınırsız satır bütçesi sağlayabilir ama karmaşıklık bütçesi hâlâ sınırlıdır; bu yüzden scope baştan reddedilmelidir
-
İlke 4: Konuma dayalı veri bir saatli bombadır
- k10s, Kubernetes API'den aldığı resource'ları doğrudan
type OrderedResourceFields []stringbiçiminde düzleştiriyordu - Fleet view'ın sıralama fonksiyonu
ra[3]değerini Alloc,ra[2]değerini Compute,ra[0]değerini ise Name olarak ele alıyordu; column kimliği yalnızca yorumlara veresource.views.jsoniçindeki column sırasına dayanıyordu resource.views.jsoniçinde Instance ile Compute arasına bir column eklendiğindera[2],ra[3]kullanan sıralama, koşullu render ve drill target mantığı sessizce bozulabiliyordu- Derleyici
[]stringiçindeki anlamı bilemez; JSON config de sıralama davranışını, koşullu render'ı ve özel drill target'ları ifade edemediği için Go kodu konuma dayalı varsayımları hardcode ediyordu - Yapay zeka, table widget'a doğrudan verilmesi kolay olduğu için
[]stringveyaVec<String>seçmeye yatkındır; typed struct ise başlangıçta daha fazla ek yapı gerektirdiğinden hızlı yolda geri plana itilir - Alternatif, yapılandırılmış veriyi render anına kadar
FleetNode,PodInfogibi typed struct olarak korumak ve sıralamayırow[3]gibi konumsal erişim yerine adlandırılmış alanlar üzerinden yapmak - Örnek yapı
FleetNode { name, instance_type, compute_class, alloc }gibi olabilir; böylece column kimliği tür sistemiyle ifade edilir ve yanlış column'a göre sıralama gibi imkânsız durumlar baştan engellenir - “Making impossible states impossible”, Elm/Rust topluluğunda kullanılan ve runtime check yerine geçersiz durumların hiç kurulamamasını sağlayacak şekilde type tasarlamayı anlatan bir ifadedir
- k10s, Kubernetes API'den aldığı resource'ları doğrudan
-
İlke 5: Yapay zeka state transition'ın sahibi değildir
- Bubble Tea mimarisinin özü, state değişiminin yalnızca message ile çalışan
Update()içinde yapılmasıdır; ancak k10s bunu ihlal etti updateTableMsghandler'ı birtea.Cmdclosure'ı döndürüyordu ve bu closure içindem.updateColumns(m.viewWidth),m.updateTableData(),m.table.SetCursor(savedCursor)gibi çağrılarlaModelalanları değiştiriliyordu- Bubble Tea,
tea.Cmdişlemlerini ayrı bir goroutine'de çalıştırdığı için closurem.resources,m.table,m.viewWidthalanlarını okuyup yazarken ana goroutine içindekiView()aynı alanları okuyabiliyordu - Ortada lock ya da mutex yoktu;
<-m.updateTableChansadece update sinyalini bekliyor,View()fonksiyonunun yarım yazılmış state'i okumasını engellemiyordu - Bu yapı açık bir data race idi ve çoğu zaman çalışsa da bazen ekranın bozulması gibi sonuçlar doğuruyordu
- Alternatif, arka plan worker'larının UI state'ini doğrudan mutate etmemesi, bunun yerine typed message'ları channel üzerinden göndermesi ve ana event loop'un bu message'ları alıp state mutation'ı uygulaması
- Concurrency kuralı şu olmalı: arka plan görevleri UI state'ini doğrudan değiştirmez, sonuçları typed message olarak gönderir ve
render()/view()side effect, I/O ya da channel işlemi içermeyen saf bir fonksiyon olmalıdır
- Bubble Tea mimarisinin özü, state değişiminin yalnızca message ile çalışan
CLAUDE.md ve agents.md içine konacak koruyucu kurallar
-
Mimari değişmezler
- Her view bir
Viewtrait/interface'ini uygulamalı ve başka view'ların state'ine erişmemeli - Tüm async veri
AppMsgvaryantlarıyla gelmeli; arka plan görevi alanları doğrudan mutate etmemeli - Yeni bir view eklemek mevcut view'ların değiştirilmesini gerektirmemeli
Appstruct'ı navigation ve message dispatch yapan ince bir router olmalı
- Her view bir
-
State sahipliği kuralları
- View'a özgü state,
App/Modelstruct'ına alan olarak eklenmemeli - Her view ayrı bir struct olarak var olmalı ve kendi key binding'lerini tanımlamalı
- Uygulama key'leri aktif view'a dispatch etmeli; yeni keybinding global handler'a değil ilgili view'ın keymap'ine eklenmeli
- Bir view eklemek mevcut view'ların değiştirilmesini gerektiriyorsa durulup onay alınmalı
- View'a özgü state,
-
Kapsam
- k10s tüm Kubernetes kullanıcıları için değil, GPU cluster operatörleri için bir araç olmalı
- Desteklenen view'lar fleet, node-detail, gpu-detail ve workload ile sınırlı olmalı
- Pods, deployments, services gibi generic resource view'lar eklenmemeli
- k9s işlevlerini kopyalayan özellikler eklenmemeli
- GPU training işleri işleten operatörlere fayda sağlamayan özellik istekleri reddedilmeli
-
Veri temsili
- Yapılandırılmış veri
[]string,Vec<String>veya konuma dayalı array'lere düzleştirilmemeli - Veri, render çağrısına kadar typed struct olarak akmalı
- Column kimliği array index'inden değil, struct alan adından gelmeli
- Sıralama fonksiyonları
row[3]gibi konumsal erişim yerine typed field'lar üzerinde çalışmalı - Görüntüleme için string üretimi yalnızca
render()/view()fonksiyonu içinde yapılmalı
- Yapılandırılmış veri
-
Eşzamanlılık kuralları
- Watcher, scraper, API call gibi arka plan görevleri UI state'ini doğrudan mutate etmemeli
- Arka plan görevleri sonuçları typed message olarak channel'a göndermeli
- State mutation'ı yalnızca ana event loop alınan message'lar üzerinden uygulamalı
render()/view()side effect, I/O veya channel işlemi içermeyen saf bir fonksiyon olmalı- Async iş sonucunda state değişecekse yeni bir
AppMsgvaryantı tanımlanmalı
Yeniden yapım yaklaşımı
- k10s bundan sonra Rust ile yeniden yazılacak; bunun nedeni Rust'ın mutlak anlamda daha iyi olması değil, yazarın onu doğrudan yönlendirebildiğini hissetmesi
- Yeterince iyi bilinen bir dilde, neyin yanlış gittiği bazen daha söze dökülmeden sezilebilir ve vibe-coding bu sezgiyi ikame edemez
- Yapay zeka ikna edici görünen kod ürettiğinde, onun gerçekten çöp olup olmadığını anlayabilme becerisi gerekir
- Yeni sürümde kod yazılmadan önce concrete interface'ler, message type'lar ve ownership rule'lar gibi tasarım çalışmaları önce insan tarafından elle yapılacak
- Önceden yapay zekanın yanlış verdiği architecture decision'lar, artık ilk prompt'tan önce belgede net biçimde tanımlanacak
- Mevcut TUI ve proje bağlantıları k10s Github ve K10S.DEV adreslerinde bulunuyor
Ek not
- Bubble Tea, The Elm Architecture tabanlı bir Go TUI framework'üdür; k10s'in mimari sorunları Bubble Tea'den değil, k10s tarafındaki implementasyondan kaynaklanıyordu
- “Making impossible states impossible”, geçersiz state'leri runtime'da kontrol etmek yerine type tasarımıyla bunların hiç kurulamamasını hedefleyen Elm/Rust topluluğuna ait bir ifadedir
- Yapay zeka yazımındaki “em-dash” gibi, yapay zeka ile kodlamada da “god-object” bir koku olarak kalabilir; vibe-coding uygulamayı ucuz hissettirdiği için odak kaybı ve şişkinliğe yol açabilir
1 yorum
Hacker News yorumları
Üretilen kodun iyi olduğunu söyleyenler genelde o kodu okumayan kişilerdi
Yazıda önerilen hafifletme yöntemleri de uzun süre dayanmaz. Sistem ya da bileşen tasarlarken “bir view başka bir view’un durumuna erişmez” gibi değişmezler ortaya çıkar; ama er ya da geç bu koşullarla çakışan bir özellik eklemek gerekir
O noktada genelde ya özellikten vazgeçilir, ya değişmezin üstüne garip ve verimsiz bir şey eklenir, ya da değişmezin kendisi değiştirilir. Bu seçim basit bir bağlam meselesi değil, bir muhakeme meselesidir ve mevcut modeller bu muhakemede çok sık hata yapıyor
Mimari kısıtları açıkça yazsanız bile ajan, o kısıtların değişmesi gereken yerde bile onlara zorla uyan karmaşık ve bakımı imkânsız kod üretir. İnsan yazımı koda kıyasla daha da dikkatli okumazsanız, sonunda “kendi kendini yiyen kod” ortaya çıkar ve bunu ancak çok geç fark edersiniz
Önemli olan, AI’ın zorlandığı noktaları tespit edip işi onun için kolaylaştırmak. Örneğin aşırı küçük bağlam, net sınırları olan modüler yapı, girdi/çıktıdan ayrılmış saf modüller, arayüzlerin arkasına gizleme, 1 saniyenin altında çalışan 100 test, benchmark’lar gibi şeyler gerekir
AI sınırlar ve küçük bağlam olduğunda iyi çalışır. Bunları vermezseniz performansı düşer ve bunun sorumluluğu aracı kullanan kişidedir
Hiçbir spesifikasyon gerçek dünyaya uzun süre dayanmaz; yeterince araştırma ve tasarım yapılsa bile, içindeki bazı değişmezlerin sonunda yanlış olduğu ortaya çıkar
Bir insan geliştirme sırasında bu durumla karşılaşınca geri çekilip değişmezin yanlış olup olmadığını, değişirse etkisinin ne olacağını yeniden düşünebilir. Buna karşılık AI, yanlış varsayım ya da tasarım altında bir şekilde hack’lenmiş çözüm üretmeye daha yatkın ve bütünü yeniden değerlendirecek içgörüden yoksun
Daha iyi iş akışları ve doğrulamayla bu durum iyileşebilir, ama Claude Code gibi araçların varsayılan olarak iyi yönettiği bir alan değil; sınırları var
Başta güçlü ilkeler koyduk ve birkaç kullanım noktasını elde taşıyarak güven kazandık. Tüm geçiş neredeyse 10 yıl ertelenecek kadar büyük ve pahalıydı; bu yüzden maliyeti düşürmek için AI ile hızlandırmak istedik
AI mekanik ve basit olan %80’lik kısımda fena değildi. Kalan %20 framework değişikliği gerektiriyordu; çoğu API alanı ekleme gibi küçük değişikliklerdi ama bir iki tanesi kavramsal yeniden tasarım istiyordu
Bir sistemin backend’i vakaların %99’unda belli veriyi üretebiliyordu ama bazı önemli durumlarda bunu mantıksal olarak üretemediği için dışarıdan bildirilmesi gerekiyordu. Oysa önemli bir optimizasyon “bu imkânsızdır” varsayımı üstüne kurulmuştu
AI aracı bunu fark etmedi ve sanki düzgün çalışacakmış gibi eski mantığı ekledi. Dağıtım biçimimiz sayesinde bu henüz bir production bug’ı olmadı ama partner ekibe doğru soruları sorarken aynı ihtiyacın başka yerlerde de olduğunu keşfettik
Sonuçta bir insan derine inmiş olduğu için büyük bir soruna dönüşmedi. Doğrulama araçları ve daha akıllı modeller gelecekte bu tür geçişleri kolaylaştırabilir, ama bugün üretilen kod bazen güzel görünse de kırılgan; bu yüzden yakından izlemek gerekiyor
Yaklaşık iki aydır kullandığım tuhaf bir mimari desen vardı; her kullandığımda hafif bir rahatsızlık hissediyordum ama ancak dün gece bunun iyi bir soyutlama olmadığını ve daha iyi bölünebileceğini fark ettim
Kodu LLM’e ürettirdiğimde bu rahatsızlığı çok daha az keskin hissediyorum; bu da sorunu fark etmeyi ve çözüm bulmayı geciktiriyor. Çevresel kısımlar üretilebilir ama çekirdek işlevlerin büyük kısmını hâlâ kendiniz yazmanız gerekiyor
Bunları hassas bir biçimsel dille ifade etseniz bile, ajanların altındaki LLM bu değişmezlerin neden gerekli olduğunu ve neden önemli olduğunu anlayacak kapasiteden yoksun. Token’larla biçimsel spesifikasyonları ilişkilendirip ispat bile yazan bir LLM çıkabilir, ama prompt’un gayriresmî kısımlarından üretilen tuhaf kodlar yine de gelmeye devam eder
Kısıtları ve prompt’ları bir teknik gereklilik listesine ya da spesifikasyona eklemek tek başına bunu engelleyemez. Daha iyi tuzak kursanız da canlı kaçıp gider
Sorun, prompt’u ya da görevi yerine getirmek için koda ekleme yapıldıkça oluşan kod şişmesi. Çoğu zaman daha az kod daha iyidir ve başkalarının ne isteyeceğini, ne bekleyeceğini öngörebilen birine ihtiyaç vardır. Üreticiler iyi ama yangın hortumu gibi; biraz daha kontrollü kullanılmaları gerekiyor
Copilot bir satırı otomatik tamamlarken “yine de tüm fonksiyonu sen yazmalısın” deniyordu; fonksiyon tamamlanınca “fonksiyonun etrafındaki mantığı sen yazmalısın” dendi; o mantık da tamamlanınca “özelliği sen yazmalısın” dendi
Şimdi özellik de tamamlanınca “yine de mimariyi sen yazmalısın” deniyor. Bu modeller mimariyi çözebilir mi bilmiyorum ama beklenti çizgisinin sürekli kayması ilginç
AI bir satırı da tamamlasa, tüm fonksiyonu da, özelliği ve ticket’ı da tamamlasa, yine de kodu okuyup anlamanız gerekir
AI’ı sürekli kullanıyorum ve giderek iyileşiyor ama hâlâ her satırı gözden geçiriyorum. Tek tek satır düzeyinde bile bugün, geçen yılın tab autocomplete’inden belirgin biçimde daha iyi olduğunu söylemek zor; bazen çok iyi, bazen gerçekten kötü
LLM’ler yazılım geliştirmede harika, ama mimariyi onların yazmasına izin vermediğinizde. Modülleri, struct’ları, enum’ları kendiniz oluşturun; mümkünse alanları ve varyantları da siz ekleyin
Her struct, enum, alan ve modüle doc comment yazın; sonra LLM’e bu modülleri ve veri yapılarını gösterip gerekli fonksiyon gövdelerini doldurtun
“Kritik yolda asla blocking yapma” diye kaç kere söyleseniz de LLM kritik yola blocking koyuyor; “X yapılırsa Y türü test gerekir” deseniz X’i yapıp testi atlıyor
İnsanlar da talimatlara %100 uymaz ama LLM daha rastlantısal davranıyor. İnsan hataları, istenenin tam tersini bu kadar isabetli biçimde yapma eğiliminde değil
LLM kod içindeki önemli değişmezleri görüp bunların etrafından dolanabilir, başarısızlığı başarı gibi gösteren testler yazabilir ve sonra da istendiği gibi yaptığını söyleyip bunu 5 bin satırlık commit’in içine gömebilir
LLM’lerin harika olduğuna ve geleceği temsil ettiğine eminim; bu yüzden onlar için https://GitHub.com/Cuzzo/clear adlı bir dil geliştiriyorum. Amaç, küresel bağlam gerekmemesi gereken yerde küresel bağlam isteyen dillerin sorununu aşmak; böylece birlikte çalışmak kolaylaşır
Başarılar da oldu ama bazen o kadar sinir bozucu oluyor ki, akıl sağlığımı buna harcamaya değdi mi diye düşündüğüm oluyor
Bu, mimarinin önemsiz olduğu anlamına gelmiyor; sadece dün iyi uyan mimarinin bugün de mutlaka iyi uyacağı anlamına gelmiyor
Kodlama ajanları kullanırken kendime birkaç kural koydum
Birincisi, ajanla kod üreteceksem, yeterli zaman verilse bunu kendim de doğru biçimde yazabileceğimden mutlak olarak emin olmalıyım
İkincisi, öyle değilse, üretilen şeyi tamamen anlayıp kendim yeniden üretebilecek duruma gelmeden ilerlemem
Üçüncüsü, ikinci kuralı bozarsam bilişsel borç oluşturabilirim ama projeyi tamamlandı ilan etmeden önce bunu tamamen kapatmam gerekir
Borç biriktikçe, sonraki üretilen kodun kalitesinin düşme ihtimali artıyor ve bu da bileşik faiz gibi büyüyor. Kişisel projelerde bu yöntem keyifli; çok şey öğreniyorum ve sonunda rahatça anlayabildiğim bir codebase kalıyor
Codebase’le bağlantıyı korurken ekibin darboğazı da olmamak arasında bir denge noktası gerekiyor
Claude doktora seviyesinde bir matematikçi, ben değilim; ama istediğim çözümün özelliklerini ve doğruluğunu nasıl test edeceğimi tam olarak biliyordum. Bu yüzden kendi basit ve naif çözümüm yerine Claude’un çözümünü bıraktım, pull request’te de bunu belirttim ve herkes bunun doğru karar olduğunu düşündü
Böyle durumlar için istisna tanımak gerekir mi merak ediyorum. AI yalnızca ileri matematikte değil, kodlamada da benden çok daha iyi hale gelirse, kodu doğrudan değerlendirme yeteneğimi kaybetsem bile testleri değerlendirebildiğim sürece tamamen elle kod yazmayı bırakır mıyım sorusu daha ilginç geliyor
Çünkü biriken borç tam olarak koda dair anlayış eksikliği; bu yüzden daha isabetli
AI’a neden birdenbire farklı muamele yapılması gerektiğini bilmiyorum
Sonunda karar risk ve ödüle göre verilmeli. Yanlış olduğunda zarar ne olur, bunun testte ve review’de yakalanma olasılığı nedir, doğru giderse getirisi ne olur; bunlara bakmak gerekir. Kütüphaneler ve dış servisler için de aynı şey geçerli
Testsiz ve güncellenemeyen bir kripto sözleşmesindeki karmaşık finans kurallarıyla, iç log verilerini görselleştiren bir viewer aynı risk düzeyi değildir
Teoride kulağa hoş geliyor ama gerçekte insan farkına varmadan hep zihinsel kestirmelere kaçıyor
Yabancı bir codebase’te bir problemi düzeltirken bunu kendim yaptığım durumla, ajanın yaptığını “tamamen anladım” dediğim durumu kıyaslayınca, bir hafta sonra aklımda kalan miktar farklı oluyor. Kendim yaptığımda genel bilgi olarak birikiyor ve önemli kısımlar çoğunlukla kalıyor; ama ajanın yaptığını kendi işimmiş gibi sahiplenmeye çalıştığımda, o anda anlamış gibi hissetsem bile çok hızlı unutuyorum
Bu yüzden böyle durumlarda LLM yardımının çoğunlukla hedeflerime zarar verdiği sonucuna vardım; zaman ve iş baskısı gibi başka kaygıları hesaba katmasam bile
Ben de aynı şeyi yaşadım
Tuzak şöyle işliyor: iyi bir codebase’te AI çok sayıda özellik üretebiliyor ve daha hızlı, daha güvenli, daha doğru görünmeye başlıyor. Özellikle de çok iyi bilmediğiniz alanlarda bu his daha güçlü oluyor
Zaman geçtikçe codebase büyüyor, gezinme süresi uzuyor, hata oranı artıyor. Bunu kabul etmek istemediğiniz için daha da zorluyorsunuz; değişiklik yapmak fiilen imkânsız hale geldikten sonra ancak duruyorsunuz
Koda yeniden bakınca, “spagetti” demek bile yetmiyor; durum adeta Çin Seddi gibi
Sonunda 140 bin satırın 75 binini sildim ve ajanlarla kodlamaya aşırı daldığım 3 ayın boşa gittiğini hissediyorum. İşe yaramaz özellikler yaptım, bug sayısını artırdım, kodun zihinsel modelini kaybettim, ancak kodun içinde görünür olan zor kararları kaçırdım ve kullanıcıları da hayal kırıklığına uğrattım
Alay etmek için söylemiyorum; gerçekten ilk beklentinin ne olduğunu ve nereden geldiğini merak ediyorum
Görünüşe göre LLM’ler için beklenti farklı. İnternette tanıdığınız rastgele bir “geliştirici”ye özet bir özellik açıklaması verip ondan yarı bozuk bir uygulama yığını alsanız kimse şaşırmazdı
Ama insanlar bazen, uzun uzun halüsinasyon gören bir makineden, bir insandan bile beklemeyecekleri mucizeleri bekliyor. O güvenin nereden geldiğini merak ediyorum
Büyük bir şehrin küçük şehirlerin toplamı gibi olması gibi; bir harita vardır, yerel bölgelere zoom yaparsınız ve o sınır içinde çalışırsınız. Bir kahve içmek için New York’un tüm ayrıntılarını bilmeniz gerekmez
Bakımı yapılabilir sağlam bir mimari kurmak, aracı kullanan kişinin sorumluluğudur. AI bunu engellemez; aracı doğru tutarsanız hatta yardımcı olabilir
Örneğin AI tarafından üretilen kodu anında legacy code gibi ele almak; güçlü kapsülleme sınırları ve iyi tanımlı arayüzler koyup sonra daha manuel bir akışla entegre etmek
Tek seferlik prompt’tan inline kod üretimine kadar bir yelpaze var ve hangi yaklaşımın uygun olduğu probleme ve codebase içindeki yerine göre değişir
Tek seferlik üretim, spesifikasyonun çok tekrarlandığı prototip aşamasına daha uygun; prototip oturunca modül/dosya düzeyinde üretime inilip daha sistematik ilerlenebilir ve bu katmanda da iyi bir zihinsel model korunmalı
Okudun ama anlamadıysan, her çıktı için ayrıntılı yorum eklemesini isteyebilirdin; modelin codebase büyüdükçe zorlandığını biliyorsan, karmaşıklık arttıkça çıktıyı daha da sıkı incelemek gerekir
Daha yüksek kaliteli kod adaları oluşturmak, AI’ı geliştiricinin niyetini ve iş kurallarını yeniden kurmaya yardımcı olmak için kullanmak ve hedef modülde seam ile unit test’ler oluşturmak gibi
AI mutlaka throughput artırmak için kullanılmak zorunda değil; daha sonraki elle kodlama ya da ajan uygulamalarını destekleyen esnek bir keşif ve refactoring aracı da olabilir
Bu tür yazıları her gördüğümde, insanların AI ile elde ettiğini söylediği hızla benim doğrudan elle kodlayarak elde ettiğim hızı kıyaslıyorum
Tesadüfen 7 aydır bir 3D MMO projesi üzerinde çalışıyorum; şu anda oynanabilir durumda, insanlar eğleniyor, grafikler fena değil ve sunucuda yüzlerce kişiyi rahatça barındırabiliyor. Mimari de oldukça iyi; özellik eklemek kolay ve yaklaşık 1 yıllık geliştirme sonunda yayımlanabilir gibi görünüyor
Ama asıl yazıdaki kişi 7 ay boyunca vibe coding yapıp temel bir TUI bile çıkaramamış. Özellik geliştirme hızı yüksekmiş gibi hissedilebilir ama böyle temel bir UI’ı yapmak için inanılmaz yavaş. İyi TUI kütüphaneleri bolca var ve gereken verilerle tablo doldurmak türünden bir şey; bunu elde birkaç haftada yapmak mümkün
AI kullanınca hızlıca çok ilerliyormuşsunuz gibi güçlü bir his oluşuyor ama gerçekte çoğu zaman manuel kodlamadan çok daha yavaş gibi görünüyor. Verimlilik verileri de AI kullananların kendilerini daha hızlı hissettiğini ama gerçek çıktıların daha düşük olduğunu destekliyor gibi
Yazılım geliştirme işlerinde en büyük zaman kaybı, paydaş beklentilerini ve çözümü hizalamaya çalışan toplantılardır. Bu açıdan bakınca AI neredeyse hiç yardımcı olmaz; bu yüzden fikir aşamasından test döngüsüne girişe kadar harcanan insan saatini karşılaştırırsanız hayal kırıklığı yaratan sonuçlar çıkması normal
Ama problem çözme, bug düzeltme ve onaylanmış çözümü uygulama tarafında eskisine göre en az 10 kat daha iyi olduğumu hissediyorum. Sadece saf zaman değil; gözlenen davranışı yorumlama ve problemi araştırma becerim de iyileşti
Yine de AI ile değerli ve doğru sonuç üretemeyen insanlar var. Ne istediğinizi ve onu nasıl istediğinizi tam biliyorsanız AI çok yardımcı oluyor. Benim zaten yapacağım şeyi ona yaptırırsam daha hızlı yapıyor. Ama ne istediğinizi tam bilmiyorsanız AI ilerlemeye zarar veriyor
İnsanların LLM ile yaptıklarını gösterdiğinde çok etkileyici gelmemesinin sebebi, çoğunun elde de çok kısa sürede yapılabilecek şeyler olması
Ayrıca etkileyici yazılımların sayısında bir artış da gözlemlemiyorum; bu da LLM’lerin şu anda önemli problemlerden çok basit problemleri çözmekte kullanıldığı gerçeğiyle uyumlu görünüyor
Ayrıca çok az konuşulan şeylerden biri de kod kalitesi
Vibe coding ile oluşmuş bir codebase, LLM’lerin kod yazmada o kadar da iyi olmadığının harika bir örneği. Kendi hatalarını düzeltirken hemen yeniden üretiyorlar ve desen kullanımı da tutarlı değil
Son zamanlarda Claude, mevcut codebase stiline hiç uymayan “ilginç” kod stili seçimleri de yapabiliyor
“Kıdemli geliştirici” tarzı bir dille bu tekrarları engellemek gerekiyor
“Kod yazmadan önce somut arayüzleri, mesaj tiplerini, sahiplik kurallarını kendim tasarlıyorum” kısmı zaten kodlamanın zor tarafı
Mimari varsa kod yazmak çok kolaydır. Kodu kendiniz yazmıyorsanız, null kabul eden bir API tasarladığınızı ama veritabanının bunu kabul etmediğini ya da kabul etse bile başka küçük sorunları kaçırdığınızı fark etmeniz zorlaşır
Bu yazıyı yazıp da sorunun AI olduğunu fark etmemiş olmasını anlamıyorum. Sadece mimariyi AI’a bırakmış olması değil; AI’ın yaptığı her şeyi dikkatle izlememiş olması da sorun
AI süslenmiş bir kod üretecidir ve yaptığı her şeyi kontrol etmek gerekir. Yazılım mühendisliğinin zor kısmı kod yazmak değil, onun dışındaki her şeydi
Kodlamanın zor olduğunu düşünen geliştiriciler AI ile kodlamayı gerçekten seviyor; çünkü önceden zor olan bir şey kolaylaştı
Buna karşılık kodlamanın kolay olduğunu düşünenler için mesele soyutlama, bakım yapılabilirlik ve ölçeklenebilirliktir. Yazılımın büyümesine izin verecek makul bir temel kurmak zordur; doğru soyutlamayı bulduğunuzda geri kalanı nispeten kolaylaşır
Bu insanlar için AI ile kodlama faydalı bir araçtır ama sihirli bir araç değildir. Asıl yazıyı yazan kişi AI’ın sınırlarını fark ettiğine göre ikinci grupta ve AI’ın yapamadığı zor kısmı görmüş durumda
Bir tarafta güçlü tab autocomplete ya da yandaki pencere chatbot’unu kullanıp yine de her şeyi açıkça review eden insanlar var; diğer tarafta ise Steve Yegge gibi, kodun çoğunu okumayacakmışsınız gibi onlarca ajanı koordine eden yeni editörleri tanıtan kişiler var: https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16d...
İlk grup hâlâ tasarım, arayüzler ve veri yapıları üzerine derin düşünüp güçlü review yapıyor. İkinci grup bunu yapmadığı için daha kaygı verici
plan → red/green/refactor yaklaşımını izliyorum; planın kendisi tüm dokümanları ve forum tartışmalarını içine çektiği için oldukça makul ve dayanaklı görünüyor
Sorun şu ki işe başlayınca mutlaka dokümantasyonla implementasyonun gerçekte farklılaştığı yerler çıkıyor. Araç kombinasyonu o şekilde hiç kullanılmamış olabilir, doküman eski olabilir ya da sadece bug vardır
Yine de proje ya da özellik hedefi yeterince netse ve yerelde çalıştırıp test edilebiliyorsa, ajan mimari çıkmazlarda döne döne bir çıkış bulabiliyor. Hatta dependency’leri ve kütüphane kodunu inceleyip upstream düzeltme bile öneriyor; bu da derin bir debugging oturumunda benim yapacağıma benziyor
Bu yüzden sıkıcı işleri bizzat yapmak yerine yönlendirme ve gözetim rolünde olmaktan oldukça memnunum. Ama ekip arkadaşlarımın önemli bir kısmı mimari sorunları bu kadar derin kazmıyor ve varsayılan olarak “mimara eskale etme” yoluna gidiyor; bu da uzun vadede iyi görünmüyor
Her şeyi çalıştırıp anlayabildiğimiz pencere hızla kapanıyor gibi. Ama derleyicinin makine koduna çevirme sürecini ya da modern CPU’ların branch prediction ve caching davranışını tamamen anlamadan da onları kullandığımız gibi, belki yeni araçlar ve framework’ler geliştirerek buna uyum sağlarız
Kod deneyimi çok fazla olmayan biri olarak, sonucu kontrol edip neyin doğru neyin yanlış olduğunu görerek hayatımda hiç olmadığı kadar çok şey öğreniyorum
Bu yüzden yakın zamanda büyük bir değişim olacağını da sanmıyorum. İnsanlar “Claude çıktısını nasıl bu kadar iyi hale getiriyorsun?” diye sorduğunda cevabım hep aynı: “Dikkatlice baktım, sorunları buldum ve Claude’a düzeltmesini söyledim.” Gerçekten hepsi bu; ama bunu duyunca insanların bakışları bile hemen sönüyor
Tıpkı Google’ın bilgi bulmayı kolaylaştırması ama iyi bilgiyle kötü bilgiyi ayırt eden insan unsurunu ortadan kaldırmaması gibi
Önce problemi düşünmek, yapıyı ve API’leri tasarlamak, ancak ondan sonra implementasyonu AI’a bırakmak
Başlık “elle kod yazmaya geri döndüm” diyor ama gerçekte yapılan şey, kod yazılmadan önce tasarım işini elle yapmak gibi görünüyor
Sonrasında kodu hâlâ Claude üretiyor gibi
Daha da önemlisi, 7 ay boyunca üretilen kaynak koduna bakmadan vibe coding projesinin iyi çalıştığını düşünüp bir de alan adı satın almış olmasını anlamak zor
Bu bir yan proje ise ve diff’leri takip ederek kademeli doğrulama yapıyorsanız, koda çok derin bakmamak o kadar da tuhaf sayılmaz. Kesinlikle farklı bir çalışma biçimi ama delilik seviyesinde değil
Geliştiricilerin proje yönetimi ve ürün yönetimi derslerini speedrun şeklinde yaşadığını görmek gibi geliyor
Artık spesifikasyonların faydalı olduğunu ve çok sayıda yanlış kod yazdırmanın projeyi hızlandırmadığını görüyorlar. Geliştiriciler toplantılara ve tartışmalara kod yazmayı engelliyor diye kızıyor ama bu süreçler çoğu zaman herkesin daha fazla yanlış şey yazmasını önlemek için var
İş takibinin yararlı olduğu da anlaşıldı; şimdi de tasarımın tamamen önden yapılması gerektiği söylemi arttıkça iş şelale modeline doğru kayıyor
Sonra buna prototipleme diye bir ad verilecek, eski gereksinimlerle yeni gereksinimleri birlikte yöneten artımlı özellik geliştirmeden söz edilecek ve sonunda müşterinin daha fazla dahil olması gerektiği söylenecek
Proje yöneticileriyle ürün yöneticilerinin aslında ne yaptığını görmek gerekiyor. Onlar kod adlı ürünü yönlendiriyor ama kod okumaları beklenmiyor; bunu yalnızca doğal dille başarmaları gerekiyor
İnsanların da kırık şeyler yazdığını sanmıyorlar mı? Ekiplerin yanlış yola girip bir haftayı, hatta ayları yaktığı olmuyor mu sanıyorlar? Artık vibe coding ile bunların hepsini 30 dakikada yaşayabiliyorsunuz. Eski bir teknik ürün yöneticisi olarak his tam anlamıyla aynı
Aslında elde kod yazmak gibi görünmüyor; bu yüzden başlıkla sonuç arasındaki fark kafa karıştırıcı