Kod Refaktoring Teknikleri: Gerçekten İşe Yarayan Yöntemler
Summary
Kod refaktoring, yeni özellik eklemeden veya var olan özellikleri bozmadan mevcut kodu yeniden yapılandırmanın sanatıdır. Extract Method, Composing Methods, Branch-by-Abstraction ve basit koşul bloklarına bölerek karmaşık şartları çözmek gibi teknikleri öğrenin. Her tekniğin ne zaman gerekli olduğunu, ne zaman fazla olduğunu ve eski sistem üzerinde altı ay çalışmaktan çıkardığım dersleri keşfedin.
Kod Refaktoring Teknikleri: Gerçekten İşe Yarayan Yöntemler
Çoğu geliştirici refaktoringün önemli olduğunu bilir. Ama hangisi başında kullanılır, ne kadarına kadar gidilir, ve bir codebase gerçekten bir temizlik turuna ihtiyaç duyduğu sinyalinin ne olduğu hiç net değildir. Eski bir öneri sisteminde altı ay çalışmaktan öğrendiklerim ve bir sonraki sefer farklı ne yapardığım işte burada.

Refaktoringün gerçekten gerekli olduğunu gösteren sinyaller
Her düzensiz görünüşlü fonksiyon dokunmaya değmez. Ciddiye almaya değer üç sinyal var:
Çoklama birçok yerde ortaya çıkıyor. Aynı veri dönüştürmeyi üç ayrı controller'da yazıyorsanız, bu stil meselesi değil. Bu bakım bombası. Bir upstream değişikliği üç yerde güncelleme anlamına gelir ve biri hakkında unutacaksınız.
Tek bir değişiklik ilgisiz özellikleri kırıyor. Ödeme akışında bir hatayı düzeltmek, kullanıcı profillerinin nasıl render edildiğini etkiler. Bağlantı çok sıkı. Codebase ayrı problemleri bir olarak ele alıyor.
Yeni mühendisler 30 dakika içinde kendilerini oryante edemiyorlar. Bu sinyal olarak eksik görülüyor. Bir dosyanın ne yaptığını tamamını okumadan anlatamıyorsanız, muhtemelen çok şey yapıyor. Appier'de boarding walkthroughs zamanlamaya başladık. Açıklaması 15 dakikadan fazla süren dosyalar refaktoring backloguna eklendi.
Kod değişiklik asimetrisi. git log --stat çalıştırın ve hangi dosyalara en sık dokunulduğunu görün. Aynı 3 dosya commit'lerinizin %60'ını oluşturursa, bu yoğunlaşma bir sinyaldir. Yüksek değişim artı yüksek karmaşıklık ilk hedef almaya değer 80/20 noktasıdır.
Refaktoringü atlayın eğer: kod çalışıyor, kimse dokunmuyor ve uzatmak için acil bir neden yok. Boy Scout Kuralı (kodu bulduğunuzdan daha temiz bırakın) iyi bir ilke ama kullanmadığınız bir alanı temizlemeyin.
Extract Method: zamanın %80'ini kullanacağınız bir teknik
Extract Method, uzun bir fonksiyonun anlamlı bir kod bloğunu çıkartıp ona bir ad vermektir. Bu ad dokümantasyondur.
Öncesi:
def process_order(order):
# doğrula
if not order.get('user_id'):
raise ValueError('Kullanıcı ID eksik')
if order.get('amount', 0) <= 0:
raise ValueError('Geçersiz tutar')
# indirim uygula
if order.get('coupon') == 'LAUNCH20':
order['amount'] = order['amount'] * 0.8
# kaydet
db.save(order)Sonrası:
def process_order(order):
validate_order(order)
apply_discount(order)
db.save(order)
def validate_order(order):
if not order.get('user_id'):
raise ValueError('Kullanıcı ID eksik')
if order.get('amount', 0) <= 0:
raise ValueError('Geçersiz tutar')
def apply_discount(order):
if order.get('coupon') == 'LAUNCH20':
order['amount'] = order['amount'] * 0.8"Sonra" versiyonu daha uzun. Sorun değil. Her fonksiyonun şimdi var olmasının bir nedeni var ve indirim mantığı değiştiğinde (ve değişecektir), tam nereye gideceğinizi bilirsiniz.
Çaba değer eğer: orijinal fonksiyon kabaca 20-25 satırdan fazla veya bir bloğu açıklamak için "# doğrula" gibi bir yorum yazmak zorunda kalırsanız. Bu yorum çıkartılmayı bekleyen bir fonksiyon adıdır. Bir kod bloğunun üzerine # indirim uygula yazmak, metodu zaten adlandırmışsınız demektir, henüz çıkartmamışsınız.
Pratik kural: sonraki beş satırın ne yaptığını açıklayan bir satır içi yorum yazma arzusu hissederseniz, bu çıkartılmaya aday.
Composing Methods: Extract Method yeterli olmadığında
Composing daha ileri gider. Bir metodu çıkartmak yerine, bütün bir sınıfı veya modülü daha küçük, odaklanmış bileşenlere ayrıştırırsınız. Bu, bir dosya 600 satıra büyümüş ve beş ilgisiz amaç hizmet ettiğinde ihtiyacınız olan şeydir.
Desen: ayrı sorumlulukları belirleyin, her birine kendi sınıfı veya modülü verin, daha yüksek düzeyde birbirine bağlayın. Martin Fowler bunu şişmanlaşmış bir sınıfı işbirliği yapan sınıflar kümesine dönüştürmek olarak tanımlar.
Pratikte, zor kısım mekanik değil. Bir sorumluluk nerede bitip diğeri başladığını bulmak. Yararlı bir buluşsal yöntem: bir fonksiyon veya sınıfın ne yaptığını "ve" kelimesi kullanmadan açıklayabiliyorsanız, muhtemelen bir sorumluluğu vardır. "Ve" gerekirse, böleyin.
Dikişleri bulmak için başka bir yol: dosyanın başındaki importlara bakın. Tek bir modül altı ilgisiz alanı importse (veritabanı katmanı, e-posta servisi, logging, ödeme istemcisi, raporlama), çok şey yapıyor. Her ithalatç kümesi kendi modülü için adaydır.
Composing methods testlenebilirliği de geliştirir. 400 satırlık bir sınıf unit testi yapmak zordur. Net arayüzleri olan dört 100-satır sınıf mock'lanması ve izole edilmesi çok daha kolay.

