Clean Code

Temiz Kod

Bu belge Robert C. Martin'in Clean Code adlı kitabından alınan notlardan oluşur.

Bkz. Clean Code: A Handbook of Agile Software Craftsmanship

Giriş

Temiz kod yazmak, kendimize profesyonel diyebilmek için yapmamız gereken şeydir. Yapabileceğimizin en iyisinden daha azını yapmak için mantıklı bir mazeret olamaz.

Total Productive Maintenance

TPM. Toyota’nın 1951’de geliştirdiği bir felsefe. Lean geliştirmenin de temelleri bu felsefe üzerine kurulmuştur.

“İlk önce bakımı daha kolay yapılabilir makineler üret.”

TPM’in 5S Prensibi

1. S-eiri (organization → s-ort) Neyin nerede olduğunu bilmek önemli. (Bu nedenle isimlendirme çok önemli.)

2. S-eiton (tidiness → s-ystematize) Her şeyin olmasını beklediğin yerde olması. Orada değilse refactor et ve oraya taşı.

3. S-eiso (cleaning → s-hine) Çalışma alanını atıklardan, pisliklerden temizle. (Commentle doldurulan kodlar, commentlenerek bırakılmış kodlar bunun gibi şeylerdir. Bunlardan kurtul.)

4. S-eiketsu (s-tandardization) Ekip çalışma alanını temiz tutmak konusunda anlaşılmalı. (Working Agreement – Çalışma Sözleşmesi oluşturulmalı. Tutarlı kod yazma stili ve pratikleri belirlenmeli ve uygulanmalı.)

5. S-hutsuke (discipline → s-elf-discipline) Pratikleri takip etmek konusunda disiplin sahibi olmak, bunu işine yansıtmak ve değişmeye istekli olmak.

Genel prensip: Sistemi her gün kontrol et, bozulmaya yüz tutmuş parçaların bozulmalarını beklemeden düzelt. → Bkz: Refactor Mercilessly / Acımasızca Refactor Et

Kötü Kod

Kötü kod çamura saplanmanıza ve en başta yapmak istediğiniz şeyi yapmak için gereksiz yere uğraşmanıza neden olabilir.

Ayrıca psikolojik etkileri vardır. Kişi mutsuz olur ve verimliliği düşer.

Kötü kodla karşılaşıldığında “Sonra düzeltiriz.” dememeliyiz. Çünkü:

LeBlanc kanunu der ki: "Sonra = Hiçbir zaman”

Bu nedenle kötü kod görüldüğü yerde refactor edilmeli. (Bkz: Opportunistic Refactoring) (Bkz: Boy Scout Rule - İzci Kuralı)

Not: Yapılacak değişiklikler çok büyük çaptaysa ve riskliyse elbette planlanarak yapılmalı.

Tutumumuz Nasıl Olmalı?

  • Kod nasıl oldu da bu kadar çürüdü?

    • Profesyonel davranmadık ya da davranılmamış. Suç proje yöneticisinde, müşteride veya pazarlamada değil, bizde.

  • Proje yöneticisi her ne kadar öyle görünmese de gerçeği duymak ister. Zaman kısıtı ve takvime saplantılı gibi görünse de iyi kod ister.

Örnek:

  • Bir doktor olsaydın ve hastan deseydi ki: “Şu gereksiz el yıkama işini atlayarak ameliyata geç, çünkü çok zaman alıyor.” (Doktorların muayenelerin arasında el yıkaması 1847’de doktorlara önerildiğinde, hasta muayeneleri aralarında bunu yapmak için zamanları olmayacak kadar meşgul oldukları söylenerek reddediliyor.)

  • Programcılar da bir çöplük yaratmanın risklerini bilmeyen yöneticilerin isteklerine uyarlarsa profesyonel olmayan bir davranış sergilemiş olurlar.

Temiz Kod Nedir?

Bjarne Stroustrup (C++ dilinin mucidi)

  • Elegant ve verimli.

  • Mantığı açık olmalı ki hataları gizlemesi zor olsun.

  • Bağımlılıkları asgari olsun, bakımı kolaylaşsın.

  • Bir şeyi iyi yapsın (Her şeyi yapmaya çalışmasın, odaklanmış olsun.)

