- Karmaşıklık, yazılım geliştirmedeki en tehlikeli unsurdur
- Gerçek verimlilik, "80/20 çözümü" gibi karmaşıklıktan kaçınan pragmatik yaklaşımlardan doğar
- Test ve refactoring konusunda dengeli ve esnek bir tutum sürdürmek önemlidir
- Araç kullanımını ve okunması ile bakımı kolay kod yazma alışkanlığını vurgular
- Aşırı soyutlamaya ve trendlere karşı dikkatli olunmasını, sadeliği hedefleyen bir yaklaşım benimsenmesini önerir
Giriş
- Bu yazı, uzun yıllar yazılım geliştirirken deneyimden öğrenilenleri derleyen bir Grug Brain geliştiricisinin düşünce notlarıdır
- Grug Brain geliştiricisi kendini çok zeki biri olarak görmese de, uzun yıllar programlama yaparak çok şey öğrenmiştir
- Başkalarının hatalardan ders çıkarmasını umarak, farkındalıklarını kolay ve komik bir dille paylaşır
- Karmaşıklık, geliştirme hayatının en büyük düşmanıdır
- Karmaşıklık, kod tabanına gizlice sızar ve başta anlaşılması kolay olan kodu zamanla değiştirilemez hale getirir
Karmaşıklık şeytanıyla başa çıkmak
- Karmaşıklık, görünmez bir ruh gibi sessizce yayılır ve proje yöneticileri ile Grug olmayan geliştiriciler bunu çoğu zaman fark etmez
- Karmaşıklığı engellemenin en iyi yolu "hayır" diyebilmektir
- "Bu özelliği yapmayacağım"
- "Bu soyutlamayı eklemeyeceğim"
- Elbette kariyer açısından yüksek sesle "evet" demek daha kazançlı olabilir, ama Grug Brain geliştiricisi kendine karşı dürüst olmayı önemser
- Koşullara göre uzlaşmak (“ok”) da gerekir; bu gibi durumlarda sorunu 80/20 çözümüyle (Pareto ilkesi) basitçe çözmeyi tercih eder
- Proje yöneticisine her ayrıntıyı söylemeden, işi gerçekte 80/20 yaklaşımıyla halletmek de akıllıca bir stratejidir
Kod yapısı ve soyutlama
- Kodun uygun birimlere ayrılacağı yerler zamanla doğal olarak ortaya çıkar; bu yüzden erken soyutlamadan kaçınmak daha iyidir
- İyi bir ayrım noktasında, sistemin geri kalanıyla arayüzün dar olması idealdir
- Erken soyutlama girişimleri kolayca başarısız olur; deneyimli geliştiriciler, kodun şekli biraz oturduktan sonra yavaş yavaş yapılandırmayı dener
- Daha az deneyimli ya da “big brain” geliştiriciler, projenin başında aşırı soyutlamaya gider ve geride bakım yükü bırakır
Test stratejisi
- Testler konusunda takıntı ile denge arasında doğru noktayı bulmak önemlidir
- Prototiplemeden sonra, kod biraz daha oturduğunda test yazmayı tercih eder
- Unit testler başlangıçta yararlıdır, ancak pratikte en büyük faydayı orta seviye testler (integration testler) sağlar
- End-to-end testler de gereklidir, ama fazla olduklarında bakımı imkansız hale gelebilir; bu yüzden yalnızca gerçekten gerekli yollar için az sayıda tutulmalıdır
- Bir bug raporu geldiğinde, bug'ı düzeltmeden önce mutlaka yeniden üretim testi ekler
Süreç, agile ve refactoring
- Agile, Grug geliştiricisine göre kötü değildir; en kötüsü de değildir, ancak "agile şamanlarından" aşırı beklentiye girmek risklidir
- Prototipleme, araçlar ve iyi takım arkadaşları gerçekte daha önemli başarı faktörleridir
- Refactoring de iyi bir alışkanlıktır, ama büyük ve zorlayıcı refactoring girişimleri risklidir
- Karmaşık soyutlamaları zorla devreye sokmak, tersine projenin başarısız olmasına yol açabilir
Bakım, mükemmeliyetçilik ve alçakgönüllülük
- Mevcut sistemi sebepsiz yere söküp yeniden yapmak risklidir; “neden var olduğu bilinmeyen bir yapıyı” düşünmeden kaldırmak iyi bir alışkanlık değildir
- Kusursuz kod hayali kuran idealizm, pratikte çoğu zaman sorun üretir
- Deneyim arttıkça, “çalışan koda saygı duymak” gerektiği daha iyi anlaşılır
Araçlar ve üretkenlik
- İyi geliştirme araçları (
IDE code completion, debugger vb.) üretkenliği ciddi biçimde artırır; bu araçları derinlemesine öğrenmek önemlidir
- Tip sistemlerinin asıl değerinin “otomatik tamamlama” ve hata önleme olduğu vurgulanır; aşırı soyutlama ve generics ise tersine riskli olabilir
Kod stili ve tekrar
- Daha okunabilir ve debug etmesi daha kolay kod için, koşulları birden çok satıra bölmek gibi stiller önerilir
- DRY(Don’t Repeat Yourself) ilkesine saygı duyulur, ancak tekrar eden kodu zorla ortadan kaldırmaktansa dengenin daha önemli olduğu vurgulanır
- Basit tekrarın, karmaşık bir DRY uygulamasından daha iyi olduğu pek çok durum vardır
Yazılım tasarım ilkeleri
- SoC(Separation of Concerns) ilkesindense davranış yerelliğini tercih eder; "ilgili davranışı yapan kodun o nesnenin içinde olması bakım işini kolaylaştırır" görüşünü savunur
- Callback/closure, tip sistemleri, generics, soyutlama gibi araçların yalnızca az ve yerinde kullanılmasını öğütler
- Closure'ların aşırı kullanımı JavaScript'te "callback hell" yaratabilir
Logging, operasyon
- Logging çok önemlidir; ana dallanmaların her birinde log bırakılmalı ve cloud ortamlarında request ID gibi bilgilerle izlenebilirlik sağlanmalıdır
- Dinamik log seviyeleri ve kullanıcı bazlı loglar kullanılabiliyorsa, üretimdeki sorunların takibinde büyük fayda sağlar
Eşzamanlılık, optimizasyon
- Eşzamanlılıkta, mümkün olduğunca yalnızca basit modellere (durumsuz web istekleri, ayrılmış worker queue'lar vb.) güvenir
- Optimizasyonun ancak gerçek performans profiling verileri elde edildikten sonra yapılmasını önerir
- Network I/O gibi gizli maliyetlere dikkat etmek gerekir; yalnızca CPU karmaşıklığına bakmak risklidir
API tasarımı
- İyi bir API kullanımı kolay olmalıdır; aşırı karmaşık tasarımlar veya soyutlamalar geliştirici deneyimine zarar verir
- "Kullanım senaryosuna uygun basit API" ile "karmaşık senaryoları da kurmaya imkan veren katmanlı API" yapısını önerir
Parser geliştirme
- Recursive descent parser'lar akademide hak ettiği değeri görmese de, gerçek üretim kodu için en uygun ve anlaşılması en kolay yöntemdir
- Parser geliştirme deneyimlerinin çoğunda, araçlarla üretilen parser'ların çıktısı fazla karmaşık olmuş ve problem çözmeyi kolaylaştırmak yerine zorlaştırmıştır
- Önerilen kitap olarak "Crafting Interpreters"ı en üst sıraya koyar; kitap çok sayıda pratik tavsiye içerir
Frontend ve trendler
- Modern frontend (React, SPA, GraphQL vb.) çoğu zaman gereksiz yere yeni karmaşıklık şeytanları çağırır
- Grug, karmaşıklığı azaltmak için htmx ve hyperscript gibi daha basit araçları tercih eder
- Frontend tarafında sürekli yeni denemeler yapılsa da, bunların çoğunun mevcut fikirlerin tekrarından ibaret olabileceğine dikkat çekmek gerekir
Psikolojik etkenler, impostor syndrome
- Geliştiricilerin çoğu sık sık “ne yaptığını bilmiyorum” duygusuna kapılır; FOLD(Fear Of Looking Dumb) etkisinden özgürleşmek gerekir
- Kıdemli bir geliştirici açıkça “bu bana da zor geliyor, fazla karmaşık” dediğinde, junior geliştiriciler de üzerlerindeki baskıyı bırakabilir
- Impostor syndrome yaygın bir duygudur; insanın öğrenerek ve ilerleyerek gelişebileceği hatırlatılır
Sonuç
- Programlamada karmaşıklığa karşı her zaman tetikte olmak gerekir; sadeliği korumak başarılı geliştirmenin anahtarıdır
- Deneyim, araçların etkili kullanımı, alçakgönüllülük ve gerçekten çalışan koda saygı, uzun vadede daha verimli ve daha değerli bir geliştirme anlayışına götürür
- "Karmaşıklık çok, çok kötüdür" — bu cümle her zaman akılda tutulmalıdır
1 yorum
Hacker News görüşleri
printsatırlarıyla debug yaptığı gerçeğini gördüm. İş akışımı ekip arkadaşlarıma anlatmaya çalışsam da pek tepki almıyorum. Bir sistemi anlamaya başlamak için en iyi çıkış noktasının debugger olduğu fikrine katılıyorum. Test sırasında ilginç bir kod satırında durup stack’e bakmak, kodu kafada takip etmeye çalışmaktan çok daha kolay. Debugger kullanmayı öğrenirseniz gerçekten küçük bir süper güç kazanmış olursunuz. Mümkünse mutlaka bir kez denemenizi tavsiye ederimprintile debug etmek tek mümkün seçenek oluyor. Hatta log sisteminde bile sorun çıkarsa ya da program log basamadan çöküyorsa,printbile kullanılamayan bir duruma düşüyorsunuzprintçıktıları ve self-checking code eklemek daha üretken.printeklemek, debugger ile adım adım içeri girmekten çok daha hızlı. Ayrıcaprintkodu programda kalır, debugging session ise kaybolur." Ben de bu görüşe katılıyorum. Geliştirmenin büyük kısmındaprint-hipotez-çalıştır döngüsü çok daha hızlı sorun çözümü sağlıyor. Kodu kafamda “çalıştırmıyorum”; onun yerine kod akışı hakkında zaten bir çalışma modelim var veprintyanlış bir çıktı gösterdiğinde çoğu zaman durumun ne olduğunu hızla sezebiliyorum. İlgili bağlantı: The unreasonable effectiveness of print debuggingprintfile debug etmenin hep yaygın olmasının nedeni, GUI tabanlı debugger’ların güvenilmez olduğu ortamlardı. Linux GUI’si çoğu zaman kararsız olduğu için güven vermiyordu. Benim de debugger’ı gerçekten kullanmaya başlamam şu dönemlere denk geldi: (1) Windows’ta GUI iyi çalışırken CLI’nin sık sık bozulduğu zamanlar ve (2)printile debug kodunun yanlışlıkla sürüme girip sorun çıkardığını birkaç kez yaşadıktan sonra. Sonrasında CLI debugger’larla çeşitli maceralarım oldu ve Junit+debugger (Eclipse gibi IDE tabanlı) ile deneysel kodu anında yazıp test olarak bırakabildiğiniz sürecin Python REPL kadar kullanışlı olduğunu hissettim. Tabii debugger’ı ortama uygun şekilde kurmak için başta bir yatırım gerekiyorVisitoradlı bir şey yazmadım. Mesela sözdizim ağacı (AST) çalışıyorsanız,VisitoryerineAstWalker,AstItem::dispatch(AstWalker),AstWalker::process(AstItem)gibi daha somut adlandırmalar çok daha anlamlı. visitor denince “ziyaret eder” demek fazla soyut ve anlamsız kalıyor. Duruma göre değişir tabii; ayrıca yoruma bunun “visitor pattern” olduğunu yazarsanız tanınmasında sorun olmaz. Geçmişte iki nesne ağacını eşleyip verileri karşılaştırma/aktarma işi yapmıştım; oradaAbstractImporteradını kullanmıştım. Daha somuttu, süreç ve rol daha açıktı. Klasik visitor pattern’den farklıydı