Temiz Kod Yazma Kültürü

Uğur GELİŞKEN
9 min readDec 29, 2021

Değişkenler

Bir proje geliştirirken mutlaka o projeyi sadece siz geliştiriyormuşsunuz gibi değil de, sanki sizden sonra başkası o projeyi devralacak ve o devam ettirecek diye düşünün. Hatta bu düşünce yapısında, sadece kendi kültürümüzdeki veya yetenek seviyemizde değil de, evrensel düşünmeliyiz.

Günümüzde projeler geliştirilirken artık Open Source kavramı ile bütün dünyanın ortak bir dil kültürü ile geliştirmiş olduğu yapılar kullanılmaktadır. İşin içine bütün dünya kültürleri girdiğinde, mutlak suretle belli bir düzen içine girmemiz gerekiyor. Sizin yazdığınız kodu bir başkası anlayamayabilir, hatta yanlış gelebilir. Başkasının kodunu da siz gördüğünüzde bu iş daha basit yapılır diye düşünebilirsiniz, ama aslında doğru olan o görmüş olduğunuz kod olabilir. Hatta bu kişi, o kodun doğru çalıştığını ispatlamak için de birim test (unit test) yazmış olabilir.

Biz de bu makale serimizde, tüm literatürü değil de en temelinden en azından olmazsa olmaz kurallarla yazılım geliştirme kültürümüzü belli bir seviyeye çıkarmaya çalışacağız. Ve tabi ki JavaScript dili ile çalışacağız.

Aklınızda bulunsun, bu makale serisinde öğrenecekleriniz sizi mükemmel bir yazılımcı yapmayacak, ancak o yolda ilerlemeniz için sizi belli bir seviyeye getirecek. Yıllar geçtikçe de daha çok kod yazdıkça, daha çok kod inceledikçe kendinizi daha da geliştireceksiniz.

Değişkenler

Değişken isimleri belirlerken hemen aklınıza gelen isimle değil, anlamlı ve belirgin bir isim kullanın.

Yanlış

const dateOfControl = new Date();

Doğru

const currentDate = new Date();

Aynı değişken türü için aynı kelimeleri kullanın.

Yanlış

getUserName();
getUserSurname();

Doğru

getUser();

Yazacağınız kodlar okunabilir ve aranabilir olmalıdır. Herhangi bir yerde bir parametre değeri kullanmanız gerekirse, onu doğrudan yazmayın, bir değişkene aktarın. Sonrasında burada yazan değer ne anlama geliyor diye düşünmek zorunda kalmazsınız.

Yanlış

// 60000 ne demek?
setTimeOut(anyMethod(), 60000);

Doğru

const ONE_MINUTE = 60000;
setTimeOut(anyMethod(), ONE_MINUTE);

Zihinsel haritalamalardan kaçının. Açıklayıcı olmak iyidir.

Yanlış

const cities = ["İstanbul", "Ankara", "İzmir"];
locations.forEach(l => {
getZipCode(1);
// 1 nedir?
});

Doğru

const cities = ["İstanbul", "Ankara", "İzmir"];
locations.forEach(city => {
getZipCode(city);
});

Gereksiz ifadelerden kaçının.

Yanlış

city.cityCode();

Doğru

city.code();

Kısa yollar ve koşullar kullanmayın. Bunun yerine varsayılan değer atamaları kullanın. Bu metot daha temizdir ve doğrudur. Varsayılan değer atamaları, sadece tanımsız, değer almamış ve işlevsiz değişkenlerde kullanılır. Yani ‘’, “”, null, NaN, undefined, 0, false gibi değerler döndüren parametrelere varsayılan atama yapılır.

Yanlış

const getName = name => {
return name || "İsim yok.";
}

Doğru

const getName = (name = “İsim yok.”) => {
return name;
}

Fonksiyonlar