Dave Thomas, Andy Hunt (Pragmatic Programmer kitabının yazarları. Dave Thomas Code Kata ve D.R.Y. (Don’t Repeat Yourself) fikirlerini ortaya ilk atan kişi)

Kırık Pencere Teorisi Kırık pencereleri olan bir binaya artık kimse özenmez, daha fazla pencerenin kırılması engellenmez, hatta sorumlular artık kendileri de kırmaya başlarlar. Sonunda bina grafiti ve çöple dolar. Bir kırık pencere çürümeyi başlatır.

Grady Booch (UML dilini geliştiren 3 kişiden biri)

  • Basit ve doğrudan olmalı

  • İyi yazılmış bir düzyazı gibi okunmalı

  • Yazarın, tasarımcının niyetini gizlememeli

“Big” Dave Thomas (Eclipse strategy)

  • Yazarından başka bir geliştirici tarafından okunabilmeli, geliştirilebilmeli

  • Birim ve kabul testleri olmalı

  • Anlamlı isimler kullanmalı

  • Açıkça belirtilen, asgari bağımlılıkları olmalı ve açık, asgari bir API sunmalı

  • Basit ve küçük parçalardan oluşmalı

Michael Feathers (Yazar)

En önemlisi: Özen gösteren biri tarafından yazıldığı belli olmalı. Kodu daha iyi hale getirmek için açık bir yol bırakmamalı. Hepsi zaten yazar tarafından düşünülmüş ve uygulanmış olmalı.

Ron Jeffries (Yazar)

Kent Beck’in basit kod kuralları:

  • Tüm testlerden geçmeli

  • Kod tekrarı içermemeli

  • Tasarım fikirlerini iyi ifade etmeli

  • Sınıf, metot, vb. adetlerini olabildiğince azaltmalı

En çok kod tekrarına odaklanıyorum. Kod tekrarlanıyorsa zihnimizin koda iyi yansıtılmadığını anlarız.İfade edicilik, açıklayıcılık (expressiveness)

  • Anlamlı isimler (gerektiği kadar yeniden isimlendirme yapılarak isimlerin oturması sağlanır)

  • Bir sınıfın, metodun birden fazla şey yapıp yapmadığına bakmak

    • Nesne çok iş yapıyorsa → 2 veya daha fazla nesneye ayrılır.

    • Metot çok iş yapıyorsa → Extract Method refactoring yöntemi uygulanır.

      • Ne yaptığını daha açık söyleyen bir ana metot ve yapılanların nasıl yapıldığını söyleyen alt metotlar. (Bir metotta sadece 1 soyutlama seviyesi kullanılması)

Kod tekrarını giderme ve açıklayıcı olmak, kötü bir kodu ele alıp düzeltirken önceliklendirilirse büyük fark yaratıyorlar.

Ward Cunningham (XP eş kurucusu, wiki mucidi, tasarım kalıplarının arkasındaki motivasyon)

Temiz kodla çalıştığını şuradan anlarsın ki, her rutin (metot) az çok beklediğin gibi okunur ve çalışır. (Principle of Least Astonishment)

Sonuç:

  • Biz yazarlarız ve okuyucularımız var.

  • Yazılan kod yazıldığından 10 kat fazla kez okunur. (Özellikle karmaşık, kritik önemde olan ve sık değişen bölümler)

  • Okuyucularımızla iyi iletişim kurmaktan sorumluyuz.

  • Yazarken yazar olduğunu, çabanı anlamaya çalışacak ve yargılayacak okuyucular için yazdığını düşün.

  • Okunabilir kod yazmak sürekli iyileştirmeye (Continuous Improvement) adanmışlık gerektirir.

Temiz Koda Ulaşmak İçin Öneriler

İsimlendirme

  • Niyet belirten isimler kullan.

  • Info, Data, a, an, the gibi gözardı edilen, yok sayılan ifadeler kullanma.

  • İsimleri öyle farklılaştır ki, okuyucu isimlerdeki farkın ne önerdiğini anlasın.

  • Telaffuz edilebilir isimler kullan.

  • Arandığında bulunabilir isimler kullan.

  • Hungarian notasyonundan kaçın.

  • Sınıf ve metotların o kadar küçük olsun ki önek kullanmana gerek kalmasın.

  • Açıklık (clarity) önemli.

  • Yapıcı metot overloadları yerine argümanları tanımlayan statik factory metotlar kullan.

  • Sorun alanı (Problem domain) isimleri kullan.

  • İsimler bağlamına uygun soyutlama seviyesinde olsun.

  • Belirsiz isimler kullanma, açıklayıcı olsun, uzunluğu önemli değil.

