Refactoring Desenleri: Davranışı Korumayı Öğrenen Kod

Bir kod tabanını iyileştirmek, her zaman yeni özellik eklemek anlamına gelmez. Bazen en değerli geliştirme; çalışan ama okunması zor, genişletmesi pahalı ve hata üretmeye yatkın kodu daha anlaşılır hâle getirmektir. Refactoring, dışarıdan gözlemlenen davranışı değiştirmeden kodun iç tasarımını düzenleme disiplinidir. Amaç yalnızca “güzel kod” yazmak değil; gelecekteki değişikliklerin maliyetini ve riskini azaltmaktır.

refactoring-desenleri-davranisi-23

``

Refactoring ile yeniden yazımı ayırmak önemlidir. Yeniden yazımda çözümün mimarisi, teknolojisi veya davranışı kökten değişebilir. Refactoring ise küçük, güvenli ve doğrulanabilir adımlarla ilerler. Bu yaklaşımın temel denklemi şöyle özetlenebilir:

\[Davranış_{önce} = Davranış_{sonra}\]

Buradaki davranış; dönen değerler, yan etkiler, hata durumları, API sözleşmeleri ve kullanıcıya görünen çıktıları kapsar. Kodun satır sayısının azalması tek başına başarı değildir; değişikliğin güvenle yapılabilmesi başarıdır.

Kavram Ana hedef Risk seviyesi Tipik örnek
Refactoring İç yapıyı iyileştirmek Düşük Uzun metodu bölmek
Bug fix Hatalı davranışı düzeltmek Orta Yanlış hesaplamayı düzeltmek
Yeni özellik Yeni davranış eklemek Orta/Yüksek Kupon sistemi eklemek
Rewrite Sistemi yeniden kurmak Yüksek Eski uygulamayı baştan yazmak

Güvenlik ağı: Testler

Refactoring’in yakıtı test, emniyet kemeri ise otomasyondur. Değişiklikten önce mevcut davranışı yakalayan testler bulunmalıdır. Test yoksa önce karakterizasyon testi yazmak akıllıcadır: Kodun şu an ne yaptığını, ideal olarak ne yapması gerektiğini değil, ölçülebilir biçimde kaydeder.

Örneğin aşağıdaki kod hem indirim mantığını hem de çıktı üretimini tek yerde topluyor:

function printOrder(customer, total) {
  let discount = 0;
  if (customer.type === "gold") discount = total * 0.20;
  else if (customer.type === "silver") discount = total * 0.10;

  const payable = total - discount;
  console.log(`${customer.name}: ${payable} TL`);
  return payable;
}

İlk refactoring adımı, hesaplamayı yan etkiden ayırmaktır. Extract Function deseniyle hesaplama ayrı bir fonksiyona taşınır:

function calculateDiscount(customer, total) {
  if (customer.type === "gold") return total * 0.20;
  if (customer.type === "silver") return total * 0.10;
  return 0;
}

function printOrder(customer, total) {
  const payable = total - calculateDiscount(customer, total);
  console.log(`${customer.name}: ${payable} TL`);
  return payable;
}

Bu dönüşümün kazancı, calculateDiscount fonksiyonunun konsol çıktısına ihtiyaç duymadan test edilebilmesidir. Ayrıca indirim kuralları büyüdüğünde ilgili değişiklik tek bir noktada yapılır.

Sık kullanılan desenler

Refactoring desenleri, kod kokularına verilen küçük reçetelerdir. Her kokunun tek bir çözümü yoktur; bağlam karar verir.

Kod kokusu Uygun desen Beklenen fayda
Çok uzun fonksiyon Extract Function Okunabilirlik ve test edilebilirlik
Anlamsız değişken adı Rename Variable Niyetin görünür olması
Tekrarlanan bloklar Extract Function / Pull Up Method Tek doğruluk kaynağı
Devasa koşul zinciri Replace Conditional with Polymorphism Yeni kural ekleme kolaylığı
Fazla parametre Introduce Parameter Object Daha anlamlı arayüz

Özellikle Rename küçümsenmemelidir. x, data veya flag gibi isimler, okuyucuyu zihinsel tersine mühendisliğe zorlar. İyi bir isim, yorum satırına duyulan ihtiyacı azaltır. Buna karşılık her satırı soyutlamak da zararlıdır: Aşırı parçalanmış kodda asıl akışı takip etmek zorlaşabilir.

Sağlıklı çalışma döngüsü basittir: önce testleri çalıştırın, tek küçük dönüşüm yapın, testleri yeniden çalıştırın ve değişikliği kaydedin. Risk kabaca değişiklik büyüklüğüyle artar: $Risk \propto Değişen\ Alan$. Bu yüzden “bir kerede temizlik” yerine kısa iterasyonlar tercih edilir.

Sonuçta refactoring, estetik bir hobi değil; yazılımın değişime direnç göstermesini azaltan mühendislik pratiğidir. Kod bugün çalışıyor olabilir. Asıl soru şudur: Yarın yeni bir kural geldiğinde, onu korkmadan değiştirebilecek misiniz?

Yorumlar