Kod Yeniden Düzenleme Teknikleri: Teknik Borcu Azalt

Summary

Kod yeniden düzenleme, teknik borcu azaltmanın ölçülebilir yoludur. Bu rehberde Extract Method, koşulluyu polimorfizmle değiştirme, hotspot analizi ve hazırlayıcı refactoring mindset'i ele alıyoruz. BCG 2024 verilerine göre sistematik refactoring, anlık müdahalelere kıyasla 3 kat daha yüksek ROI sağlıyor. Pazartesi sabahı başlayabileceğin somut bir eylem planı var.

Yazılım mühendisi refactoring oturumu için çift monitörlü iş istasyonunda kodu inceliyor

Kod Yeniden Düzenleme Teknikleri: Teknik Borcu Gerçekten Azaltanlar

Kod yeniden düzenleme teknikleri ekibindeki hiç kimsenin dokunmak istemediği o modülü iyileştirmenin ölçülebilir yoludur. Bug'lar orada kümeleniyor. Oraya dokunan her PR, inceleme süresini iki katına çıkarıyor. Ekibindeki üç engineer bu modülü bağımsız olarak "hayalet kanat" diye adlandırmış olabilir. Refactoring yapmak gerektiğini biliyorsun ama son deneyen kişinin iki sprint boyunca içinde kaybolduğunu ve kırık bir feature flag ile çıktığını da biliyorsun.

Kod yeniden düzenleme teknikleri "değişkeni yeniden adlandır" ile "tüm servis sınırını yeniden yapılandır" arasında geniş bir yelpazede yer alır. Bu rehber gerçekten metrikleri hareket ettirenleri kapsar: yeni engineer'ların ilk commit süresi, modül başına bug oranı ve PR inceleme süresi.

Refactoring'in bir lüks olmadığını veriler de doğruluyor. Stack Overflow 2024 anketinde geliştiricilerin yüzde 62'si teknik borç nedeniyle hayal kırıklığı yaşadığını belirtiyor. BCG 2024 raporuna göre ise sistematik refactoring, anlık müdahalelere kıyasla 3 kat daha yüksek ROI sağlıyor. Mesele "mükemmel kod" değil; ölçülebilir metrikler üzerinde etkisi olan teknik seçimler yapmak.

Extract Method: Bugün Yapabileceğin Tek Teknik

Eğer bir codebase'de yalnızca tek teknik uygulayabilseydin, Extract Method olurdu. Uzun bir fonksiyonun içinde tek bir tutarlı iş yapan bir kod bloğu tespit ediyorsun, onu adlandırılmış ayrı bir fonksiyona çıkarıyorsun. İki anlık fayda elde ediyorsun: ana fonksiyon okunabilir hale geliyor, çıkarılan fonksiyon ise izole olarak test edilebilir hale geliyor.

Kullandığım basit kural şu: bir kod bloğunun ne yaptığını açıklamak için yorum yazmak zorunda kaldıysan, o blok zaten bir fonksiyon olmalıydı. Fonksiyon adı yorum görevi görür ve yorumların aksine fonksiyon adları eskidiğinde build'i kırar.

Bu tekniği uygulamanın doğru zamanı: fonksiyonun 20 satırı aştığı ve birden fazla soyutlama seviyesi barındırdığı her an. Örneğin kullanıcı kaydı yapan bir fonksiyon hem veri doğrulama hem e-posta gönderme hem de veritabanı yazma işini aynı anda yapıyorsa, üç ayrı fonksiyon çıkarılabilir: validate_user_input, send_welcome_email, persist_user. Ana fonksiyon artık sadece bu üçünü sırayla çağıran bir orkestratör haline gelir. Okumak 5 saniye sürer.