Fonksiyonlar

  • Bir programın organizasyonunda ilk aşama

  • Küçük metotlar. 20 satır bile olmamalı.

  • Koşullu ifade blokları (if, else, while, vb.) 1 satır olmalı. O satır da metot çağrısı olmalı. Girinti seviyesi 1 veya 2’den fazla olmamalı.

FONKSİYONLAR BİR ŞEY YAPMALI.

ONU İYİ YAPMALI.

SADECE ONU YAPMALI.

Bu “bir” şeyi (sorumluluğu) tespit etmek her zaman kolay değil.

"BİR" ŞEY: Eğer metot adının belirttiğinden bir seviye aşağıdaki adımları yerine getiriyorsa (bir alt seviye soyutlama seviyesinde kalıyorsa), metot sadece 1 şey yapıyor diyebiliriz.

  • “Bir” şey yapan metotlar mantıklı bir şekilde bölümlere ayrılamaz, daha fazla parçalanamaz.

  • Her metotta 1 soyutlama seviyesi kullanılmalı.

  • Switch yerine polimorfizm uygulanmalı. Switch kullanımını abstract factory ile kısıtla. Sadece bir switch kullanılsın ve ilgili tipi oluşturmaktan sorumlu olsun.

  • Metotların argüman sayısı 3’e çıkarsa düşünülmeli.

  • Flag argümanı metodun birden fazla iş yaptığını gösterir. Flag kullanmak yerine metotları ayır.

  • Output argümanlardan kaçın.

  • Hata yönetimi “bir” şeydir (sorumluluktur). Hata yönetimindeki bloklar da metot çağrılarından oluşmalı. Hata yönetimi ve uygulama akışı karışmamalı, ayrılmalı.

  • Don’t Repeat Yourself (DRY). Yazılımdaki bütün kötülüklerin temeli kod tekrarı.

  • Temiz kod ilk seferinde doğru şekilde yazılamayabilir. Çoğunlukla da yazılamaz. Testler ve refactoring ile son haline gelir. Önce işlevin çalışmasına yoğunlaşıp istediğin gibi yaz. Sonra refactor et.

Sonuç:

  • Her sistem, programcıların sistemi tanımlamak için tasarladıkları alana-özgü (domain-specific) dil ile inşa edilir.

  • Metotlar bu dilin fiilleri, sınıflar isimleridir.

  • Programlama sanatı her zaman dil tasarım sanatıdır.

  • Amacın sistemin hikayesini anlatmak.

Yorumlar

  • Yorum yazmanın genel motivasyonlarından biri kötü koddur. Yorum yazmak yerine kod kendini açıklayıcı hale getirilmeli.

  • Yorumlar kod ve tasarım hakkındaki teknik notlar için kullanılmalı.

  • Kodu kendisinin söylemeyediği şeyleri söylemeli.

Formatlama

  • Her programcının kendi favori biçimlendirme kuralları vardır, ancak bir ekip ile birlikte çalışılıyorsa, o zaman kuralları ekiple üyeleriyle birlikte koyar.

  • Takımın üzerinde anlaştığı formatlama kuralları kullanılmalı ve tutarlı olunmalı.

Hata Yönetimi

  • Hata yönetimi önemli, ancak kodun mantığını belirsiz hale getiriyorsa yanlış kullanılıyor demektir. Algoritma ile hata yönetimi kodlarını karıştırma.

  • Hata yönetimini, ana mantığımızdan bağımsız olarak görünür halde yapabilirsek, sağlam ve temiz kod yazmış oluruz.

Sınırlar

  • Temiz sınırlar için 3. parti kodları sararak (wrapper → adapter) bu kodların olabildiğince az yerde geçmesini sağlamalıyız.

  • 3. parti kodlar için öğrenme testleri (learning tests) yazarak çalışma şeklini test edebiliriz. Güncellendiğinde davranışlarının durumunun değişip değişmediğini kontrol edebiliriz.

Birim Testleri

