İçeriğe geç

Clean Code: Okunabilir Kod Yazmanın 10 Altın Kuralı

T
Tolgahan
· · 15 dk okuma · 64 görüntülenme

Clean Code: Okunabilir Kod Yazmanın 10 Altın Kuralı

Bir istatistiğe göre yazılımcılar zamanlarının %70'ini kod okuyarak, sadece %30'unu yazarak geçiriyor. Bu oranı düşün: yazdığın her satır kod, ileride (belki yarın, belki 6 ay sonra) birisi tarafından — muhtemelen sen dahil — onlarca kez okunacak. Eğer o kod okunması zor, karmaşık ve niyeti belirsizse, her okuma seansı zaman kaybı demek. Ama okunabilir, temiz ve niyetini açıkça ifade eden kod yazarsan, hem sen hem de ekip arkadaşların çok daha verimli çalışırsınız.

Robert C. Martin'in (Uncle Bob) ünlü sözü: *"Temiz kod, yazıldığından daha fazla okunur."* Bu yazıda, okunabilir kod yazmanın 10 altın kuralını gerçek örneklerle, Java ve Python dillerinde öğreneceksin. Her kural için önce kötü kodu görecek, neden kötü olduğunu anlayacak, sonra düzeltilmiş halini inceleyeceksin.

Kural 1: İsimler Her Şeyi Anlatmalı

İsimlendirme, kodun en temel iletişim aracı. İyi bir isim, yoruma gerek bırakmaz. Kötü bir isim, okuyucuyu tahmin oyununa zorlar.

// ❌ Kötü isimlendirme — hiçbir şey anlaşılmıyor
int d; // geçen gün sayısı
List<int[]> list1; // ne listesi?
String tp; // neyin kısaltması?

public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<>();
    for (int[] x : theList) {
        if (x[0] == 4) {      // 4 ne anlama geliyor?
            list1.add(x);
        }
    }
    return list1;
}
// ✅ Anlamlı isimlendirme — kod kendini anlatıyor
int daysSinceCreation;
List<Cell> flaggedCells;
String userType;

public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<>();
    for (Cell cell : gameBoard) {
        if (cell.isFlagged()) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

İkinci versiyonda hiçbir yorum satırına ihtiyaç yok. Kodun kendisi hikayeyi anlatıyor: "oyun tahtasındaki işaretlenmiş hücreleri getir."

Python'da da aynı prensip:

# ❌ Ne yaptığı belirsiz
def calc(lst):
    return [x for x in lst if x.a > 18 and x.s == 1]

# ✅ Apaçık
def get_active_adult_users(users):
    return [user for user in users if user.age > 18 and user.is_active]

İsimlendirme kuralları:

  • Değişkenler: Ne olduğunu söylesin → userAge, orderTotal, isActive

  • Fonksiyonlar: Ne yaptığını söylesin → calculateTax(), sendEmail(), validateInput()

  • Boolean'lar: Soru gibi olsun → isValid, hasPermission, canExecute

  • Sabitler: Anlamını söylesin → MAX_RETRY_COUNT, DEFAULT_TIMEOUT_SECONDS

  • Kısaltma kullanma: usr değil user, btn değil button, mgr değil manager

Kural 2: Fonksiyonlar Küçük ve Tek Sorumlu Olmalı

Bir fonksiyon tek bir iş yapmalı, o işi iyi yapmalı ve sadece o işi yapmalı. "Tek Sorumluluk Prensibi" (Single Responsibility Principle) fonksiyon seviyesinde de geçerli.

// ❌ 50+ satırlık monster fonksiyon — her şeyi yapıyor
public void processOrder(Order order) {
    // Validasyon
    if (order.getItems().isEmpty()) throw new IllegalArgumentException("Boş sipariş");
    if (order.getCustomer() == null) throw new IllegalArgumentException("Müşteri yok");
    for (OrderItem item : order.getItems()) {
        if (item.getQuantity() <= 0) throw new IllegalArgumentException("Geçersiz miktar");
        if (item.getPrice().compareTo(BigDecimal.ZERO) <= 0) throw new IllegalArgumentException("Geçersiz fiyat");
    }
    
    // Fiyat hesaplama
    BigDecimal subtotal = BigDecimal.ZERO;
    for (OrderItem item : order.getItems()) {
        subtotal = subtotal.add(item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())));
    }
    BigDecimal tax = subtotal.multiply(new BigDecimal("0.18"));
    BigDecimal total = subtotal.add(tax);
    order.setTotal(total);
    
    // Stok kontrolü ve güncelleme
    for (OrderItem item : order.getItems()) {
        int stock = inventoryService.getStock(item.getProductId());
        if (stock < item.getQuantity()) throw new OutOfStockException(item.getProductId());
        inventoryService.decreaseStock(item.getProductId(), item.getQuantity());
    }
    
    // Ödeme
    paymentService.charge(order.getCustomer(), total);
    
    // Veritabanına kaydet
    orderRepository.save(order);
    
    // Email gönder
    emailService.sendOrderConfirmation(order);
}
// ✅ Her fonksiyon tek iş yapıyor — okunabilir, test edilebilir
public void processOrder(Order order) {
    validateOrder(order);
    calculateTotal(order);
    reserveInventory(order);
    chargePayment(order);
    saveOrder(order);
    notifyCustomer(order);
}