Extract Method'un düşünmediğin bir yan faydası: yeni bir engineer repo'ya girdiğinde, iyi adlandırılmış küçük fonksiyonlar bir navigasyon haritası işlevi görür. "Bu fonksiyon ne yapıyor?" sorusu 10 satır kodu okumak yerine fonksiyon adını okumakla yanıtlanır. Bu fark, 5 kişilik bir takımda haftada birkaç saate karşılık gelir.

İki engineer ayakta masa başında kod incelemesi yaparken, arka planda mimari diyagram olan beyaz tahta görülüyor

Koşulluyu Polimorfizmle Değiştir

Uzun if/elif/else zincirleri veya bir tip alanını kontrol eden switch ifadeleri, kodu genişletmeyi zorlaştıran en yaygın pattern'lardan biridir. Yeni bir ödeme yöntemi eklerken mevcut bir if/else zincirine bir dal daha eklemek zorunda kalmak, bu durumun klasik göstergesidir. Her yeni özellik, mevcut kodu değiştirmeyi gerektiriyor ve bu, yanlışlıkla mevcut davranışları bozma riskini her seferinde yeniden yaratıyor.

Çözüm, koşulluyu bir sınıf hiyerarşisi veya strateji pattern'ı ile değiştirmektir. Her case, aynı interface'e sahip kendi sınıfı haline gelir. Yeni bir ödeme yöntemi eklemek artık mevcut koda dokunmak değil, yeni bir sınıf yazmak anlamına gelir.

Bu tekniği atla eğer koşulunun iki dalı varsa ve büyümesi olası değilse. Polimorfizmin ek yükü var: bir fonksiyon yerine artık birden fazla dosyan var. ROI yalnızca büyük ölçekte ortaya çıkar. Genişleme noktaları belirsizken polimorfizme koşmak over-engineering'in bir biçimidir.

Praktik bir kural: bir if/else bloku üçüncü bir case eklendiğinde "buraya bir daha geleceğim" hissini veriyorsa, polimorfizme geçme zamanı gelmiştir.

Hotspot Analizi: Önce Nereyi Yeniden Düzenleyeceğini Belirle

Siklomalık karmaşıklığı değişim frekansıyla birleştir. Bir modül gerçekten berbat olabilir ama üç yıldır dokunulmamış olabilir. Onu yeniden düzenlemek arkeoloji, mühendislik değil. Sana maliyet yaratan modüller hem dağınık hem de ekibinin her hafta içinde olduğu modüllerdir.

Praktik uygulama iki adımda tamamlanır. Birincisi, değişim frekansı: git log --format=format: --name-only | sort | uniq -c | sort -rg komutu en sık değiştirilen dosyaları sıralar. Bu komut sana son altı aydaki commit geçmişinde en çok değiştirilen dosyaların listesini verir. İkincisi, karmaşıklık: bu listeyi bir karmaşıklık metriği aracıyla, Python projesinde radon veya lizard, Java projesinde SonarQube, Go projesinde gocyclo, birleştirirsin. Sonuç iki boyutlu bir matristir.

Yüksek karmaşıklık artı yüksek değişim frekansı ilk hedef anlamına gelir. BCG 2024 raporuna göre sistematik refactoring, anlık müdahalelere kıyasla 3 kat daha yüksek ROI sağlıyor. Bu matris olmadan refactoring önceliği sezgisel bir karardır. Matrisle birlikte veri odaklı bir karar haline gelir ve ekibinle yapacağın her tartışmada savunulabilir bir dayanağın olur.

Gerçek bir örnek: bir e-ticaret repo'sunda checkout modülünün hem en yüksek siklomalık karmaşıklık skoruna sahip olduğunu hem de son üç ayda en sık değiştirilen beş dosyadan biri olduğunu fark edebilirsin. Bu çakışma, refactoring zamanı ve bütçesinin nereye yatırılacağını söyler.

Geliştiricinin ellerinin klavyede olduğu yakın çekim, arka planda koyu IDE ekranında sözdizimi vurgulı kod görülüyor

Branch-by-Abstraction: Canlı Sistemlerde Yeniden Düzenleme