Fonksiyonlarda parametre sayısı sınırlandırılmalıdır. Teoride bir fonksiyon canımızın istediği kadar parametre alabilir, ancak bu durum fonksiyonun hem anlaşılmasını, hem kullanılmasını hem de o fonksiyonun test edilebilirliğini imkansız hale getirir. En fazla 3 adet parametre alması uygundur, ideal olanı da 1 veya 2 parametredir. Eğer 3’ten fazla parametre alması gerekiyorsa, o fonksiyon parametre olarak Object almalıdır ve o Object içinden değerler ayrıştırılmalıdır. Ayrıştırma işlemini ES6’da gördüğümüz Object parçalama metodu ile rahatlıkla yapabiliriz.

Bir fonksiyon çok fazla parametre alıyorsa, çok fazla iş yapıyor demektir. Bu da yanlış bir durumun sinyalini verir, fonksiyonlar mümkün olduğunca az iş yapmaya odaklanmalıdır. Her fonksiyon kendi işini yapmalı, karmaşık işlemler yapmamalıdır.

Bir fonksiyona baktığımızda en fazla 3 saniyede o fonksiyonun ne iş yaptığını anlayamıyorsak, o fonksiyon tekrar elden geçirilmelidir.

Yanlış

const createMessage = (name, organization, city, time) => {
return "Merhaba" + name + ", " + organization + " şirketinden gelmiş olmalısınız. Saat şu an " + time + " ve şu an " + city + " şehrindeyiz.";
}
createMessage("X", "ABC", "İstanbul", "12:30");

Doğru

const createMessage = user => {
return "Merhaba" + user.name + ", " + user.organization + " şirketinden gelmiş olmalısınız. Saat şu an " + user.time + " ve şu an " + user.city + " şehrindeyiz.";
}
const user = { name: "X", organization: "ABC", city: "İstanbul", time: "12:30" };
createMessage(user);

Fonksiyonlar sadece tek bir iş yapmalı. Bu kural, belki de yazılım geliştirirken dikkat edilmesi gereken en önemli kuraldır. Eğer bir fonksiyona birden fazla görev verirseniz, o fonksiyonun yönetilmesi zorlaşır, yazılım başka alanlarında da kullanılamaz. Bir süreci bir fonksiyona vermek yerine birkaç fonksiyona bölmeniz hem işinizi kolaylaştırır, hem de o fonksiyon parçacıklarını başka yerlerde de kullanabilmenize olanak sağlar. Ayrıca kodun daha temiz olmasını ve okunabilirliğini artırır.

Yanlış

showNameAndSurnameAndAge();

Doğru

showName();
showSurname();
showAge();

Fonksiyonların adları, onun ne iş yaptığını anlatmalıdır.

Yanlış

// Ne için gün ekleniyor?
addDay();

Doğru

addDateForPlan();

Yenilenen, yani tekrarlanan kodları kaldırın, sadeleştirin. Eğer bir kod birden fazla yerde tekrar ediyorsa, bir sorun var demektir. O tekrar eden kodlar, bağlı olduğu fonksiyonun aslında birden fazla iş yaptığını ve başka bir fonksiyonun da o işi tekrar yüklendiğini gösterir. Büyük bir sorun var ortada.

Tekrarlanan kodlarda bir yeri değiştirdiğinizde, diğer yerleri de değiştirmeniz gerekir.

Yanlış

const getMesage(message) => {
let currentTime = new Date().toLocaleTimeString();
return "Gelen mesaj " + message + "[" + currentTime + "]";
}
const postMesage(message) => {
let currentTime = new Date().toLocaleTimeString();
return "Giden mesaj: " + message + "[" + currentTime + "]";
}

Doğru

const getCurrentTime = () => {
return new Date().toLocaleTimeString();
}
const getMesage = message => {
return "Gelen mesaj " + message + "[" + getCurrentTime() + "]";
}
const postMesage = message => {
return "Giden mesaj: " + message + "[" + getCurrentTime() + "]";
}