private void validateOrder(Order order) {
    if (order.getItems().isEmpty()) throw new IllegalArgumentException("Boş sipariş");
    if (order.getCustomer() == null) throw new IllegalArgumentException("Müşteri yok");
    order.getItems().forEach(this::validateOrderItem);
}

private void validateOrderItem(OrderItem item) {
    if (item.getQuantity() <= 0) throw new IllegalArgumentException("Geçersiz miktar");
    if (item.getPrice().compareTo(BigDecimal.ZERO) <= 0) throw new IllegalArgumentException("Geçersiz fiyat");
}

private void calculateTotal(Order order) {
    BigDecimal subtotal = order.getItems().stream()
        .map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
        .reduce(BigDecimal.ZERO, BigDecimal::add);
    
    BigDecimal tax = subtotal.multiply(TAX_RATE);
    order.setTotal(subtotal.add(tax));
}

Refactored versiyonu oku: processOrder metodu bir öykü gibi okunuyor — doğrula, hesapla, envanteri ayır, ödeme al, kaydet, bilgilendir. Her adımın detayı kendi fonksiyonunda gizli.

Fonksiyon boyutu kuralları:

  • İdeal: 5-20 satır. 30 satırı geçiyorsa muhtemelen bölünmeli.

  • Parametre sayısı: İdeal 0-2, maksimum 3. Daha fazlası varsa bir nesne (DTO/record) kullan.

  • İç içe (nested) yapı derinliği: Maksimum 2 seviye. 3+ seviye iç içe if/for varsa, erken return veya alt fonksiyon kullan.

Kural 3: Yorumlar Neden'i Anlatsın, Ne'yi Değil

İyi kod kendini açıklar — yoruma ihtiyaç duymaz. Eğer bir yorum yazmak zorunda hissediyorsan, önce kodu daha anlaşılır hale getirmeyi dene. Yorum, kodun yetersizliğinin itirafıdır.

// ❌ Gereksiz yorumlar — kod zaten bunu söylüyor
// Kullanıcı yaşını kontrol et
if (user.getAge() >= 18) {
    // Kullanıcıyı ekle
    users.add(user);
}

// Döngüyü başlat
for (int i = 0; i < list.size(); i++) {
    // Elemanı al
    String item = list.get(i);
}
// ✅ Yorum ancak "neden" sorusuna cevap veriyorsa yazılmalı
// Yasal düzenleme (KVKK) gereği 18 yaş altı kullanıcı verisi işlenemez
if (user.getAge() >= 18) {
    users.add(user);
}

// API rate limit aşımını önlemek için 100ms bekliyoruz
// Bakınız: https://api.example.com/docs/rate-limits
Thread.sleep(100);

// TODO: Bu geçici çözüm — v2.0'da OAuth2 ile değiştirilecek
String token = generateLegacyToken(user);