Mevcut uygulamayı sarmalayan bir soyutlama oluştur, çağıranları bu soyutlamayı kullanacak şekilde taşı, yeni uygulamayı arkada yaz, sonra geçiş yap. Strangler fig pattern'ının servis seviyesinde çalışma biçimi budur.

Büyük e-ticaret ve fintech şirketlerinde yaygın olan monolitten mikroservise geçiş projelerinde bu teknik hayatta kalmanın yoludur. Sistemi durdurmadan yeni yapıyı inşa edersin. Eski ödeme modülünü yeni bir payment service ile değiştirirken, ikisi aynı anda var olabilir çünkü her ikisi de aynı interface'i uygular.

Adımlar net olmalı: önce interface'i tanımla ve mevcut kodun bunu uyguladığından emin ol. Ardından yeni implementasyonu yaz ve feature flag arkasına al. Testler geçince flag'i aç ve trafiği yeni implementasyona yönlendir. Son adımda eski implementasyonu sil.

Kritik nokta: soyutlamayı kalıcı hale getirme. Geçiş tamamlandıktan sonra eski uygulamayı sil ve soyutlamayı isteğe bağlı hale getirebilirsin ya da tamamen kaldırabilirsin. Ara soyutlama katmanları refactoring için vardır, kalıcı mimari kararlar için değil.

Sihirli Sayıyı Adlandırılmış Sabit ile Değiştir

Adlandırılmış sabitler bir stil tercihi değildir. Tek-kaynak-gerçeği mekanizmasıdır. Sabit değiştiğinde her yerde otomatik olarak değişir.

# Kötü
if retry_count > 3:
    raise MaxRetriesExceeded()

# Iyi
MAX_RETRY_COUNT = 3
if retry_count > MAX_RETRY_COUNT:
    raise MaxRetriesExceeded()

Bu teknik özellikle zaman aşımı süreleri, sayfa başına öğe sayısı ve yeniden deneme sayaçları gibi iş kurallarını temsil eden sayılar için kritiktir. 30 görünce bu 30 saniye mi, 30 gün mi, 30 istek mi anlamazsın. SESSION_TIMEOUT_SECONDS = 30 görünce anlarsın. Sonraki engineer bu sayıyı değiştirmek istediğinde, tek bir dosyada tek bir satırı değiştirmesi yeterli olur ve bu değişiklik tüm kullanım noktalarına yansır.

Yan fayda: bu sabitler genellikle konfigürasyon dosyasına veya environment variable'lara taşınabilir. Bir kez adlandırılmış sabite çevrildiğinde, bu geçiş çok daha kolay hale gelir.

Sol tarafta karmaşık kablo sarmalı sunucu rafı, sağ tarafta düzenli kablolu sunucu rafı; kod yeniden düzenleme için görsel metafor

Parametre Nesnesi Ekle

Alti argüman alan bir fonksiyon, yanlış çağrılacak bir fonksiyondur. Bir grup parametre her zaman birlikte seyahat ediyorsa, bunlar bir nesneye aittir.

# Kotu: arguman sirasi onemli ve hataya acik
def create_user(first_name, last_name, email, age, role, department):
    ...

# Iyi: nesne, yapiyi aciklar
from dataclasses import dataclass

@dataclass
class UserConfig:
    first_name: str
    last_name: str
    email: str
    age: int
    role: str
    department: str

def create_user(config: UserConfig):
    ...

Ek fayda: UserConfig artık kendi başına test edilebilir ve doğrulanabilir. Doğrulama mantığı, nesnenin __post_init__ metoduna taşınabilir. Dokümantasyon kod içinde yaşar ve IDE otomatik tamamlama tam anlamıyla çalışır. Altı parametreli bir fonksiyon çağrısında, argümanların sırası hakkında düşünmek zorunda kalmak zihinsel yük yaratır; nesneyle bu yük ortadan kalkar.