TDD’nin (Test-Driven Development) 3 Yasası

  1. Geçmeyen bir birim testin olmadan uygulama kodu yazma.

  2. Testin geçmemesini sağlayacak test kodundan fazlasını yazma.

  3. Geçmeyen testi geçirmekten fazlasını yapan uygulama kodu yazma.

Testleri Temiz Tutmak

  • Uygulama kodu değişince testler de değişmelidir. Testler ne kadar kirliyse, değişmeleri de o kadar zor olur. Geliştirmelerin uzamasını testlere bağlayan geliştiriciler test yazmayı bırakırlar. Bu da hata oranın artmasına ve kodun istenmeyen şekilde gelişmesine (karmaşıklaşmasına, temizliğini yitirmesine) neden olur. Uygulama kodu zamanla çürümeye başlar.

  • Test kodu uygulama kodu kadar önemlidir.

  • Kodumuzu esnek, bakım yapılabilir ve yeniden kullanılabilir yapan şey birim testleridir.

Testleri Temiz Yapan Şey: Okunabilirlik

  • Açıklık (clarity)

  • Basitlik (simplicity)

  • İfadenin yoğunluğu, özlüğü (daha az ifadeyle daha çok şey anlatmak.)

  • Testler için alana özel bir dil geliştirmek

Testlerin sahip olması beklenen özellikler: F.I.R.S.T.

  • Fast

    • Testler hızlı çalışmalı. Hızlı olmazsa çalıştırmazsın.

    • Sorunları önceden göremezsin. Mikro geribildirim şansını kaçırırsın.

    • Kodu temizlemek için özgür hissetmezsin, çünkü kodu düzenlerken testleri sık sık çalıştırıp her adımda yeni durumu doğrulaman gerekir. Kodu temizlemediğin için kod çürür.

  • Independent

    • Testler birbirlerinden bağımsız olmalı. Çalışma sıraları veya ortak kullandığı veriler (örneğin kurulum verileri) sonuçlarını etkilememeli.

  • Repeatable

    • Ortamdan bağımsız olarak çalıştırılabilmeli, tekrar edilebilmeli.

  • Self-Validating

    • Boolean bir sonucu olmalı. Kendini doğrulamalı.

    • Geçmeli veya geçmemeli.

  • Timely

    • Doğru zamanda yazılmalı. (Uygulama kodundan hemen önce)

    • Uygulama kodundan sonra yazarsan uygulama kodunun test edilmesinin zor olduğunu görebilirsin.

    • Uygulama kodunu test edilebilir olarak tasarlamamış olabilirsin.

Sonuç: Testlerin çürümesine izin verirsen uygulama kodu da çürür. Testleri temiz tutmak gerekir.

Sınıflar

  • Sınıflar küçük olmalı.

  • Sınıfların isimleri görevini anlatmalı.

  • Sınıfa isim bulmakta zorlanıyorsan çok şey yapıyor olabilir. Olması gerekenden büyük olabilir.

  • Bir sorumluluğu (değişmek için bir nedeni) olmalı. (S.O.L.I.D. - Single Responsibility Principle)

  • Private metotları public metottan sonra çağırma sırasına göre yaz. (Step down kuralı) Kod böylece bir gazete haberi gibi okunabilir. (Private metotları sınıfın bellir bir yerinde bağımsız olarak gruplama)

  • Cohesive olmalı. Sınıf cohesionı kaybederse ayır.

  • İdeal bir sistemde sistemi genişleterek (extend) yeni özellikler ekleriz, olan kodu değiştirerek değil. (S.O.L.I.D. - Open-Closed Principle)

Sistemler

“Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build and test.” Ray Ozzie, CTO of Microsoft

  • Separation of Concerns → En eski ve önemli tasarım tekniklerinden biri

    • Main metodunu ayır. Uygulama mantığını “Uygulama” adı altında oluştur ve mainden bu uygulamayı çalıştır. Uygulama mainden çalıştırıldığını bile bilmesin.

  • Dependency Injection kullan ve gevşek-eşleştirme (loose-coupling) sağla. (S.O.L.I.D. - Dependency Inversion Principle)

  • Sistem ilk seferinde doğru yapılamaz.

    • Sadece bugünün hikayelerini yazarız.

    • Yarın refactor ederiz ve yeni hikayeler için sistemi genişletiriz.

    • Çevikliğin özü (iterative ve incremental) TDD, refactoring ve bunların ürettiği temiz kod ile bunu kod seviyesinde sağlarız.

  • İyi bir API çoğu zaman görünmezdir, takım API ile uğraşmak yerine yeni özellikleri geliştirmeye odaklanır.

  • Kararları olası en geç zamana kadar ertele. Olgunlaşmamış bir karar, optimal olmayan bilgiyle verilen bir karardır.

  • Sistemler de temiz olmalı.

