Yukarıda paylaştığım sözde şunu söylüyor:
Her ne kadar makaleye bu şekilde başlamış olsam da, makalede “Neden “clean code” yani temiz kod yazmamız gerekir?” sorusunu cevaplayarak devam etmeyeceğim. Zaten yukarıda Robert C. Martin sebebini gayet açık bir şekilde dile getirmiş. Bu arada Robert C. Martin, “Clean Code: A Handbook of Agile Software Craftsmanship” adında bir kitap yazmıştır ve bu alanda en iyi kitaplardan biridir. Aynı zamanda Youtube vb. mecralarda bu konu hakkındaki videolarını da bulabilirsiniz. Bakmanızı tavsiye ederim.
Gelelim asıl konumuza. Yazılım işiyle uğraşan, alaylı veya alaysız herkes bir şekilde projelerini hayata geçiriyor veya öğrenmek için kendince projeler geliştiriyor. Kimi zaman projelerimiz kişisel iken, kimi zaman da bir şirket adına veya bir başkası adına yapılan projeler oluyor. Yazdığımız kod kim için, ne için veya ne amaçla yazılmış olursa olsun, temiz kod yazmayı her zaman kendimize borç bilmeliyiz. Belki kodunuzu bir başkası okumayacak, belki ileride dönüp bakmayacaksınız bile o koda. Ne olursa olsun, kodumuzu yazarken okunaklı ve kaliteli olmasına özen gösterelim. Olur da ileride kodunuz üzerinde başkası çalışırsa, sonra küfür yemeyin boş yere. 😊
Son olarak başlamadan önce küçük bir not düşeyim. Başlıkta her ne kadar JavaScript yazdıysam da, yazının devamında bahsedeceğim bir çok şey, diğer programlama dillerinde de geçerli olacaktır.
1. Magic Numbers (Sihirli Sayılar)
Magic Numbers aslında kodumuzda kullandığımız, çoğu zaman, zamana bağlı olarak değişmeyen sabit değerlerdir. Örneğin, bir gün içinde 24 * 60 * 60 = 86400 saniye var. Bu sayı Perşembe günleri 90934 olmuyor. Her zaman 86400. İşte böyle durumlarda kodumuzda bu sayıyı direkt olarak kullanmak çok da mantıklı olmayacaktır. Çünkü, kodu okuduğunuzda bu sayının ne anlama geldiğini büyük ihtimalle anlamayacaksınız. İsterseniz hemen bir örnek yapalım.
for (let i = 0; i < 86400; i++) { console.log(i); }
Yukarıda yazdığım kod basitçe, loopta dolaşıp her adımda “i” değerini yazdırıyor. Ama kodu bir başkası okuduğunda veya ileride dönüp baktığınızda, muhtemelen 86400’ün ne olduğunu hatırlamayacaksınız ve kodunuz daha karmaşık bir yapıya geldiğinde, takip etmeniz de imkansız olacak. Onun yerine şöyle bir şey yapabiliriz:
const SECONDS_IN_DAY = 86_400; // veya // const SECONDS_IN_DAY = 86400; for (let i = 0; i < SECONDS_IN_DAY; i++) { console.log(i); }
Artık kodumuzun ne yapmaya çalıştığını biraz da olsa anlayabiliyoruz. Belli ki, bir gün içindeki saniyelerle alakalı bir şeyler yapıyor.
Not: İleride “SECONDS_IN_DAY” isimlendirmesini neden büyük harflerle ve alttan tire ile yaptığımı da açıklayacağım.
İkinci bölüme geçmeden önce araya girip, “Ne oluyor burada şimdi?” diye soranların sorusunu cevaplayayım. Özellikle başlıktaki iki tane farklı terimi açıklamak istiyorum: Best Practices ve Code Conventions.
Best Practices
Aslında gayet açık. Kodun en iyi şekilde nasıl olması gerektiğini söylüyor. Kodumuzu yazarken her zaman pratikte en iyisi hangisiyle onu uyguluyoruz. Yıllar boyunca insanlar sürekli kod yazmışlar ve bazı tecrübeler edinmişler. Bu tecrübelere bakarak kodun en iyi şekilde nasıl yazılması gerektiğini görmüşler.
Code Conventions
Best Practice ile birlikte geliyor bu terim de. Kodun en iyi şekilde nasıl yazılması gerektiğini gördükten sonra, herkes kodu o şekilde yazmaya başlıyor. Bu da doğal olarak bir düzen oluşturuyor. Bir nevi kural olarak düşünebilirsiniz. Zorunlu değil, ama olsa ne güzel olurdu dediğimiz kodlar.
2. Anlamlı İsimlendirme
Aslında Magic Numbers kısmında bunu kullandım. Sihirli sayımıza isim verirken “SECONDS_IN_DAY” dedim. Kodumuzda değişkenlere isim verirken, bu isimlerin anlamlı olmasına dikkat etmeliyiz. Bu sayede o değişkenin ne ile alakalı olduğunu daha rahat anlarız. Örneğin, ben “SECONDS_IN_DAY” yerine “SECONDS” yazabilirdim. Daha kısa ve kolay. Ama “Neyin saniyesi?” sorusunu bize sorduruyor. Nereden geldi bu saniye? Ne iş yapar? Ne için kullanılıyor? Bunların hiçbirine cevap vermiyor. Doğal olarak isimlendirme yaparken en iyi ismi bulmaya özen göstermeliyiz. Geliştirmekte olduğum bir projeden örnek vereyim:
const width = document.documentElement.clientWidth;
Yukarıdaki kod aslında, sayfanın genişliğini piksel cinsinden veriyor bize. Ama sadece “width” olarak isimlendirmişiz. İyi de bu “width” ne işe yarıyor? Neyin genişliği? Şöyle daha iyi olabilirdi:
const screenWidth = document.documentElement.clientWidth;
Artık neyin genişliği olduğunu biliyoruz. Ekranın genişliği!
3. camelCase
Değişken isimleri verirken, her programlama dilinde aynı olmasa da, JavaScript’te “camelCase” düzenini kullanıyoruz. Yani, her kelimenin baş harfini büyük yazıyoruz, ancak ilk kelimenin baş harfi küçük yazılıyor. Burada önemli olan, istisnalar haricinde her zaman tek bir “case” kuralına uymanız gerektiğidir. Bir yerde değişken ismini “Camel Case” kuralına göre yazıp, başka yerde “Pascal Case” kuralını kullanmayın. Önceki örneklerimde de bu kurala uyduğuma dikkat edin.
const formMessage = document.getElementById('form'); const MaxLength = 500; // doğrusu const maxLength = 500;
Yukarıda hem “Pascal Case” hem de “Camel Case” isimlendirme kuralını kullandım. Ancak bu yaptığımız doğru değil. Kodunuzu öyle bir yazmalısınız ki, ileride dönüp baktığınızda “Bu kodu ben yazmışım.” diyebilin.
Bu arada dikkat ettiyseniz “getElementById” fonksiyonu da “Camel Case” kuralına göre yazılmış.
4. Az olmasın, öz olmasın.
“Az olsun, öz olsun” lafını değiştirip, “az olmasın, öz olmasın” yapıyoruz. Kod yazarken, tembellik yapıp, değişken isimlerini kısa tutmaya çalışmayın. Daha az önce, “anlamlı isimlendirme” kuralından bahsettik. Sırf daha kısa yapacağım diye, anlaşılmasını zorlaştırmayın.
const getDailyPostsWithPagination = (pageSize, pageNumber) => { // some stuff here }
Yukarıdaki fonksiyon ismi 5 kelimeden oluşuyor ve çok uzun. Ama fonksiyonu okuduğunuzda ne yaptığını anlayabiliyorsunuz. Örneğin, ben fonksiyonun ismini “getPostsWithPagination” veya “getDailyPosts” yapabilirdim. Ama aslında fonksiyon, günlük makaleleri çekip, bunları sayfalandırarak gösteriyor. Doğal olarak hem “günlük” yani “daily” sözcüğünü ekledim, hem de “sayfalandırma ile” yani “with pagination” sözcüklerini ekledim. Böylelikle, fonksiyona baktığımda tam olarak ne yaptığını ve bana ne vereceğini biliyorum.
5. Class isimlerinde Pascal Case ve İsim kullanın.
Pascal Case’de de, Camel Case gibi her kelimenin baş harfi büyük oluyor. Aynı zamanda ilk kelimenin de baş harfi büyük oluyor. Bir çok programlama dilinde olduğu gibi JavaScript’te de Class isimlerinde Pascal Case’i kullanıyoruz. Bunun yanında Class isimleri “isim” şeklinde olmalı. Örneğin, “Car” ismi uygun iken “MakeCar” doğru değil.
6. Sabit Değer İçin BÜYÜK HARF
Aslında en başta bunu kullandım. “SECONDS_IN_DAY” olarak oluşturduğumuz değer aslında sabit, değişmeyen bir değer. Bu nedenle bunu büyük harflerle yazıyoruz. Burada “Neden büyük harf kullanıyoruz ki?” diye bir soru sorabilirsiniz. Aslında bu zamana kadar bahsettiğimiz her şeyin cevabı da bu olabilir. Bize az da olsa kod ile alakalı bilgi vermesi için. Aynı zamanda kodun okunaklılığını da arttırmak için. Şöyle ki; ben X firmasında çalışıyorum. İşten çıktım ve yerime başkasını aldılar. Doğal olarak illa ki, bu kişi benim yazdığım kod üzerinde çalışacak. Kodu okuyan kişi, büyük harflerle yazdığım değerin, aslında sabit bir değer olduğunu ve değişmediğini anlayacak. Bu ve bunun gibi durumlar için bu kuralları uyguluyoruz aslında.
7. Tek Harfli Değişken İsmi Koymayın
Gayet açık aslında. Değişken ismi verirken “x”, “q” vb. tek harfleri kullanmayın. Kimi zaman “query” yerine “q” kullanıldığını veya herhangi bir fonksiyonun içinde bir daha kullanmayacağımızı düşündüğümüz bir değere “x” ismini verebiliyoruz. Her ne kadar kullanılmayacak olsa da “x” ismini vermeyin. Daha önce de bahsetmiştik aslında. İsimlendirme yaparken, anlamlı olmasına özen gösterin, aynı zamanda öz veya kısa olması için de kendinizi zorlamayın. Uzun bir değişken ismi olması çok da kötü bir şey değil.
Burada bir istisnadan bahsetmek istiyorum. Bu durum WordPress’te de mevcut. URL’de bu tarz kısaltmalar kullanılabiliyor. “Query parameter” diye adlandırdığımız, özellikle sayfada filtreleme vs. için kullanılan bu parametrelerde kısaltma kullanılabiliyor. Bunun bir kaç sebebi var. Öncelikle URL’de karakter sınırı var. Bunun için kısaltmalar kullanmak bizim yararımıza oluyor. Tabii ki, kodumuzda hala “query” yazabiliriz. Bunun bizim için herhangi bir dezavantajı da yok.
8. Boolean Değerler İçin “is” Kullanın
Kodumuzu genel olarak İngilizce dilini kullanarak yazdığımız için, bu dilin bazı özelliklerini kullanmak da anlamsız olmayacaktır. Örneğin, bir nesnenin türünün “Vehicle” olup olmadığını bir değişkende tutmak istiyorsunuz. İngilizce’de olsa “Is vehicle?” yani “Araç (Vehicle) mı?” diye sorardık. Kodumuzu sanki İngilizce bir yazı okuyormuş gibi yazarsak, anlaması çok daha kolay olacaktır. Doğal olarak bu tarz Boolean değerler için “is” kullanıyoruz. Bizim örneğimizde değişkenimizin adı “isVehicle” olacak doğal olarak. Aynı zamanda “if-then” kullandığımız yerlerde de kontrolü çok kolay bir şekilde yapabiliyor olacağız. Örneğin;
const car = new Car('Mustang'); const isVehicle = typeof car === 'Vehicle'; if (isVehicle) { return car; } return new Car('BMW');;
9. Fonksiyonlar İçeriğe Uygun Fiiller Kullanın
Bu da aslında iyi isimlendirmeyle alakalı bir Best Practice. Başlığa dikkat ederseniz “fiiller” yazıyor. Daha önce Class’lara isim verirken isim kullanın demiştik. Fonksiyonlar da bir şeyler yaptıkları, bir aksiyon aldıkları için fonksiyonlarda fiil kullanmaya özen gösterin. Özellikle ReactJS ile geliştirme yaptıysanız, form değişikliklerini uyguladığımız fonksiyonlarda “handle” fiilini kullanıyoruz. Mesela, “handleEmailChange” gibi.
Ama sadece fiil kullanmakla kalmayıp, kullandığımız fiilin çalıştığımız içeriğe, konsepte de uygun olmasına özen gösterin. Örneğin, veritabanında bir işlem yapıyoruz. Bunun için “create, get, update, set, delete” gibi fiilleri kullanın. Bazı yerlerde “get” yerine “retrieve” veya “fetch” kullanıldığını görürsünüz. Ben neredeyse hiç kullanmıyorum bunları. Genel olarak da bu fiiller çok kullanılmaz. Biz burada Best Practice üzerine konuştuğumuz için tabii ki tercihimiz “get” fiilinden yana olacak.
const latestPosts = getLatestPosts(); const newPost = createPost({ title: 'JavaScript Best Practices', slug: 'javascript-best-practices', content: 'Some dummy content', status: POST_STATUSES.DRAFT.CODE, author: { username: 'rawsly', email: 'rawsly@gmail.com', }, });
10. Kod Tekrarı
Kod tekrarı aslında en önemli kurallardan biridir. Kısaca DRY (Don’t Repeat Yourself) diye de kullanıldığını görürsünüz. Kodunuz tekrar ediyorsa, çok büyük ihtimalle bir yerlerde Best Practice’e uymamış, kodunuz aslında yeteri kadar iyi yazılmamış demektir. Herkesin de başına gelmiştir bu kural ihlali. Çoğu zaman da kod tekrarı ortadan kaldırmak sizi zorlamaz. Ufak bir refactor ile durumu kurtarabilirsiniz. Çoğu zaman da kod tekrar kodu ilk yazdığımız zamanlar olur. Peki, nasıl çözeriz bu kod tekrarı sorununu? Nasıl refactor yaparız?
Çok basit aslında. Öncelikle kodunuzda tekrar eden yerleri bulmanız gerekiyor. Daha sonra da bunları nasıl birleştirip, ortak bir çözüm bulabilirim, diye düşünmeniz gerekiyor. Örnekle ilerleyelim. Örnekte, yeni bir makale oluştururken tüm alanların boş olmadığından emin olmak için “validation” yani doğrulama yapıyoruz.
const createPost = (data) => { const { title, slug, content, author, description } = data; if (!content) { throw new Error('Content cannot be empty.'); } if (!title) { throw new Error('Title cannot be empty.'); } if (!description) { throw new Error('Description cannot be empty.'); } const newPost = new Post({ id: uuid(), title, slug, content, status: POST_STATUSES.DRAFT.CODE, author }); newPost.save(); return newPost; }
İlk bakışta, yukarıda kod tekrarı yok gibi gelebilir. Ama her alan için “null” kontrolü yapmışız ve hepsinde de “if” kullanmışız. 3 kere “if” kullanmak yerine, tüm alanları loopta dönüp, hatayı tek seferde fırlatabiliriz. Örneğin;
const createPost = (data) => { const { title, slug, content, author, description } = data; const validationFields = [content, title, description]; validationFields.forEach(field => { if (!field) { throw new Error('Field cannot be empty.'); } }); const newPost = new Post({ id: uuid(), title, slug, content, status: POST_STATUSES.DRAFT.CODE, author }); newPost.save(); return newPost; }
Yukarıda kaybettiğimiz tek şey, her alana özel fırlattığımız hata mesajı. Eğer o da sizin için çok önemliyse, “validationFields” olarak tanımladığım arrayi, object arrayine çevirip, her alanı bir object olarak tanımlayıp, ‘fieldName’ adında bir özellik ekleyebilirsiniz. Bu tamamen size kalmış bir şey.
Dikkat etmeniz gereken şey, eğer bir yerlerde kendinizi tekrar ediyorsanız, çok fazla “copy-paste” yapıyorsanız, çok büyük ihtimalle DRY kuralına uymamışsınız demektir.
11. Çok Uzun Fonksiyonlardan Kaçının
Fonksiyonlar için “fonksiyon tek bir iş yapmalı” kuralına olabildiğince özen göstermeliyiz. Eğer bir fonksiyon aynı anda hem filtreleme, hem veritabanından veri çekme işlemini, hem “validation” gibi bir çok işi yapıyorsa, muhtemelen o fonksiyonu parçalamak kodu daha okunaklı hale getirecektir.
Örneğin, 10. alt başlıkta bahsettiğim DRY kuralında bu kuralı bozduk. “createPost” olarak yazdığımız fonksiyon, yeni bir makale oluştururken, aynı zamanda “validation” da yapıyor. Bunun yerine “validateFields” adında yeni bir fonksiyon tanımlayıp, “validation” işlemini ayrı bir yerde yapabiliriz. Bu sayede kodumuz daha okunaklı olurken, aynı zamanda bir fonksiyonla bir iş yapmış oluruz.
Sonuçta, “createPost” metodunu bir yerlerde çağırmadan önce, “validateFields” yapıp, validation işlemini gerçekleştirmek daha doğru olacaktır.
12. Comment Yazmayı Bırakın
Burada dikkat etmeniz gereken bir şey var. Comment yazmak veya yazmamak her zaman tartışma konusu olmuştur. Aslında daha doğrusu, kodunuzu öyle bir yazın ki, comment yazmaya gerek bile kalmasın.
Başından beri sürekli “clean code” diyoruz. Anlaşılabilir kod diyoruz. Eğer kodunuz yeteri kadar anlaşılabilirse, comment yazmanıza gerek bile kalmayacaktır. Comment’i kodunuzun ne yaptığını veya nasıl çalıştığını yazmak için kullanmak yerine, neden o kodu yazdığınızı veya neden başka türlü yazmadığınızı, neden o koda ihtiyacınız olduğunu yazabilirsiniz. Örneğin, bir fonksiyonunuz var ve bu fonksiyon tek bir şey yapıyor. Tüm kurallara uydunuz ve çok güzel bir şekilde yazdınız kodu. Bütün projenizde de buna uygun kod yazdığınızı düşünelim. Ama bu fonksiyonda, diğerlerinden farklı olarak tek bir şey yapıyorsunuz. Bunu neden farklı yaptığınızı, neden böyle yapmaya ihtiyacınız olduğunu comment olarak yazabilirsiniz. Bunun dışında uyarı mesajlarını da comment olarak yazabilirsiniz. Belki fonksiyonunuz tehlikeli bir şeyler yapıyordur ve bunu belli şartlar altında çalıştırmanız gerekiyordur. Uyarı amacıyla comment bırakılabilir. Bu sayede daha sonra dönüp baktığınızda veya birisi kodunuza baktığında neden bu yolu seçtiğinizi daha iyi anlayabilecek. Bunun dışında comment yazma ihtiyacı duymamanız gerekiyor.