Blog
Mert Özen'in yazılım geliştirme, backend, frontend ve DevOps üzerine blog yazıları.
Refactoring Nedir, Ne Değildir? Davranışı Korumak, İki Şapka ve Rewrite Kararı
Refactoring, gözlemlenebilir davranışı değiştirmeden iç yapıyı küçük ve güvenli adımlarla değiştirmektir; davranışı ölçen bir test yoksa yapılan şey refactoring değil, umutla yapılan bir değişikliktir. Yazıda "no behavior change" başlıklı bir PR'ın faturayı bir kuruş değiştirmesi, iki şapka ve ayrı commit disiplini, ne zaman refactor edileceği ve rewrite kararının ölçütleri gerçek çıktılarla anlatılıyor.
Refactoring'den Önce Güvenlik Ağı: Characterization Test, Approval Testing ve Kör Noktaları Bulmak
Testi olmayan legacy kodu refactor etmeden önce bugünkü davranışı characterization test ve approval testing ile kayda almak gerekir; ama ağın gerçekten koruduğunu coverage ve bilinçli mutasyonlarla sınamadan ona güvenilmez. Yazıda testsiz bir InvoiceRenderer Verify ile onaylanıyor, tarih ve culture kaynaklı kırılmalar scrubber'larla gideriliyor, 48 kombinasyonluk snapshot ve yüzde 100 coverage'a rağmen iki mutasyonun nasıl yaşadığı gerçek çıktılarla gösteriliyor.
Küçük ve Güvenli Adımlar: IDE Refactoring'leri, Parallel Change ve Toplu Otomatik Düzeltmeler
Büyük bir method'u açmanın güvenli yolu, her biri derlenip test edilen ve ayrı commit'e giren küçük adımlardır; imza değişikliği ise parallel change ile genişlet, taşı, daralt diye yürütülür. Yazıda PricingEngine.Calculate 14 commit'te hiç kırmızı commit olmadan açılıyor, dotnet format ile toplu düzeltme ayrı commit'e alınıyor, decimal scale farkını yakalayan snapshot ve rename'in JSON contract'ını ve convention tabanlı DI kaydını sessizce bozduğu durumlar gerçek çıktılarla gösteriliyor.
Koşul Yığınından Okunur Tasarıma: Switch, Flag Parametreleri ve Kural Tabloları
İç içe koşulları dönüştürmenin tek doğru hedefi yoktur: yeni tipler sık geliyorsa polymorphism, yeni işlemler sık geliyorsa switch expression, kurallar sık değişiyor ve aynı biçimdeyse kural tablosu daha ucuzdur. Yazıda PricingEngine'in indirim koşulları üç hedefe de aynı 144 kombinasyonluk snapshot'tan geçerek dönüştürülüyor; enum, guard clause, bayrak parametresini bölmek ve Percentage tipi ile CS8509, CS0535 ve eksik kural testinin gerçek çıktıları gösteriliyor.
Legacy Kodda Dependency Kırmak: Sprout, Wrap, Extract Interface ve Static Bağımlılıklar
Testi olmayan legacy bir class'a yeni davranış eklemenin güvenli yolu, önce değişiklik noktası ile test noktasını bulup yalnız aradaki engeller için seam açmaktır: yeni kural sprout ile ayrı yerde test edilir, constructor parametrelenir, static çağrı ince bir wrapper'a, zaman TimeProvider'a alınır. Yazıda SQL Server'a takılan bir OrderImportJob'da bu adımlar uygulanıyor; testler job'un bilinmeyen bir varsayımını ve sprout'u bağlayan satırdaki hatayı gerçek çıktılarla yakalıyor.
Büyük Refactoring'i Planlamak: Mikado Method, Branch by Abstraction ve Parallel Run
Tek PR'a sığmayan bir refactoring, Mikado Method ile deneyip geri alarak çıkarılan bir ön koşul graph'ıyla planlanır ve branch by abstraction ile ana branch'te küçük, her biri yeşil commit'lerle yürütülür. Yazıda static LegacyShippingClient typed HttpClient tabanlı bir client'la 13 commit'te değiştiriliyor; parallel run testlerde olmayan iki farkı yakalıyor, static state'in paralel testleri nasıl bozduğu da gerçek çıktılarla gösteriliyor.
Veri ve Contract Değişirken Refactoring: API Alanını, EF Core Property'sini ve Angular Modelini Birlikte Yeniden Adlandırmak
Bir property'nin adı değişirken dış contract'lar (JSON alanı, veritabanı kolonu, CSV başlığı, cache'teki şekil) koddan ayrılmalı ve expand-contract ile adım adım taşınmalıdır. Yazıda Book.Price'ın ListPrice'a dönüşmesi ve DiscountRate'in ayrı tabloya taşınması .NET, EF Core, SQL Server ve Angular'da yürütülüyor; kırılan view, sıfır okunan cache, tolerant reader ve dual write'ı atlayan script gerçek çıktılarla gösteriliyor.
Angular Kodunu Refactor Etmek: NgModule'den Standalone'a, Constructor Injection'dan inject()'e, RxJS'ten Signal'lere
Eski bir Angular component'ini modernleştirmenin güvenli yolu, davranışı testle sabitlemek, resmi migration schematic'lerini --path ile feature feature, her birini ayrı commit'te çalıştırmak, schematic'lerin yapamadığı veri akışını ise testlerin gözetiminde elle dönüştürmektir. Yazıda bir CheckoutComponent sekiz schematic ve elle yapılan switchMap ve signal geçişiyle taşınıyor: prune-ng-modules'ın build'i kırması, hatadan sonra biten akış ve iki katına çıkan istek gerçek çıktılarla görülüyor.
Teknik Borcu Önceliklendirmek ve Refactoring'i Savunmak: Hotspot Analizi, Ölçüm ve Product Owner ile Konuşmak
Refactoring yatırımı en çirkin koda değil, en sık değişen karmaşık koda, yani hotspot'lara yapılır ve Product Owner'a değişim maliyeti, lead time ve hata oranı üzerinden anlatılır. Yazıda git log ve Roslyn ile Cognitive Complexity hesaplayan küçük bir C# script'i Bookstore repo'sunda çalıştırılıyor; PricingEngine'in birinci hotspot çıkması, InvoiceRenderer ile yüzde 59'luk change coupling ve sekiz fatura hatası gerçek çıktılarla gösteriliyor.