Yorum yazılması gereken durumlar:

  • Neden: İş kuralı, yasal gereklilik, teknik zorunluluk

  • TODO/FIXME: Geçici çözümler, bilinen eksiklikler

  • Uyarı: Performans, thread-safety, beklenmedik davranış gibi tuzaklar

  • API dokümantasyonu: Public API'ler için Javadoc/docstring

Yorum yazılmaması gereken durumlar:

  • Kodun zaten söylediği şeyi tekrarlamak

  • Kötü isimlendirmeyi yorum ile telafi etmeye çalışmak

  • Eski, güncelliğini yitirmiş yorumlar (bunlar aktif olarak zararlıdır)

Kural 4: DRY — Kendini Tekrar Etme

DRY (Don't Repeat Yourself) prensibi: Her bilgi parçası sistemde tek bir yerde ifade edilmeli. Aynı mantık iki yerde varsa, biri değiştiğinde diğerini unutursun — ve bug doğar.

# ❌ Aynı doğrulama mantığı 3 farklı yerde tekrarlanıyor
def create_user(name, email):
    if not name or len(name) < 2:
        raise ValueError("İsim en az 2 karakter olmalı")
    if not email or "@" not in email:
        raise ValueError("Geçerli bir email girin")
    # kullanıcı oluştur...

def update_user(user_id, name, email):
    if not name or len(name) < 2:
        raise ValueError("İsim en az 2 karakter olmalı")
    if not email or "@" not in email:
        raise ValueError("Geçerli bir email girin")
    # kullanıcı güncelle...

def invite_user(name, email):
    if not name or len(name) < 2:
        raise ValueError("İsim en az 2 karakter olmalı")
    if not email or "@" not in email:
        raise ValueError("Geçerli bir email girin")
    # davet gönder...
# ✅ DRY — doğrulama mantığı tek yerde
def validate_user_data(name, email):
    """Kullanıcı verilerini doğrula. Geçersizse ValueError fırlatır."""
    if not name or len(name) < 2:
        raise ValueError("İsim en az 2 karakter olmalı")
    if not email or "@" not in email:
        raise ValueError("Geçerli bir email girin")

def create_user(name, email):
    validate_user_data(name, email)
    # kullanıcı oluştur...

def update_user(user_id, name, email):
    validate_user_data(name, email)
    # kullanıcı güncelle...

def invite_user(name, email):
    validate_user_data(name, email)
    # davet gönder...

Artık email doğrulama kuralını değiştirmek istediğinde tek bir yeri güncellersin. Ama dikkat: DRY'ı aşırıya kaçırma. İki kod parçası aynı görünüyor diye her zaman birleştirilmeli anlamına gelmez. Eğer farklı nedenlerle değişme olasılıkları varsa, ayrı tutmak daha doğru olabilir. Buna "yanlış soyutlama" (wrong abstraction) denir ve DRY'dan daha tehlikelidir.

Kural 5: KISS — Basit Tut

KISS (Keep It Simple, Stupid) prensibi: Bir işi yapmanın en basit yolunu seç. Karmaşıklık, sadece gerçekten gerektiğinde eklensin.

// ❌ Gereksiz karmaşıklık — basit bir null kontrolü için Strategy Pattern
interface NullCheckStrategy {
    boolean isNull(Object obj);
}
class DefaultNullCheckStrategy implements NullCheckStrategy {
    public boolean isNull(Object obj) { return obj == null; }
}
class NullCheckExecutor {
    private final NullCheckStrategy strategy;
    // ... constructor, getter, setter ...
    public boolean execute(Object obj) { return strategy.isNull(obj); }
}

// ✅ KISS — ihtiyacın olan kadar basit
if (user == null) {
    throw new UserNotFoundException(userId);
}

Bu abartılı bir örnek gibi görünebilir ama gerçek projelerde benzer aşırı mühendislik (over-engineering) sık karşılaşılan bir problem. "Ya ileride lazım olursa?" düşüncesiyle gereksiz soyutlama katmanları eklenir. Bu da bizi bir sonraki kurala getiriyor.

Kural 6: YAGNI — İhtiyacın Olmayan Şeyi Yazma

YAGNI (You Aren't Gonna Need It) prensibi: Şu an ihtiyacın olmayan bir özelliği, "belki ileride lazım olur" diye şimdiden yazma.

# ❌ YAGNI ihlali — henüz kimse XML/CSV export istemedi
class ReportExporter:
    def export_json(self, data):
        return json.dumps(data)
    
    def export_xml(self, data):
        # 50 satır XML oluşturma kodu
        # Hiç kullanılmadı, hiç test edilmedi
        pass
    
    def export_csv(self, data):
        # 30 satır CSV oluşturma kodu
        # Hiç kullanılmadı, hiç test edilmedi
        pass
    
    def export_yaml(self, data):
        # Belki bir gün lazım olur diye yazıldı
        pass

# ✅ YAGNI — sadece şu an gerekeni yaz
class ReportExporter:
    def export_json(self, data):
        return json.dumps(data, ensure_ascii=False, indent=2)
    # XML lazım olduğunda ekleriz — şimdi değil

Kullanılmayan kod bakım yükü oluşturur, test edilmemiş olduğu için güvenilmezdir ve kodu gereksiz yere şişirir. İhtiyaç oluştuğunda eklemek her zaman daha kolaydır çünkü o zaman gerçek gereksinimleri bilirsin.

Kural 7: Erken Return ile İç İçeliği Azalt

Derin iç içe yapılar (deeply nested code) kodun okunmasını zorlaştırır. Guard clause (koruma cümlesi) pattern'i ile erken return yaparak iç içeliği düzleştir:

// ❌ "Ok kodu" — sağa doğru büyüyen piramit
public String processPayment(Order order) {
    if (order != null) {
        if (order.getCustomer() != null) {
            if (order.getTotal().compareTo(BigDecimal.ZERO) > 0) {
                if (paymentGateway.isAvailable()) {
                    PaymentResult result = paymentGateway.charge(order);
                    if (result.isSuccess()) {
                        return "Ödeme başarılı";
                    } else {
                        return "Ödeme başarısız: " + result.getError();
                    }
                } else {
                    return "Ödeme sistemi kullanılamıyor";
                }
            } else {
                return "Geçersiz tutar";
            }
        } else {
            return "Müşteri bilgisi eksik";
        }
    } else {
        return "Sipariş bulunamadı";
    }
}
// ✅ Guard clause ile düz, okunabilir kod
public String processPayment(Order order) {
    if (order == null) return "Sipariş bulunamadı";
    if (order.getCustomer() == null) return "Müşteri bilgisi eksik";
    if (order.getTotal().compareTo(BigDecimal.ZERO) <= 0) return "Geçersiz tutar";
    if (!paymentGateway.isAvailable()) return "Ödeme sistemi kullanılamıyor";

    PaymentResult result = paymentGateway.charge(order);
    return result.isSuccess() 
        ? "Ödeme başarılı" 
        : "Ödeme başarısız: " + result.getError();
}

İkinci versiyon tam aynı mantığı uygular ama düz bir akışla okunur. Her guard clause bir "eğer bu yanlışsa, burada dur" demek. Tüm kontroller geçildikten sonra asıl iş mantığına gelirsin.

Python'da da aynı prensip:

# ❌ İç içe
def get_discount(user, product):
    if user is not None:
        if user.is_premium:
            if product.is_discountable:
                if product.price > 100:
                    return product.price * 0.2
                else:
                    return product.price * 0.1
    return 0

# ✅ Guard clause
def get_discount(user, product):
    if user is None or not user.is_premium:
        return 0
    if not product.is_discountable:
        return 0
    
    discount_rate = 0.2 if product.price > 100 else 0.1
    return product.price * discount_rate

Kural 8: Sihirli Sayılar ve String'ler Kullanma

Kodda anlamsız sayılar ve string'ler "magic number/string" olarak adlandırılır. Bunlar kodun okunabilirliğini ve bakımını zorlaştırır:

# ❌ Sihirli sayılar — 86400 ne? 3 ne? 0.18 ne?
def calculate_subscription(user):
    if time.time() - user.created_at > 86400 * 365:
        return user.base_price * 0.85
    if user.referral_count >= 3:
        return user.base_price * 0.90
    return user.base_price * 1.18

# ✅ Sabitlerle anlamlı hale geldi
SECONDS_IN_A_YEAR = 86400 * 365
LOYALTY_DISCOUNT_RATE = 0.85
REFERRAL_DISCOUNT_RATE = 0.90
MIN_REFERRALS_FOR_DISCOUNT = 3
TAX_MULTIPLIER = 1.18

def calculate_subscription(user):
    account_age_seconds = time.time() - user.created_at
    
    if account_age_seconds > SECONDS_IN_A_YEAR:
        return user.base_price * LOYALTY_DISCOUNT_RATE
    
    if user.referral_count >= MIN_REFERRALS_FOR_DISCOUNT:
        return user.base_price * REFERRAL_DISCOUNT_RATE
    
    return user.base_price * TAX_MULTIPLIER

Java'da enum kullanmak daha da iyi:

// ❌ String karşılaştırma — typo riski, refactoring zorluğu
if (order.getStatus().equals("pending")) { ... }
if (order.getStatus().equals("pendng")) { ... } // typo! Derleme hatası yok!

// ✅ Enum kullan — type-safe, IDE desteği, refactoring kolay
public enum OrderStatus {
    PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED
}

if (order.getStatus() == OrderStatus.PENDING) { ... }

Kural 9: Code Smell'leri Tanı ve Refactor Et

Code smell (kod kokusu), kodun çalışmasına engel olmayan ama daha derin bir tasarım problemine işaret eden belirtiler. Martin Fowler'ın tanımladığı en yaygın code smell'ler:

Long Method (Uzun Metot)

30+ satır fonksiyon. Çözüm: Alt fonksiyonlara böl (Kural 2).

Feature Envy (Özellik Kıskançlığı)

Bir sınıf, kendi verisi yerine başka sınıfın verisini sürekli kullanıyor:

// ❌ Feature Envy — OrderPrinter sürekli Order'ın iç detaylarını kurcalıyor
class OrderPrinter {
    public String formatOrder(Order order) {
        return order.getCustomer().getName() + " - " +
               order.getItems().size() + " ürün - " +
               order.getItems().stream()
                   .map(i -> i.getPrice().multiply(BigDecimal.valueOf(i.getQuantity())))
                   .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

// ✅ Mantığı verinin sahibine taşı
class Order {
    public String getSummary() {
        return customer.getName() + " - " + items.size() + " ürün - " + getTotal();
    }
    
    public BigDecimal getTotal() {
        return items.stream()
            .map(OrderItem::getSubtotal)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

Primitive Obsession (İlkel Takıntı)

Her şeyi String ve int ile temsil etmek:

# ❌ Her şey string — tip güvenliği yok
def create_user(name: str, email: str, phone: str, address: str):
    # email ile phone'u karıştırma riski — ikisi de str
    pass

# ✅ Değer nesneleri (Value Object) kullan
@dataclass(frozen=True)
class Email:
    value: str
    def __post_init__(self):
        if "@" not in self.value:
            raise ValueError(f"Geçersiz email: {self.value}")

@dataclass(frozen=True)
class Phone:
    value: str
    def __post_init__(self):
        if not self.value.replace("+", "").replace(" ", "").isdigit():
            raise ValueError(f"Geçersiz telefon: {self.value}")

def create_user(name: str, email: Email, phone: Phone, address: str):
    # Email ve Phone karıştırılamaz — farklı tipler
    pass

Boolean Parameter (Flag Argument)

Boolean parametre alan fonksiyonlar genelde iki iş yapıyordur:

// ❌ Boolean parametre — çağıran tarafta ne anlama geldiği belirsiz
sendEmail(user, order, true, false);  // true ve false ne?

// ✅ Ayrı, anlamlı metotlar
sendOrderConfirmationEmail(user, order);
sendOrderShippedEmail(user, order);

Kural 10: Tutarlılık Her Şeyden Önemli

Tüm kurallardan daha önemli bir kural var: tutarlılık. Ekipte bir stil belirleyin ve herkes buna uysun. Yarısı camelCase yarısı snake_case kullanan bir proje, her iki stilin de "doğru" olduğu bir projeden daha kötüdür.

# ❌ Tutarsız — her fonksiyon farklı stilde
def getUserData(userId):        # camelCase
    pass

def calculate_total(order):     # snake_case
    pass

def ProcessPayment(data):       # PascalCase
    pass

def send_email(to):             # snake_case
    pass

# ✅ Tutarlı — Python convention'ına uygun, hepsi snake_case
def get_user_data(user_id):
    pass

def calculate_total(order):
    pass

def process_payment(data):
    pass

def send_email(to):
    pass

Tutarlılık kontrol listesi:

  • Linter kullan: Java'da Checkstyle veya SpotBugs, Python'da flake8/ruff, formatlamak için Java'da google-java-format, Python'da black

  • Formatter paylaş: .editorconfig veya IDE formatter ayarlarını versiyon kontrolüne koy

  • Code review yap: Pull request'lerde stil tutarlılığını kontrol et

  • Convention belirle: Ekipçe kabul edilen bir coding style guide oluştur

Refactoring Örneği: Hepsini Bir Arada

Şimdi tüm kuralları birleştiren bir refactoring örneği yapalım. Aşağıdaki "kötü" kodu adım adım temizleyeceğiz:

# ❌ ÖNCE — birçok code smell barındıran kod
def proc(d):
    r = []
    for i in d:
        if i["t"] == "A" or i["t"] == "B":
            if i["a"] >= 18:
                if i["s"] == 1:
                    # İndirim hesapla
                    if i["t"] == "A":
                        p = i["p"] * 0.9  # %10 indirim
                    else:
                        p = i["p"] * 0.8  # %20 indirim
                    i["fp"] = p + (p * 0.18)  # KDV ekle
                    r.append(i)
    return r

Bu kodda neler yanlış?

  • Kötü isimlendirme (d, r, i, t, a, s, p, fp)

  • Sihirli sayılar (18, 0.9, 0.8, 0.18, 1)

  • Derin iç içelik (4 seviye)

  • Fonksiyon birden fazla iş yapıyor

  • Yorum satırları kodu "düzeltmeye" çalışıyor

# ✅ SONRA — temiz, okunabilir, bakımı kolay
from dataclasses import dataclass
from enum import Enum
from typing import Optional

class MembershipType(Enum):
    PREMIUM = "A"
    STANDARD = "B"
    BASIC = "C"

MINIMUM_AGE = 18
TAX_RATE = 0.18

DISCOUNT_RATES = {
    MembershipType.PREMIUM: 0.10,   # %10 indirim
    MembershipType.STANDARD: 0.20,  # %20 indirim
}

@dataclass
class Customer:
    name: str
    membership_type: str
    age: int
    is_active: bool
    base_price: float
    final_price: Optional[float] = None

    def is_eligible_for_discount(self) -> bool:
        """Müşterinin indirime uygun olup olmadığını kontrol eder."""
        if self.age < MINIMUM_AGE:
            return False
        if not self.is_active:
            return False
        
        eligible_types = {t.value for t in DISCOUNT_RATES}
        return self.membership_type in eligible_types

    def calculate_final_price(self) -> float:
        """İndirimli ve KDV dahil nihai fiyatı hesaplar."""
        membership = MembershipType(self.membership_type)
        discount_rate = DISCOUNT_RATES.get(membership, 0)
        
        discounted_price = self.base_price * (1 - discount_rate)
        price_with_tax = discounted_price * (1 + TAX_RATE)
        return round(price_with_tax, 2)


def get_discounted_customers(customers: list[Customer]) -> list[Customer]:
    """İndirime uygun müşterilerin nihai fiyatlarını hesaplayıp döndürür."""
    eligible_customers = []
    
    for customer in customers:
        if not customer.is_eligible_for_discount():
            continue
        
        customer.final_price = customer.calculate_final_price()
        eligible_customers.append(customer)
    
    return eligible_customers

Refactored versiyon:

  • Anlamlı isimlerproc yerine get_discounted_customers, d yerine customers

  • Sabitler18 yerine MINIMUM_AGE, 0.18 yerine TAX_RATE

  • Guard clause — iç içe if yerine continue ile erken çıkış

  • Tek sorumluluk — eligibility kontrolü ve fiyat hesaplama ayrı metotlar

  • Enum — string karşılaştırma yerine type-safe enum

  • Dataclass — dict yerine yapılandırılmış veri

Yaygın Hatalar ve Tuzaklar

1. Aşırı Mühendislik (Over-Engineering)

Clean code demek "en karmaşık tasarım pattern'ini uygula" demek değil. 3 sınıfla çözülebilecek bir probleme 15 sınıf, 5 interface ve 3 abstract class yazmak clean code'un antitezidir.

2. Erken Soyutlama

İlk tekrarda soyutlama yapma. Bir pattern'in gerçekten tekrarlandığını en az 2-3 kez gördükten sonra soyutla. "Rule of Three" — üç kez tekrarlanana kadar soyutlama.

3. Yorum Yazmayı Tamamen Bırakmak

"Clean code'da yorum olmaz" diye yanlış bir inanç var. Yorum ne yapıldığını değil, neden yapıldığını anlatmalı. Yasal gereklilikler, karmaşık algoritmalar, beklenmedik davranışlar yorumla açıklanmalı.

4. Refactoring'i Sonsuza Ertelemek

"Sonra temizleriz" en tehlikeli cümle. Teknik borç (technical debt) birikir ve faizi katlanarak artar. Boy Scout Rule'u uygula: *"Kodu bulduğundan daha temiz bırak."* Her dokunduğun dosyada küçük bir iyileştirme yap.

Best Practices

  1. Linter ve formatter kullan. Stil tartışmalarını otomatize et. Java'da google-java-format, Python'da black + ruff. CI/CD'de kontrol et.

  2. Anlamlı commit mesajları yaz. "fix" değil "fix: null pointer exception in OrderService.calculateTotal when items list is empty". Commit mesajı da koddur.

  3. Code review kültürü oluştur. Her PR en az bir kişi tarafından review edilmeli. Review'da "bu isim daha açıklayıcı olabilir" gibi clean code geri bildirimleri ver.

  4. Teknik borcu yönet. TODO ve FIXME yorumlarını takip et. Sprint'lerde refactoring için zaman ayır. "Sonra yaparız" demek "asla yapmayacağız" demek.

  5. Testler olmadan refactor etme. Refactoring'den önce testlerin olduğundan emin ol. Test olmadan refactoring, paraşütsüz atlama gibidir.

  6. Küçük adımlarla refactor et. Tüm sistemi bir seferde yeniden yazmaya çalışma. Her seferinde bir fonksiyonu, bir sınıfı temizle. Boy Scout Rule.

Sonuç

Clean code bir lüks değil, profesyonel yazılımcılığın temel gereksinimi. Bu yazıda öğrendiğin 10 kuralı özetleyelim:

  1. İsimler her şeyi anlatmalı — isim, en iyi dokümantasyondur

  2. Fonksiyonlar küçük ve tek sorumlu olmalı — 5-20 satır, tek iş

  3. Yorumlar neden'i anlatsın — ne'yi değil, neden'i açıkla

  4. DRY — kendini tekrar etme, ama yanlış soyutlamadan kaçın

  5. KISS — basit tut, gereksiz karmaşıklık ekleme

  6. YAGNI — ihtiyacın olmayan şeyi yazma

  7. Erken return ile iç içeliği azalt

  8. Sihirli sayılar kullanma — sabitler ve enum'lar kullan

  9. Code smell'leri tanı ve refactor et

  10. Tutarlılık — ekipçe bir stil belirle ve ona uy

Bu kuralların hiçbiri mutlak değil — her birinin istisnaları var. Önemli olan prensipleri anlamak ve bilinçli kararlar vermek. "Bu kodu 6 ay sonra ilk kez gören biri anlayabilir mi?" sorusunu sürekli sor. Cevap "evet" ise, temiz kod yazıyorsun demektir.

Paylaş:
Son güncelleme: Jun 05, 2026

Yorumlar

Giriş yapın ve yorum bırakın.

Henüz yorum yok

Düşüncelerinizi paylaşan ilk siz olun!

Bu yazıyı beğendiniz mi?

Bültene abone olun ve yeni yazılardan ilk siz haberdar olun. Spam yok, söz.

İlgili Yazılar