Branch-by-Abstraction: üretim ortamında kalarak refaktoring yapma
Bu, daha mimariye yönelik ama büyük ölçekli refaktoringı üretim ortamlarında hayatta tutmayı mümkün kılan tekniktir.
Fikir: bir uygulamadan diğerine büyük bir geçiş yapmak yerine, değiştirmek istediğiniz kod etrafına bir abstraksiyon katmanı (genellikle bir interface veya protokol) tanıtırsınız. Eski uygulama ve yeni uygulama her ikisi de arayüzü karşılar. Yeni uygulamayı bir özellik bayrağının arkasına koyabilir, her ikisini paralel çalıştırabilir ve eski olanı yeni kararlı olduktan sonra kullanımdan kaldırabilirsiniz.
Side projeler için neden önemlidir: ödeme entegrasyonunu yeniden derlemek veya ORM geçişi yapıyorsanız, branch-by-abstraction, yeni olanı hazırlanırken mevcut versiyonu üretimde çalıştırmak anlamına gelir. Üç hafta main'den sapan "refaktoring şubesi" yok.
Maliyet: temiz bir arayüz tanımlamak için daha fazla ön çalışma. Değiştirilecek şey riskli olduğunda (auth, faturalandırma, veri erişimi) ve tam bir kesinti penceresine çekemediğinizde değerlidir.
Beton bir örnek: PostgreSQL tablosuna doğrudan yazan eski bir event-tracking modülümüz vardı. Kafka tabanlı bir pipeline'a geçiş 40+ call sitesine dokunmak anlamına geliyordu. Branch-by-abstraction ile EventTracker arayüzü tanıttık, uygulamayı bir bayrak arkasında değiştirdik, iki hafta boyunca her iki hedefi paralel çalıştırdık, sonra eskisini sildik. Sıfır kesinti, sıfır acil deploy.
Koşulları Basitleştirme: çoğu insanın atladığı temizlik
Koşul mantığı karmaşıklığın gizlendiği yerdir. Beş iç içe if ifadesine sahip bir fonksiyon, beş ayrı koşuldan daha zor okunur, test edilir ve uzatılır ve guard cümleleri veya çok biçimlilik ile ifade edilir.
İç içe koşulları koruma cümleleri ile değiştir:
Öncesi:
function getDiscount(user) {
if (user) {
if (user.isActive) {
if (user.plan === 'pro') {
return 0.20;
} else {
return 0.05;
}
} else {
return 0;
}
} else {
return 0;
}
}Sonrası:
function getDiscount(user) {
if (!user) return 0;
if (!user.isActive) return 0;
if (user.plan === 'pro') return 0.20;
return 0.05;
}Aynı mantık. Çok daha kolay taramak. Erken dönüş satırlarını indentasyon piramidini ortadan kaldırır ve her koşulu izole olarak okumanızı sağlar.
Koşulları çok biçimliliğe değiştir aynı if type === X kontrolü birden fazla fonksiyonda tekrarlandığında. Bir temel sınıf veya protokol tanımlayın, her tür için bir uygulama ve gönderişin call sitede olmasına izin verin. Atla eğer: sadece iki türün varsa ve üçüncü ekleme olası değilse. Abstraksiyon genel gideri, hiç büyümeyecek ikili bir seçim için değerli değildir.
Yinelenen koşulları birleştir. Aynı koşul üç ayrı yerde biraz farklı şeyler yapmadan önce kontrol ediliyorsa, koşulu adlandırılmış bir predicate fonksiyonuna çıkartın. if (isEligibleForDiscount(user)) her sefer üç kısım kontrolünü tekrarlamaktan daha iyi okunur.