Bu teknik ayrıca API'ların evrimini kolaylaştırır. UserConfig'e yeni bir alan eklemek, fonksiyon imzasını değiştirmez ve mevcut çağrı noktaları kırılmaz.

Hazırlayıcı Refactoring Zihniyeti

Feature eklemeden hemen önce yeniden düzenle, bağımsız bir proje olarak değil. Kent Beck'in formülasyonu: "değişimi kolay yap, sonra kolay değişimi yap."

Bu çerçeveleme önemli çünkü yönetim bir refactoring girişimini onaylamak zorunda kalmaz; feature çalışmasına gömülü olduğunda. McKinsey 2024 verilerine göre sistematik modernizasyon yüzde 40-50 daha hızlı tamamlanma sağlıyor. Bu zaman kazancının önemli bir kısmı, önceden yapılan küçük refactoring'lerin feature delivery sürecini düzleştirmesinden kaynaklanıyor.

PR açıklamasına "Önce bu modülü Extract Method ile düzenledim, ardından yeni feature'ı ekledim" yaz. Bu commit geçmişini temiz tutar ve refactoring'i görünür kılar. Ayrıca kod incelemesini iki ayrı mental moda ayırır: önce refactoring commitini incele ve bunun davranış değiştirmediğini doğrula, ardından feature commitini incele. İki iş bir arada sunulduğunda inceleme süresi ikiye katlanır; ayrı sunulduğunda her biri daha hızlı tamamlanır.

Hazırlayıcı refactoring ayrıca "bu feature neden bu kadar uzun sürdü?" sorusuna karşı bir savunma mekanizması sağlar. Commit geçmişi, feature work başlamadan önce ne kadar temizlik yapıldığını açıkça gösterir.

AI Araçları Refactoring'de Ne Yapar (ve Ne Yapmaz)

Cursor, GitHub Copilot ve Cody mekanik refactoring için sürtünmeyi azaltır. Uzun bir fonksiyonu Extract Method ile bölerken, değişkenleri yeniden adlandırırken veya tekrarlayan pattern'ları tespit ederken gerçekten işe yararlar. Cursor özellikle bir fonksiyonu seçip "bu bloğu ayrı bir fonksiyona çıkar, anlamlı bir ad öner" dediğinde tatmin edici sonuçlar üretir.

Ancak stratejik katman hala senin iş yerin. AI araçları:

Hotspot analizi, önceliklendirme kararı ve bağlam değerlendirmesi senin iş yerin. Araçlar mekanik uygulamayı hızlandırır, stratejik bağlamı değerlendirmez. Bu farkı net tutmak, AI araçlarından gerçekçi fayda sağlamanın ön koşuludur.

Pazartesi Sabahı Nerede Başlamalı

Bu hafta repo'nda hotspot analizini çalıştır. En yüksek skoru alan dosyayı seç. En uzun üç fonksiyona Extract Method uygula. Test yaz. Başladığın karmaşıklık skoruna referans veren bir commit mesajı ile commit et.

Bir sprint sonra bug oranını ve PR inceleme süresini aynı modül için ölç. Ölçülebilir değişiklik yoksa, tekniği değil hedef seçimini sorgula: ya bu modül gerçekten hotspot değildi, ya da extract ettiğin fonksiyonlar yeterince küçük soyutlama birimleri değildi.

Refactoring bir proje değil, bir alışkanlık. Her feature PR'ı "bu modülü biraz daha iyi bırakabilir miyim?" sorusuyla başladığında, altı ay sonra codebase'in farklı bir durumda olduğunu görürsün. Sprint sonunda toplantı odasında "refactoring sprint'i" planlamak zorunda kalmazsın.

SSS

Kod yeniden düzenleme ne zaman yapılmalı?