Emergence (Belirme)

Bazı temel kuralları uygulayarak iyi tasarımların belirmesini, oluşmasını kolaylaştırabiliriz.

Kent Beck’in 4 Basit Tasarım Kuralı:

  1. Tüm testlerden geçer.

    • Test edilemeyen sistemler doğrulanamaz. Test yazarak Dependency Inversion Principle ve dependency injection araçlarını, arayüzleri ve soyutlamaları kullanarak eşleştirmeyi (coupling) daha gevşek hale getiririz. Test yazmak daha iyi tasarımlara götürür.

  2. Kod tekrarı içermez.

    • Yazılımdaki bütün kötülüklerin kaynağı.

  3. Programcının niyetini ifade eder.

    • Yazılım projelerinin maliyetinin çoğu uzun vadeli bakım aşamasında oluşur. İyi testler ifade edicidir (expressiveness). Kullanım örneğiyle belgelemek bu testlerin temel amaçlarından birisidir.

  4. Sınıf ve metotların sayısını olabildiğince azaltır.

    • Kod tekrarının engellenmesi, kodun açıklayıcılığı ve Single Responsibility Principle kullanımında da aşırıya kaçılmamalı. Gereksizce fazla sınıf ve metot oluşturulmamalı. Bu madde önceliği en düşük madde. İlk 3 madde daha önemli.

Successive Refinement

  • İlk seferinde temiz ve elegant kod yazmanız gerekmez. Programlama bilimden çok bir zanaattir.

  • Kirli yaz → sonra temizle.

  • İyi yazılım tasarımının çoğu parçalamak hakkındadır. Değişik tiplerde kodları farklı yerlere koymak → anlaşılması ve bakımı kolay olur.

  • Kodun çalışması yetmez. Bu düşünce profesyonellik dışıdır. Kodunu her zaman temiz ve basit tut.

Genel Öneriler

Yorumlar

Yorum yazmak yerine kodun açıklayıcı bir yapısı olmasını sağla.

Flag Argümanları

Boolean parametreler metodun birden fazla şey yaptığını haykırır. Metotları ayır ve argümanı kullanma.

Kod Tekrarı

Kodda her gördüğün kod tekrarı, kaçırılmış bir soyutlama fırsatıdır.

  • Alt metot (subroutine)

  • Ayrı sınıf

Çoğu tasarım kalıbı kod tekrarını engellemenin iyi bilinen yollarıdır.

Vertical Separation (Dikey Ayrım)

Kod yukarıdan aşağıya dikey bir düzlemde okunur. Değişkenler ve metotlar kullanıldıkları yere yakın tanımlanmalıdırlar. Dikey mesafe kısaltılmalıdır.

Tutarsızlık

Bir şeyi bir şekilde yapıyorsan hep o şekilde yap. Tutarsızlıktan kaçın.

Açıklayıcı Değişkenler

Hesaplamanın ara aşamalarında adı anlamlı değişkenler kullanarak okunabilirliği arttır.

Diğer Öneriler

  • Switch yerine polimorfizm kullan.

  • Sihirli sayılar yerine isimlendirilmiş sabitler kullan.

  • Koşullu ifadeleri enkapsüle et.

  • Sınır koşullarını enkapsüle et. (+1 -1 hesaplarını tekrar tekrar yapmak yerine anlamlı değişkenler kullan.)

  • Metotların içinde sadece aynı seviye soyutlama kullan. (Metodun adının belirttiği soyutlamanın bir alt seviyesini)

    • Soyutlama seviyelerini ayırmak, refactoringin en önemli ve en zorlarından görevlerinden biridir.

  • Tespit (SVG): Aynı seviye “Ne” veya “Nasıl” sorularından birine cevap veren adımlardan oluşur. Üst seviye metotlar “Ne” sorusuna yanıtlar verirler, alt seviyelere ve implementasyonlara doğru inildikçe “Nasıl” sorusu yanıtlanır.

Last updated