Object.assign ile varsayılan nesneleri ayarlayın.

Yanlış

const buttonConfig = {
text: "Google",
link: "https://www.google.com",
enabled: true
};
const createButton = config => {
config.title = config.title || "Boş";
config.link= config.body || "#";
config.enabled = config.enabled !== undefined ? config.enabled : false;
}
createButton(buttonConfig);

Doğru

const buttonConfig = {
text: "Google",
link: "https://www.google.com"
};
const createButton = config => {
config = Object.assign(
{
title: "Boş",
link: "#",
enabled: false
},
config
);
}
createButton(buttonConfig);

Yan etkilerden kaçınmak. Yani bir işi yaparken bir işi bozmak…

Fonksiyon, eğer parametreleri alıp işlem yapıp değer döndürmekten başka işler de yapıyorsa, yan etkilerinin olması kaçınılmazdır. Mesela fonksiyonunuz bir dosyaya yazı yazarken, işlem bitince yazdırılan değişkeni de siliyorsa bir sorun olabilir. Belki o değişken ve değeri kullanılacaktı. Bu tür yan etkiler kaçınılmaz olabilir, ancak her bir fonksiyonu yeteri kadar merkezileştirebilirseniz, yan etkilerle baş etmeniz daha kolay olacaktır.

Yanlış

let message = "Bu bir deneme yazısıdır.";const countMessageWords = text => {
message = text.split(" ");
return message.length;
console.log(message);
// Yan etki oldu, mesaj değişkeni artık 4 sonucunu veriyor.
}
countMessageWords(message);

Doğru

let message = "Bu bir deneme yazısıdır.";const countMessageWords = text => {
return text.split(" ");
}
const messageWordLength = countMessageWords(message).length;console.log(messageWordLength);

Diğer bir yan etki durumu da nesne, dizi gibi veri türleri ile çalışırken yaşanılan veri kaybı durumu olabilir. Mesela bir diziye yeni eleman eklemek istediğinizde, o an bellekte yaşanabilecek bir sorun ile tüm veriyi kaybedebilirsiniz. Bu gibi durumlarda nesneyi klonlayarak işlem yapmak gerekebilir. Klonlamak, bellekte fazlaca yer kaplayan bir durumdur, bu nedenle aşırı kullanılmaması gerekir.

Yanlış

const addItemToList = (list, item) => {
list.push({ item, date: Date.now() });
};

Doğru

const addItemToList = (list, item) => {
return [...list, { item, date: Date.now() }];
};

Global fonksiyonlar yazmayın. Globali gereksiz kodlarla kirletmek kötü bir yöntemdir. Çünkü başka kütüphanelerle veya API’lar ile çakışmanız olasıdır. Mesela JavaScript’te Array nesnesini, iki dizi arasındaki farkı gösteren bir different metodu ile genişletmek istediğinizi düşünün. Bunu Array.prototype ile yazabiliriz, ancak projeye dahil ettiğimiz başka bir framework veya library’de aynı isimle metot oluşturmuşsa çakışma yaşanacaktır. Prototipleme yerine istenilen nesneyi extend edip Class oluşturmak ve içinde metotları tanımlamak daha doğrudur.

Yanlış

