Kod tabanını tamamen anlamıyor olmanızın neden sorun olmadığı
(seangoedecke.com)- On milyonlarca satıra ulaşan büyük sistemlerde hiç kimse bütününü zihninde tutamaz; bu yüzden mühendislerin kısmen doğru bir anlayışla da etkili biçimde çalışabilmesi gerekir
- Peter Naur’un Programming as Theory Building yaklaşımı, mevcut ekibin anlayışı kaybolduğunda programı atıp yeniden yapmanın daha iyi olduğunu savunur; ancak kullanıcıların ve sayısız istisnanın iç içe geçtiği büyük sistemleri sıfırdan yeniden kurmak zordur
- Sorumlularının tamamı ayrılmış bir kod tabanı bile, tek bir akışı baştan sona anlayıp ardından değişiklik kapsamını dikkatle genişleterek yeniden canlandırılabilir; büyük organizasyonlarda bu tür anlayışın yeniden inşası tekrar tekrar yaşanır
- LLM’ler, ayrıntılı zihinsel model kurmayı zorlaştırırken aynı zamanda kısmi anlayışı hızlıca oluşturup kullanmaya yardımcı olur; ayrıca işbirliği, yasal gereklilikler, güvenlik güncellemeleri ve bağımlılık ekleme de kod anlayışıyla diğer değerler arasında ödünleşim gerektirir
- Doğru bir zihinsel model, keyifli ve güvenli geliştirmeye yardımcı olur ama mutlak ölçüt değildir; pratikte hız, yasal uyum ve kurumsal gereksinimler için tam anlayıştan vazgeçmek de gerekebilir
Tam anlayış ve kısmi anlayış
- Küçük ve ekip değişiminin az olduğu kod tabanlarında, iyi iş çıkarmak için bütünü anlamak gerektiğini düşünmek kolaydır
- Redis veya The Witness) gibi projeler buna örnektir
- Google web arama backend’i veya GitHub gibi büyük ve ekip değişiminin sık olduğu kod tabanlarında ise bütünü anlamak mümkün değildir; bu yüzden herkes kendi sorumlu olduğu yerel alanı olabildiğince anlamaya çalışarak iş yapar
- Bu iki ortamın geliştirme yöntemleri, pratikleri ve kültürü büyük ölçüde farklıdır; ancak çevrimiçi yazılım mühendisliği tartışmalarında tam anlayışı önemseyen ilk kültür gereğinden fazla temsil edilir
- Açık kaynak mühendislerinin yaptıkları işi yazıyla paylaşma motivasyonu yüksektir ve saf mühendislik çalışması, büyük ölçekli kapalı sistemlere kıyasla daha görünür olur
- Kapalı sistemleri hukuki nedenlerle paylaşmak zordur; paylaşılabilse bile büyük kod tabanlarını anlatmak için aşırı miktarda somut bağlam gerekir
- Birçok yazılım ortamında kısmi anlayış hatalı bir durum değildir; büyük sistemlerde gerçekçi olarak ulaşılabilecek en iyi seviye budur
- Bu fark, Pure and impure software engineering yazısında ayrılan tam anlayış kültürü ile kısmi anlayış kültürünün çatışmasına yol açar
Programming as Theory Building’in iddiası
- Peter Naur’un Programming as Theory Building makalesi, programcıların ürettiği asıl çıktının kod değil, programa dair bir teori olduğunu söyler
- Bu teori, neyin neden olduğuna dair sezgisel bir anlayıştan oluşur
- Kod ve dokümantasyon bu anlayışı ancak kısmen taşıyabilir
- Kodu kaybetseniz bile teoriye sahip bir ekip programı yeniden yazabilir; ancak ekip tamamen değişip bu anlayış kaybolursa mevcut kodu kavramak zorlaşır
- Naur’a göre mevcut teoriyi yalnızca doküman veya kod üzerinden yeniden kurmak mümkün değildir; bu yüzden orijinal programı bırakıp yeni ekibin problemi baştan çözmesi gerekir
- Naur’un alıntıladığı Gilbert Ryle’ın The Concept of Mind eseri, teori kurmanın kapsamını daha geniş ele alır
- Gerçekte bir işi yaparken teori veya ustalık bilgisi doğal olarak oluşabilir
- Dolayısıyla kodun kendisini keşfederek mevcut kod tabanını anlama süreci de bu yaklaşımla uyumludur
Büyük sistemler neden sıfırdan yeniden yazılamaz
- Kullanıcısı olan yeterince büyük sistemlerde, yeniden uygulanması zor binlerce istisna durum ve tuhaf davranış birikir
- Sistemi iyi bilen bir ekip bile tüm ayrıntıları aynı anda düşünemeyeceğinden, bütünü tek seferde yeniden yazmak zordur
- Başarılı yeniden yazımlar, mevcut kod tabanını küçük ve izole parçalara ayırıp her seferinde bir parçayı değiştirme yöntemiyle ilerler
- Sonuçta yeniden yazım da mevcut sisteme bir dizi değişiklik uygulama işidir
- Mevcut sistemi değiştiremiyorsanız, onu tamamen yeni bir sistemle değiştirmek daha da zordur
Terk edilmiş bir kod tabanı nasıl kurtarılır
- Yüz milyonlarca satır kod ve binlerce mühendise sahip teknoloji şirketlerinde, belli bir kod tabanını bilen hiç kimsenin kalmaması nadir değildir
- Sorumlu birkaç kişinin kötü bir zamanda ayrılması veya kod tabanının bir yıl boyunca bakım görmemesi bile bu duruma yol açabilir
- Naur, mevcut teoriyi az da olsa bilen biri olmadan tamamen ölü bir programın kurtarılmasının yeni programcılara bırakılmasının pek olmayacağını düşünüyordu; ancak büyük organizasyonlarda bu gerçekten yaşanır
- Terk edilmiş bir kod tabanı da zaman ayrılırsa yeni bir anlayış inşa edilip etkili şekilde çalışılabilir hale getirilebilir
- Önce tek bir işleme akışını baştan sona anlayın
- Dikkatli değişiklikler yaparak bu akıştan çevredeki alanlara doğru anlayışın kapsamını genişletin
Herkes eksik bir teoriyle çalışır
- Modern büyük ölçekli yazılımlar, yalnızca bireylerin değil, tüm ekiplerin bile bütün davranışları zihinde tutamayacağı kadar büyüktür
- Yeterince büyük bir kod tabanında herkes program hakkında bir ölçüde hatalı bir teoriyle çalışır
- Etkili mühendisler, kusursuz anlayışa sahip biri cevap verene kadar beklemek yerine mevcut bilgiyle en sağlam kararı verir ve sonuca göre hareket eder
- Bu çalışma biçimi, belirsizlik altında da pozisyon alabilme yeteneği ve özgüven gerektirir
- Büyük ürünlerin tüm davranışını kimsenin bütünüyle bilmediği gerçeği, Nobody knows how software products work yazısında ele alınan durumla aynıdır
Naur dönemi ile günümüzün kod ölçeği
- Naur’un makaleyi yazdığı 1985 yılında ortalama programların bugünkünden birkaç büyüklük mertebesi daha küçük olması muhtemeldir
- Naur’un verdiği ilk büyük program örneği 200 bin satırlık bir endüstriyel izleme programıydı; ikinci örnek ise bir derleyiciydi
- GCC’nin ilk sürümü 1987’de yaklaşık 100 bin satır iken, 2015’te 14 milyon satırın üzerine çıktı
- Mevcut testleri yeniden kullanabiliyorsanız 100–200 bin satırlık programlar nispeten kolay yeniden yazılabilir; ancak 1–2 milyon satırın üzerindeki sistemler için aynı yargıyı uygulamak zordur
LLM’lerin teori kurmadaki iki yönlü etkisi
- LLM’ler, genel teori kurma sürecini bozduğu gerekçesiyle sık sık kötü araçlar olarak değerlendirilir
- Ancak diğer yazılım araçlarında olduğu gibi LLM’lerin de iki yönlü etkisi vardır
- Yazılıma dair ayrıntılı bir zihinsel model kurmak daha zor hale gelebilir
- Kısmi teoriler hızlıca kurulabilir
- Eksik anlayışla bile daha etkili çalışmayı destekleyebilir
- LLM’ler ile kod anlayışı arasındaki ilişki, basit bir artı-eksi listesiyle açıklanamaz; hâlâ değerlendirme gerektiren karmaşık bir ödünleşimdir
Kod anlamayı zorlaştırsa da gerekli olan seçimler
- Kod tabanına dair doğru bir teoriyi sürdürmeyi zorlaştıran etkenler yalnızca LLM’ler değildir
- Başkalarının aynı kod tabanına kod yazmasına izin vermek
- Erişilebilirlik veya veri koruma gibi yasal olarak zorunlu özellikleri uygulamak
- İş arkadaşlarının işten ayrılmasına veya takım değiştirmesine izin vermek
- Güvenlik yamaları için yazılım sürümlerini yükseltmek
- Kütüphane veya başka bağımlılıklar eklemek
- Sırf teori kurmayı zorlaştırıyor diye bir araç ya da pratiğin kötü olduğuna karar verilemez
- Kod tabanını anlamak da, okunabilirlik, bakım yapılabilirlik ve doğruluk gibi pek çok mühendislik değerinden yalnızca biridir
- Duruma göre diğer değerlerden feragat edip doğru anlayışı önceliklendirebilirsiniz
- Tersine, hız, yasal uyum veya kurum içi politik nedenler uğruna kod anlayışından ödün verebilirsiniz
- Kod anlayışının diğer tüm değerlerin gerçekleşmesi için temel olduğu itirazı, okunabilirlik, bakım yapılabilirlik ve doğruluk için de aynı şekilde geçerlidir; pratikte bu temel değerlerin kendisiyle de sürekli ödünleşim yapılır
Kişisel tercih ve iş sorumluluğu
- Özellikle saf mühendisler, doğru bir zihinsel modeli koruyarak çalışmayı tercih eder
- Geliştirme daha eğlenceli ve daha az stresli olur
- Kendi gözlerinde gerçek mühendisliğe daha yakın hissettirir
- Birçok mühendisin boş zamanında tek başına küçük açık kaynak projeler yapmasının nedenlerinden biri de, kod tabanı hakkında doğru bir Naur tarzı teori koruyarak çalışabilmeleridir
- İş hayatında ise kişinin kendi mühendislik değerlerinden çok, organizasyonun bedelini ödeyip talep ettiği değerler kümesine uyulmalıdır
- Performansa önem verseniz bile takvime uymak veya zorlu gereksinimleri karşılamak için yavaş kod yazabilirsiniz
- Kod tabanını tam olarak anlamak da her zaman korunması gereken mutlak bir ölçüt değil, iş hedeflerine göre başka değerlerle takas edilebilecek bir seçenektir
2 yorum
Bu yüzden soyutlamaya bu kadar tutunuyoruz.
Lobste.rs görüşleri
Ne yazık ki orijinal başlık clickbait’e daha yakın; asıl içerik ise büyük bir kod tabanını kısmen anlamış olsanız da ilerleme kaydedebilmeniz gerekir fikrine daha yakın.
Teori kurma açısından bakıldığında, teorinin hem genişliği hem de derinliği olduğunu kabul edersek bu da yazının ana fikriyle çelişmez. Genişlik, sistemin hangi kapsamına kadar yeterince anlayıp soruları yanıtlayabildiğinizi ve ustalıkla değişiklik yapabildiğinizi; derinlik ise belirli bir bölüm hakkında ne kadar karmaşık soruları yanıtlayabildiğinizi ve bütünlüğü koruyarak ne kadar karmaşık değişiklikler yapabildiğinizi ifade eder.
Kodu modül modül yeniden yazıp arada bir geçiş dönemi bırakıyorsanız, o modül hakkında dar ama derin bir teori kurup ona göre çalışıyorsunuz demektir. Küçük fonksiyonlarda parametre ya da dönüş değerine bir şey eklemek sorun olmayabilir; ama ölçek büyüdükçe en azından belirli bir modül için temiz bir durumdan yeniden başlamak ve diğer bölümlerin yeni implementasyonu çağıracak şekilde bağlanması daha makul hale gelir. Temelde hatalarla dolu bir implementasyonun doğruluğunu kademeli olarak iyileştirmeye çalışmak pek işe yaramaz; bu durum performans gibi özellikler için de sık sık geçerlidir.
Yine de Naur “programı canlandırmaya çalışmak yerine mevcut program metni atılmalı ve yeni bir programcı ekibinin verilen problemi sıfırdan çözmesine izin verilmelidir” dediğinde, yeniden yazmaya başlamadan önce mevcut kodun gerçekten silinmesi mi gerektiği, yoksa yeni programın uygun bir alternatif olduğu doğrulandıktan sonra atılmasının da yeterli olup olmadığı tartışmaya açıktır.
Üstelik gerçek hayatta o verilen problem zaten çoğu zaman verilmiş değildir. Naur, orijinal problem tanımının elde kaldığını varsayar; ama büyük kod tabanlarında bu çoğu zaman yoktur. En yakın şey genellikle test suite’tir; dolayısıyla testleri geçirmeye devam ederken bileşenleri değiştirebilirsiniz.
O dönemde sürekli dağıtım da yoktu. Bugün ise servis çalışmaya devam ederken nöbet tutmanız, soruları yanıtlamanız ve acil bug’ları çözmeniz gerekir; bu yüzden yeniden yazımın maliyet-fayda hesabı da değişmiştir.
Bu itirazın iyi ifade edilmiş tarafları var, ama çoğu bana oldukça bariz geliyor. Yeterince büyük sistemlerde kısmi anlayış tek mümkün durumdur, ancak organizasyonlar her zaman daha derin bir anlayışı hedeflemelidir.
“İş yapmak için para alıyoruz” mantığından bağımsız olarak, büyük yazılımlar kullanıcıları memnun etmekte ya da iş hedeflerine ulaşmakta sık sık başarısız olur. Windows ve OS X’in de biriken karmaşıklık yüzünden çürümekte olduğu söylenebilir.
Yöneticiler de karmaşıklığı sihirli biçimde yok edebilseler bunu yaparlardı. Sorun daha çok bunun hâlâ çözülmemiş bir yazılım mühendisliği problemi olması; yöneticilerin kötü kod yayımlamak istemesinden ziyade mevcut koşullarda böyle ödünleri kabul etmelerine yakın.
“Tekelsen yeter, berbat ve bug dolu yazılım çıkarabilirsin” diye de bakılabilir ama bu aşırı nihilist olur. Tekel olmayan ya da kullanıcılarını elde tutamayan pek çok yazılım şirketi var; oralarda kalite gerçekten önemlidir.
Bu yorum altında anlayış kavramı da bir tür kapı bekçiliği gibi görünmeye başlar.
Yazıda açıklanan yaklaşım, kodun bir bölümünü yerel olarak akıl yürüterek anlama becerisiyle yakından ilişkilidir.
Çevrenin tamamını anlamadan büyük bir kod tabanının tek bir bölümünü anlayabilmeyi ifade eden yerel akıl yürütme, bilgisayar biliminin başından beri peşinden koştuğu temel bir kavramdır.
Yapısal programlamanın başlıca avantajlarından biri yerel akıl yürütmeye izin vermesiydi; global değişkenlerden kaçınılması tavsiyesi de büyük ölçüde aynı amaçtan doğdu. Fonksiyonel programlama yan etkileri ortadan kaldırarak yerel akıl yürütmeyi mümkün kılar; nesne yönelimli programlamanın ilkelerinden biri olan veri yapılarıyla onları işleyen kodu bir arada tutma yaklaşımı da yerel akıl yürütme için başka bir araç sunar.
Bu temel kavramların gerçek gücü, ideal bir durum olmasa da, kod tabanının tamamını anlamadan küçük parçalar üzerinde akıl yürütüp çalışabilmeyi sağlamasında yatar.
Bunu, “kod tabanındaki her satırı anlamalısınız” tavsiyesine karşı ilginç bir itiraz olarak okudum; yazının ana tezi de geliştirme uygulamalarıyla yakından ilgili olduğu için
#practicesetiketini ekledim.Bu topluluğun LLM’leri sevmediğini biliyorum ama bu yazının gerçekten
#vibecodingkapsamına girip girmediğinden emin değilim.vibecodingolarak sınıflandırılıyor; ben de buna katılmıyorum ama yerleşik pratik bu. Bu yazının temel sorusu da LLM destekli kodlama ile yakından bağlantılı.Yine de tek etiketin sadece
vibecodingolmaması gerektiği konusunda katılıyorum. Yazı LLM’leri ancak sonlara doğru açıkça ele alıyor; ana argüman ise LLM öncesi dönemde de ilginç ve tartışmaya değer. Bu yüzdenpracticesetiketini geri yüklemek için yeterli gerekçe olduğunu düşündüm ve bizzat geri ekledim; başkalarının da aynısını yapmasını öneririm.Kod tabanını tam anlamanın LLM kullanımı yüzünden engellenebileceği bir dünyada da bu konu önemini koruyor.
Eğer etiket “vibe coding ile ilgilenen kişiler için ilgili olabilir” anlamına geliyorsa kesinlikle uygun. Ama “vibe coding ile ilgilenenler dışında kimse için ilgili değildir” anlamına geliyorsa uygun değil. Diğer tüm etiketler birinci anlamda kullanılırken, Lobsters kullanıcılarının önemli bir kısmı yalnızca bu etiketi ikinci anlamda kullanıyor; bu yüzden sonunda bu yazıyı kaçırmaları kaçınılmaz oluyor.
1985’ten farklı olarak modern geliştirmede öne çıkan başka bir özellik de, 300 bin satırlık bir kod tabanının uzmanı olsanız bile gelecek hafta kendinizi bambaşka bir 300 bin satırlık kod tabanında çalışırken bulabilmenizdir.
Deneyimli profesyonel programcılar, her an bilinmeyen bir alana atılabileceklerine alışkındır ve ortak deyimlere (Google C++ Style Guide), API yanlış kullanıldığında hata veren compiler gibi otomasyon araçlarına ve büyük programların “nasıl yapılandırılması gerektiğine” dair sezgilerin birleşimine dayanır.
LLM ile yeniden yazılan Bun örneğinde olduğu gibi, “artık kod tabanının tamamının nasıl çalıştığını anlayan kimse yoksa geliştirmeye nasıl devam edilebilir?” türü tartışmalar da gördüm; ama bu, bir romanı ancak yazarıysa anlayabileceğini sormaya benziyor. Sonuçta bu kod; okunur. Bir fonksiyon anlaşılmıyorsa onu parçalara ayırabilir, testler yazabilir, kontrol akışını kâğıda dökebilir, aklınıza gelen ne varsa deneyebilirsiniz.
Projeler arasında düzenli olarak geçiş yaptığınız için eski kod tabanlarına dair anlayışı sürekli korumanın da pek bir nedeni yoktur. Bir noktadan sonra hepsi birbirine karışmaya başlar. Şirketin satın aldığı bir startup’ın Scala kodunun, özelleştirilmiş Thrift tabanlı bir RPC protokolü üzerinden Go ile yeniden yazılmakta olan JSON tabanlı bir Ruby servisle haberleşmesine yardım etmeniz gerekse bile, bir saat kadar
Scala syntax referenceveThrift wire encodingaraması yaparak başlayabilirsiniz.Uzman olmanıza da gerek yok. Bir ay sonra kendinizi Linux kernel’in Go implementasyonunda, JavaScript için OCaml tabanlı bir type checker’ın neden çöktüğünü debug ederken bulabilirsiniz. Sonuçta hepsi sadece kod.