Nesneler arasında özellikleri taşıma: az kullanılan teknik
Bazen bir metod yanlış sınıfta. Kod yanlış değil ama başka bir sınıftan verilerle çalışıyor kendinden daha fazla. Bu onu oraya taşımak işareti.
Move Method deseni basittir: metodu belirleyin, gerçekten hangi sınıfa bağlı olduğunu kontrol edin, oraya taşıyın, tüm çağrıcıları güncelleyin. IDE'nizden lint kırılan referansları yakalar.
Bu en çok side projeler'de ortaya çıkar: her yere açıkça ait olmadığı fikri toplayan utility sınıfları. helpers.js veya utils.py 400 satıra büyüyen dosyalar. Belli bir noktada, bu fonksiyonlar üzerinde çalıştıkları domain nesnelerine aittir. Bir User nesnesi alıp ona beş şey yapan bir fonksiyon utils'ta değil User'da aittir.
Atla eğer erken aşamasındaysan: proje iki haftalık ve hala şeklini buluyorsa, yapıyı aşırı mühendislik yapmayın. Gerçek kullanımdan tasarım ortaya çıkmasına izin verin temiz domain modeline yatırım yapmadan. İşleri çok erken taşımak, gereksinimler değiştiğinde tekrar taşımak anlamına gelir.
Refaktoring yapmadan önce yapılması gerekenler: test temeli
Testler olmadan refaktoring yapmak sadece kumarbazlık. Temizlik yapıyorsunuz sandığınız, kırıyor olabilirsiniz.
Asgari güvenlik ağı: karakterizasyon testleri. Değiştirmek üzere olduğunuz kodun mevcut davranışını yakalayan testler yazın. Ne yapması gerektiği değil, ne gerçekten yaptığı. Bu testler tasarımı doğrulamak değil, regresyonları yakalamak için var.
Temel aldıktan sonra, küçük commit'lerde refaktoring. Commit başına bir desen. Bir şey kırılırsa, tam hangi değişiklik yaptığını bilirsiniz. Bunu alternatifle kıyaslayın: üç günlük refaktoring oturumu bir dev diff'inde commit. CI saat 2'de başarısız olduğunda, 40 değişiklikten hangisinin kırdığını bilmiyorsunuz.
Her refaktoring öncesinde pratik kontrol listesi:
Etkilenen modül için karakterizasyon testleri yazın
Başlamadan önce CI pipeline'ın yeşil olduğundan emin olun
80/20 noktalarını belirleyin: karmaşık ve sık değiştirilen kod (git log churn verisi gösterebilir)
Bir teknik seçin, uygulayın, yeşile dönün, commit'leyin, sonra sonrakine gidin
Refaktoring commit'lerini özellik commit'lerinden ayırın, diff okunabilir kalır
Faydalı kısıtlama: commit mesajında refaktoringü tek cümleyle açıklayamıyorsanız, muhtemelen çok fazlasını yapıyorsunuz.
Verileri organize etme: her şeyi iyileştiren desen
Birçok karışık koşul mantığı veri modeli yanlış olduğu için var. Ham primitivler etrafında geçiş yaparken, her yerde tür-kontrol koşulları sonuçlanır.
Un primitive'i bir nesneyle değiştirme (string email EmailAddress olur, int cents Money olur) doğrulama mantığı için ev verir. if not re.match(EMAIL_REGEX, email) 12 yerde kontrol etmek yerine, EmailAddress yapıcısı bunu bir kez yapar.
Bu Fowler'ın Refactoring kitabındaki "Type Code'u Sınıfla Değiştir" desenidir. Göz kamaştırıcı değil ama koşul yayılımını kitaptaki hemen hemen her şeyden daha iyi azaltır.
Aynı ilke magic sayılar'a uygulanır: if status == 3 okunamaz. if status == OrderStatus.SHIPPED öyledir. Magic sayıları adlandırılmış sabitlerle değiştirme beş dakika alır ve kodu okuyan her sefer öder.
Ne zaman refaktoring yapmayacağınız: insanların atladığı vakalar
Çoğu rehber burada durur. Pratikte ne takıldığı: her codebase refaktoring yapmaya değmez ve temizliği her zaman iyi olarak görmek sadece farklı türden teknik borçtur.
Zor bir deadline öncesinde refaktoring yapmayın. Refaktoring değişiklik getir. Değişiklik risk getir. Client demosu 48 saate kaldıysa, bu zaman değil.
Silmek üzere olduğunuz kodu refaktoring yapmayın. Bir özellik sonraki sprint'te kesileceğine göre, uygulanmasını temizlemek çöp çalışma. Temizliğe yatırımdan önce silmeyi onaylayın.
Sadece stil için refaktoring yapmayın. Kod çalışıyorsa, test edildiyse ve kimse dokunmuyorsa, farklı yazmış olacağınız gerçeği değiştirmek gerekçesi değil. Regresyon riski gerçek; fayda estetik.
Açık hedef olmadan refaktoring yapmayın. "Bunu temizle" bir görev değil. "OrderProcessor'dan doğrulama mantığını özel bir Validator sınıfına çıkart" bir görevdir. Muğlak kapsam scope creep'e yol açar, bu üç haftalık bir şuba ve conflict'li merge'ü yol açar.
Mükemmel kural seti değil. Uygulanabilir olanıdır ve production codebase'te, uygulanabilir mükemmeli yener.
Altı aylık incelemeli refaktoring gerçekten neye benziyor
Her sprint'te refaktoring yapan ve gönderdiği özelliklerle hizalı bir ekip, içinde çalışması daha ucuz olan bir codebase ile sona erer. İlk yazılması daha hızlı değil ama uzatması, debug'lanması ve yeni katılımcıların katılması daha hızlı.
Solo builders için: ödül kişisel. Altı aydan sonra yeni mühendis sizsiniz. Bugün temizlediğiniz kod Ocak'ta yeniden okumazsanız anlarsınız.
Öneri sistemi projesinden altı aylık resim: 1200 satır ve test olmayan bir servis dosyası ile başladık. Her özellik sprint'inde metotları çıkarttık, her dokunulan bölüme karakterizasyon testleri ekledik ve domain'i daha iyi anlamaya başladığımız sürece şeyleri doğru sınıflara taşıdık. Altı aydan sonra, aynı mantık dokuz dosyaya ortalaması 130 satır dağıtıldı, hepsi test edildi, hepsi ne yaptığı için adlandırıldı. Deployment frekansı haftadan günlüğe çıktı. Bir hatadan kurtulma süresi kabaca %40 düştü.
Bu rakamlar bir çalışmadan değil; bir ekip, bir sistemden. Ama yön tutuyor. Daha küçük, temiz birimler değiştirmesi daha hızlı. Tüm bahis bu.