Array.prototype.different = function different (comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
const totalList = [1,2,3,4,5];
const checkList = [2,5]
console.log(totalList.different(checkList)); //[1, 3, 4]

Doğru

class ExtendedArray extends Array {
different(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}

Fonksiyonel programlama yönelimini tercih edin. Fonksiyonel programlamanın test edilebilirliği daha kolaydır.

Yanlış

const numbers = [1,5,6,7];let totals = 0;for (let i = 0; i < numbers.length; i++) {
totals += numbers[i];
}
console.log(totals); //19

Doğru

const numbers = [1,5,6,7];const totals =  numbers.reduce(
(currentTotal, output) => currentTotal + output, 0
);
console.log(totals); //19

Koşulların kapsamasında tüm koşulları ve mantıksal değer döndüren fonksiyonları koşullarda kullanmayın.

Yanlış

if (data.state === "empty" && isActive(data)) {
// ...
}

Doğru

function shouldGenerateCard(data, user) {
return data.state === "empty" && isActive(user);
}
if (shouldGenerateCard(dataInstance, userInstance)) {
// ...
}

Olumsuz koşullardan kaçının.

Yanlış

if(!user.userActive()){
//
}

Doğru

if(user.userActive() === true){
//
}

Koşulların kendisinden de kaçının. Her bir koşul, sistemi yoran, ayrıca kodu kirleten bir işlemdir. Sürekli olarak koşullarla uğraşmanız kaçınılmaz olabilir. İmkansız gibi görünüyor ama bazen gerçekten de koşul kullanılması gerekiyor mu diye sorgulamanızda fayda var.

Yanlış

const calculate = (type, numberOne, numberTwo) => {
if(type === "sum"){
return numberOne + numberTwo;
}
if(type === "diff"){
return numberOne - numberTwo;
}
}

Doğru

const getDiff = (numberOne, numberTwo) => numberOne - numberTwo
const getSum = (numberOne, numberTwo) => numberOne + numberTwo
getDiff(1, 5); //-4
getSum(2, 5); //7

Gereksiz yazım denetiminden kaçının. Eğer kompleks verilerle çalışmıyorsanız yazım denetimi yapmak pek de gerekli değildir. Eğer gerçekten ihtiyacınız varsa, bu durumda TypeScript’e yönelmenizin vakti gelmiş demektir. JavaScript’te mümkün olduğunca tür denetimine ihtiyacınız olmayacak şekilde temiz, iyi okunabilir ve test edilebilir kodlar yazın.

Yanlış

const combine = ( a,b ) = {
if (
(typeof a === "number" && typeof b === "number") ||
(typeof a === "string" && typeof b === "string")
) {
return a + b;
}
throw new Error("Parametre türleri aynı değil.");
}

Doğru

const combine = ( a,b ) = {
return a + b;
}

Aşırı optimizasyon yapmaktan kaçının. Zaten modern tarayıcılar kodunuzu yeterince optimize edecektir. Eğer gerçekten de optimizasyon işini tarayıcının kendisinden daha iyi yapabileceğinizden emin değilseniz, optimizasyon işine pek bulaşmayın. Bu hem vakit kaybıdır, hem de kodunuzu kirletir.

Yanlış

// len değişkenine gereksiz, tarayıcı zaten durumu algılayacaktır.
for (let i = 0, len = user.length; i < len; i++) {
// ...
}

Doğru

for (let i = 0; i < user.length; i++) {
// ...
}

Ve fonksiyonlarla ilgili son olarak; kullanılmayan fonksiyonları silin. İlerde belki lazım olur, belki bir daha bu metodu yazmamam kalsın mantığıyla gereksiz kodları projede tutmayın.

Açıklamalar

Sadece işleyiş mantığı karmaşık olan şeyleri yorumlayın. Yorumlar zorunluluk değil veya bir şeyi açıklamak da değildir. İyi bir kod kendisini zaten açıklar.

Yanlış

// Sayılar dizide tutuluyor.
var numbers = [1,5,6,7];
// Her bir sayı tek tek toplanıp totals değişkenine aktarılıyor.
const totals = numbers.reduce(
(currentTotal, output) => currentTotal + output, 0
);
// Toplam değerler konsolda yazdırılıyor.
console.log(totals); //19

Doğru

var numbers = [1,5,6,7];
// Bütün sayıların toplamı
const totals = numbers.reduce(
(currentTotal, output) => currentTotal + output, 0
);
console.log(totals);

Kullanılmayan metotları yorum satırlarında bırakmayın.

Yanlış

getUserName();
//getUserNameFromData();

Doğru

getUserName();

Yorumlarda satırlarca açıklama eklemeyin. Eğer kodlarınızın takibini yapmak istiyorsanız herhangi bir versiyonlama sistemi kullanın ve smart-tag sistemi ile her bir işleminizi versiyonlayın. Yani uzun uzun açıklamalarınızı versiyonlarken yazın.

Yanlış

/* Kullanıcı adını çeken fonksiyon yazdık */
/* Bu fonksiyon önce data gelmiş mi diye bakar */
/* Data varsa içinden kullanıcı adını çeker */
getUserName();

Doğru

getUserName();

Konum belirten garip garip açıklama satırlarından kaçının. Genelde kodun içinde kaybolmuş kişiler gereğinden fazla uzamış kodun içinde aradığı önemli fonksiyonu bulmak için bazı desenler çizer.

Yanlış

//////////////////////   VERİYİ ÇEKEN METOT BURADA  //////////////////////
getData();

Doğru

getUserName();

Biçimlendirmeler

Biçimlendirmeler, programlama dillerinde zorunlu olmayan, ancak kodun okunabilirliği açısından zorunluluğu kaçınılmaz olan kod düzen sistemidir. Mesela kodlarda yer alan girintiler, sekmeler, boşluklar, çift veya tek tırnaklar gibi… Bunun için IDE’lerde birçok yerleşik veya ekstradan yüklenen plug-in’lerle kodlar biçimlendirilebilir, bunlardan en uygun olanını kullanın. Mesela kitapta Visual Studio Code’da yerleşik olarak Format Document veya Format Selection araçları kullanılabilirken, ekstra plug-in’lerden js-beautify da kullanılabilir.

Yanlış

const pi = 3.14
// Daire alanı hesapla
const calculateCirclearea = r =>{
return r*r *pi}

Doğru

const PI = 3.14;
// Daire alanı hesapla
const calculateCircleArea = r => {
return r * r * PI
}

Büyük/küçük harfleri ve özel karakterleri tutarlı kullanın.

Yanlış

const MONTH_IN_WEEK = 7;
const daysInyear = 365;
const books = ["Kitap 1", "Kitap 2"];
const Authors = ["Yazar 1", "Yazar 2"];
function getBooks() {}
function get_Authors() {}
class bookStore {}
class author {}

Doğru

const MONTH_IN_WEEK = 7;
const DAYS_IN_YEAR = 365;
const BOOKS = ["Kitap 1", "Kitap 2"];
const AUTHORS = ["Yazar 1", "Yazar 2"];
function getBooks() {}
function getAuthors() {}
class BookStore {}
class Author {}

Tanımlı olan bir fonksiyon başka bir yerden çağırılıyorsa, çağıran ile çağrılan birbirine yakın olsun. Doğal okuma düzeni yukarıdan aşağıya doğrudur. Bu nedenle çağırılan yeri fonksiyonun üzerinde tutmak idealdir. Ancak bazı framework’lerde yukarıda çağırdığınızda, fonksiyon henüz tanımlı olmadığı için (henüz fonksiyonun olduğu satıra gelinmedi), tanımsız fonksiyon gibi hatalar alabilirsiniz. Bu gibi durumlarda da fonksiyonların alt tarafında kullanabilirsiniz.

Yanlış

class Books {

constructor() {
}
func_1() {
//
}
func_3() {
//
}
func_2() {
//
}
define () {
this.func_1();
this.func_2();
this.func_3();
}
}
const book = new Books ();
book.define();

Doğru

class Books {

constructor() {
}
define() {
this.func_1();
this.func_2();
this.func_3();
}
func_1() {
//
}
func_2() {
//
}
func_3() {
//
}
}
const book = new Books ();
book.define();

--

--

Uğur GELİŞKEN

Full-stack Developer [ UI / UX | JAM Stack | ME(A,R,V)N | LAMP ], Author, Pro Gamer