Clean Code: Okunabilir Kod Yazmanın 10 Altın Kuralı
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,isActiveFonksiyonlar: Ne yaptığını söylesin →
calculateTax(),sendEmail(),validateInput()Boolean'lar: Soru gibi olsun →
isValid,hasPermission,canExecuteSabitler: Anlamını söylesin →
MAX_RETRY_COUNT,DEFAULT_TIMEOUT_SECONDSKısaltma kullanma:
usrdeğiluser,btndeğilbutton,mgrdeğilmanager
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ğilKullanı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_rateKural 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_MULTIPLIERJava'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
passBoolean 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):
passTutarlı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ş:
.editorconfigveya IDE formatter ayarlarını versiyon kontrolüne koyCode 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 rBu 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_customersRefactored versiyon:
Anlamlı isimler —
procyerineget_discounted_customers,dyerinecustomersSabitler —
18yerineMINIMUM_AGE,0.18yerineTAX_RATEGuard clause — iç içe if yerine
continueile 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
Linter ve formatter kullan. Stil tartışmalarını otomatize et. Java'da google-java-format, Python'da black + ruff. CI/CD'de kontrol et.
Anlamlı commit mesajları yaz.
"fix"değil"fix: null pointer exception in OrderService.calculateTotal when items list is empty". Commit mesajı da koddur.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.
Teknik borcu yönet.
TODOveFIXMEyorumlarını takip et. Sprint'lerde refactoring için zaman ayır. "Sonra yaparız" demek "asla yapmayacağız" demek.Testler olmadan refactor etme. Refactoring'den önce testlerin olduğundan emin ol. Test olmadan refactoring, paraşütsüz atlama gibidir.
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:
İsimler her şeyi anlatmalı — isim, en iyi dokümantasyondur
Fonksiyonlar küçük ve tek sorumlu olmalı — 5-20 satır, tek iş
Yorumlar neden'i anlatsın — ne'yi değil, neden'i açıkla
DRY — kendini tekrar etme, ama yanlış soyutlamadan kaçın
KISS — basit tut, gereksiz karmaşıklık ekleme
YAGNI — ihtiyacın olmayan şeyi yazma
Erken return ile iç içeliği azalt
Sihirli sayılar kullanma — sabitler ve enum'lar kullan
Code smell'leri tanı ve refactor et
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.
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
Observer Design Pattern: Nesneler Arası Olay Tabanlı İletişim
Observer Design Pattern nedir, nasıl implemente edilir ve gerçek projelerde nasıl kullanılır? Java ve Spring örnekleriyl...
Strategy Design Pattern: Koşullu Mantığı Zarif Kodla Değiştirmenin Yolu
Strategy Design Pattern nedir, neden kullanılır, Java ve Spring Boot ile nasıl uygulanır? if-else cehenneminden kurtulma...
Microservices İletişim Desenleri
Microservice mimarisinde servisler arası iletişim: REST, gRPC, Message Queue, Circuit Breaker, Saga Pattern ve Event-Dri...