Hem yüksek karmaşıklığa sahip hem de ekibinin sık değiştirdiği modüllerde yapılmalı. Üç yıldır dokunulmamış karmaşık bir modül ikincil önceliktedir. Hotspot analizi bu kararı veri odaklı hale getirir.

Refactoring'i nasıl yöneticime onaylatabilirim?

Hazırlayıcı refactoring mindset'i kullan: feature çalışmasına göm. Bağımsız bir refactoring projesi yerine "X feature'ını eklemeden önce bu modülü temizledim" yaklaşımı onay gerektirmez.

Extract Method ne zaman çok ileri gider?

Fonksiyon tek satıra düştüğünde veya çıkarılan fonksiyon yalnızca bir yerden çağrıldığında ve bağlamı anlamak için iki dosya arasında geçiş yapmak zorunda kaldığında. Aşırı ayrıştırma, over-engineering'in bir biçimidir.

AI araçları refactoring'i otomatikleştirebilir mi?

Mekanik adımları evet: Extract Method uygulamak, değişken adlarını normalize etmek. Nereye uygulanacağını belirleyen stratejik kararı hayır. Bu ayrımı net tut.

Hangi metrikler refactoring başarısını ölçer?

Modül başına bug oranı, PR inceleme süresi ve yeni bir engineer'ın ilk anlamlı commit'ine ulaşma süresi. "Kod daha temiz hissettiriyor" ölçülebilir değil.

Branch-by-Abstraction ne zaman kullanılmalı?

Canlı bir sistemi değiştirirken ve geçişi atomik hale getiremediğinde. Özellikle monolitten mikroservise geçişlerde veya büyük kütüphane güncellemelerinde etkilidir.

Refactoring testleri kırarsa ne yapılmalı?

Test kırmak bir hata sinyali, durdurma sinyali değil. Kırılan test, refactoring öncesi var olan bir davranışı açık eder. Önce testi düzelt veya davranışın değişmesi gerektiğini belgele.

Frequently asked questions

Kod yeniden düzenleme ne zaman yapılmalı?
Hem yüksek karmaşıklığa sahip hem de ekibinin sık değiştirdiği modüllerde yapılmalı. Üç yıldır dokunulmamış karmaşık bir modül ikincil önceliktedir. Hotspot analizi bu kararı veri odaklı hale getirir.
Refactoring'i nasıl yöneticime onaylatabilirim?
Hazırlayıcı refactoring mindset'i kullan: feature çalışmasına göm. Bağımsız bir refactoring projesi yerine 'X feature'ını eklemeden önce bu modülü temizledim' yaklaşımı onay gerektirmez.
Extract Method ne zaman çok ileri gider?
Fonksiyon tek satıra düştüğünde veya çıkarılan fonksiyon yalnızca bir yerden çağrıldığında ve bağlamı anlamak için iki dosya arasında geçiş yapmak zorunda kaldığında. Aşırı ayrıştırma, over-engineering'in bir biçimidir.
AI araçları refactoring'i otomatikleştirebilir mi?
Mekanik adımları evet: Extract Method uygulamak, değişken adlarını normalize etmek. Nereye uygulanacağını belirleyen stratejik kararı hayır. Bu ayrımı net tut.
Hangi metrikler refactoring başarısını ölçer?
Modül başına bug oranı, PR inceleme süresi ve yeni bir engineer'ın ilk anlamlı commit'ine ulaşma süresi. 'Kod daha temiz hissettiriyor' ölçülebilir değil.
Branch-by-Abstraction ne zaman kullanılmalı?
Canlı bir sistemi değiştirirken ve geçişi atomik hale getiremediğinde. Özellikle monolitten mikroservise geçişlerde veya büyük kütüphane güncellemelerinde etkilidir.
Refactoring testleri kırarsa ne yapılmalı?
Test kırmak bir hata sinyali, durdurma sinyali değil. Kırılan test, refactoring öncesi var olan bir davranışı açık eder. Önce testi düzelt veya davranışın değişmesi gerektiğini belgele.