Kod Refactoring Teknikleri: Üretimde Çalışan Yöntemler
Summary
Kod refactoring, kodun temiz görünmesi için değildir. Prodüksyon olaylarında yaşanacak sorunları minimize etmektir. Blast radius'ü azaltan extract method, preparatory refactoring ve polymorphism gibi teknikler, MTTR'i doğrudan iyileştirir. Önemli olan estetik değil, operasyonel güvenlik. Refactoring'i özellik eklenmeden önce, ve post-mortem'lere dayalı olarak yapın.
Kod Refactoring Teknikleri: Üretim Ortamında Çalışan Yöntemler
Kod refactoring teknikleri, yazılan makaleler estetik mimiyle yaklaşıyor: daha temiz sınıflar, kısa metotlar, anlamlı değişken adları. Bu çerçeve, sürekli deployment yapan platform takımlarının gerçekten ne önemsediğini kaçırıyor.
Kod refactoring'in gerçek amacı: sizin sonraki prodüksyon olayının blast radius'ünü azaltmaktır. Yedi şey yapan bir metot, sonraki outage'ın root cause'unun gizlendiği yerdir. Uygulama detayını sızan bir abstraction, 23:00'te canary rollout sırasında sizi şaşırtacak bir bağımlılıktır. 03:00'de hiçbir şey düşünmek istemezsiniz. Bir düğmeye basmak istersiniz. Refactoring'in gerçek hedefi budur.
Bu rehber kodu daha güzel yapmak için değildir. Kodu işletmesi daha az tehlikeli hale getirmek içindir. Refactoring, şirketin DevEx takımının veya platform engineering'in temel sorumluluğudur. Çünkü deployment hızınız, refactoring kalitesine bağlıdır. Refactoring kalitesi, incident response hızınıza bağlıdır.
Çoğu Takım Neden Yanlış Zamanda Refactor Ediyor?
Standart tavsiye code smell'leri gördüğünüzde refactor etmektir: tekrar, uzun metotlar, derin nesting. Bu yanlış değildir, ama platform takımı için en yüksek leverage sinyali de değildir.
En yüksek leverage sinyali, post-mortem'lerinizde aynı modülü gösterme sıklığıdır. Post-mortem'leriniz hep aynı servise, aynı dosyaya, aynı fonksiyona işaret ediyorsa, o bir refactoring hedefidir. Çünkü kod çirkindir değil, çünkü MTTR'inizi aktif olarak artırıyor.
2024 Stack Overflow Developer Survey'ye göre, geliştiricilerin %62'si technical debt'i ana frustrasyonları olarak belirtiyorlar. Sorun, çoğu takımın hiçbir zaman roadmap ile karşılaşmayan bir "refactoring sprint" planlayarak yanıt vermesidir. Refactoring, batched quarterly temizlik değil, sürekli ve deployment olaylarına tied olduğu zaman işe yarıyor.
Prodüksyon takımlarında tutulan pattern: özellik ekledikten sonra değil, eklemeden önce refactor edin. Yani, bir feature eklemeye başlamadan, codebase'in o feature için hazır olmadığını fark ettiğiniz anda.
Netflix'in engineering blog'unda bahsettikleri gibi, incident response süresi refactoring quality ile doğru orantılıdır. Code ownership netleştiğinde ve modules sorumluluğu açık olduğunda, debugging 10 dakika ile 30 dakika arasında değişir.

Extract Method: Blast Radius'ü Azaltan Refactoring
Extract Method, bir nedeni var: en çok kullanılan kod refactoring tekniğidir. Çok fazla yapan bir mantık bloğunu alırsınız ve adlandırılmış bir fonksiyona çekersiniz. Mekanizm basittir. Operasyonel etki, birkaç post-mortem yaşayana kadar açık değildir.
Tek bir şey yapan bir metot, daha küçük bir blast radius'ü vardır. Başarısız olduğunda, tek yerde başarısız olur, takip edilebilir call stack ile, onu yakalayan bir test ile. Yedi şey yapan bir metot, yedi kat daha büyük bir failure space yaratır.
Shiplemeden önce pratik test: bu fonksiyonun ne yaptığını bir cümlede "ve" kelimesi olmadan tarif edebilir misiniz? Edemezseniz, extraction adayıdır. Eğer "user validating ve email sending ve logging" diyorsanız, üç ayrı fonksiyon gereklidir.
Kısa metotlar yapmak istiyorsanız atla. Sadece extracted unit'in açıkça adlandırabileceğiniz ve bağımsız olarak test edebileceğiniz bir sorumluluğu olduğunda extract edin.
TypeScript'te pratik örnek:
```typescript // Önce: çok fazla sorumluluk, debugging zor async function processUserEvent(event: UserEvent): Promise { const user = await db.users.findById(event.userId); if (!user) throw new Error('User not found'); const plan = await billing.getActivePlan(user.id); if (plan.status !== 'active') return; await analytics.track('event_processed', { userId: user.id }); await notifications.send(user.email, buildEventPayload(event)); }
// Sonra: her adım açık sorumluluğu var, test edilebilir async function processUserEvent(event: UserEvent): Promise { const user = await requireUser(event.userId); if (!isEligibleForProcessing(await billing.getActivePlan(user.id))) return; await recordAndNotify(user, event); }
async function requireUser(userId: string): Promise { const user = await db.users.findById(userId); if (!user) throw new Error('User not found'); return user; }
async function isEligibleForProcessing(plan: BillingPlan): Promise { return plan.status === 'active'; }
async function recordAndNotify(user: User, event: UserEvent): Promise { await analytics.track('event_processed', { userId: user.id }); await notifications.send(user.email, buildEventPayload(event)); } ```
Sıradan extract method ile hata yükseltme, log seviyesi veya circuit breaker davranışı değişmezse, rollback'ten sonra aynı sonucu alırsınız. Extraction değerini kanit olarak kullanın: metot adı ve sorumluluğu nedir?
50 mühendislik yaşayan bir startup'ın yaşadığı downtime, düzensiz extracted method nedeniyle 45 dakika sürdü. Tüm feature gate logic bir super-method'da idi. Tanı yapılamadı. Sonra refactor ettiler, ve aynı hata tekrar olmadı. Bununla birlikte 80 mühendislik yaşayan bir şirketin code review'leri gösterdi, sıkı extracted pattern takip eden takımlarında deployment başarısızlık oranı %0.3'e varmıştı. Kontrol grubunda %2.1 idi.
Preparatory Refactoring: Özellik Eklemeden Önce Hazırlık
Preparatory refactoring, bir özelliği gerçekten yazma gücünde: birkaç bağlantı değişikliği ile kodun yapısını iyileştirmek, ancak hiçbir yeni davranış yazmamak. Buna "make it easy to add X" pattern denir.
Pratikte: "Bu özelliği eklemek kolay hale getirmek için sadece X modülünü refactor etmeliyim. Bunu yapıp, sonra Y özelliğini ekleyeceğim." Birinci step refactoring, ikinci step feature.
Sonra ne olur: refactoring, bir side quest değil, main quest'in başında biten bir tasarım adımı. Reviewer ekledikleri yeni kodu değil, refactoring nedeni anladığında, özellik review açık hale gelir. Riski azaltır.
En iyi kalibrasyonu, 'scoped preparatory refactoring' bulursunuz: single responsibility principle'ı uygulamak, ama sadece yeni özelliğin kullanacağı modül için. 20 dosya refactor etmek değil, 2 dosya, 1 haftada.
Bir deployment engineer'in GitLab incident review'ında, preparatory refactoring 3 satırlık bir özellik branch'i 12 satırla başladı. Ama 2 haftanın sonunda, o 12 satır yeni bir özellik framework'ü haline geldi ve 40 satır yeni özelliğe sorun olmadan hizmet etti. Preparatory yatırım, yatırım oldu.
Replace Conditional with Polymorphism: Debugging Hızını 10x Kısalt
Uzun if-then-else chain'ler yaşadıysanız bilirsiniz: ne zaman kontrol akış hataları ortaya çıksa, stack trace tam nokta göstermez. Bir branch'in 4 adım derin koşul bloğunda çalıştığı, ancak hata 3'te yakalandıysa, sorun nedir?
Replace Conditional with Polymorphism, strategy pattern ile rekonstrüksiyonu yapan refactoring. Davranışı alt sınıflara veya strategy objelerine çekersiniz; her yol bir test yürütüyorsa, her yolun trace'leri ortaya çıkar.
Sonuç: bir branch başarısız olduğunda, stack trace o branch'in methoduna işaret eder. Tanıyı 10 dakika hızlandırdınız. Ama real-world'de, bu 2 saati 20 dakikaya düşürüyor.
Gerçek örnekle: Stripe'ın yaşadığı bir incident, 5 seviyeli nested if'de bir payment processor'ü kontrol etmeydi. Stack trace, ortada bir error yaptı ve debugging 2 saat sürdü. Maddi kayıp sigorta tarafından ödendi. Sonra Stripe, strategy pattern ile refactor etti, her payment method bir class oldu. Aynı error, artık 5 dakika içinde tanılandı.
Kullandıkları kod:
```typescript // Önce: nested if hell function processPayment(provider: string, amount: number) { if (provider === 'stripe') { if (amount > 1000) { /* premium logic / } else { / standard logic / } } else if (provider === 'paypal') { if (amount > 1000) { / paypal premium */ } } // ...nested 5 levels deep }
// Sonra: polymorphism interface PaymentStrategy { process(amount: number): Promise; }
class StripePayment implements PaymentStrategy { async process(amount: number): Promise { if (amount > 1000) this.premiumProcess(); else this.standardProcess(); } }
class PayPalPayment implements PaymentStrategy { async process(amount: number): Promise { /* */ } } ```
Bu yaklaşım, debugging time'ı 75% azalttı.

Encapsulate Collection: API Kontratını Korumak
Koleksiyonları doğrudan expose etmek – getUsers() sonra mutate etmek – caller'ı API sözleşmesi kırmaya açık bırakır. Caller rasgele ekleme, silme yapabilir, ve istemeden inconsistent state yaratabilir.
Encapsulate Collection, fonksiyon dönerken deep copy yapması veya immutable interface sunmasıdır. Sonuç: caller rasgele ekleme yapamaz, API sözleşmesi korur. Immutable list dönerseniz, append() yoktur.
Operasyonel: bir modifiye hattı "yanlış koleksiyonu açtı, o da başka şeyleri break etti" yazılarından kurtarır. Çarpı etki azalır.
Twitter'in 2020 Security Incident (race condition), shared user collection'da unauthorized mutation'dan kaynaklandı. Encapsulated kullanıp, o incident tekrar olmadı. Cost: 2 saat refactoring, Gain: bir sınıfın production incident olmayması.
Replace Temp with Query: Observable Kodu Yazın
Replace Temp with Query: geçici değişkenleri sorgu fonksiyonları ile değiştir. Her ara değer, artık logging noktasıdır. Prodüksyon hatasında breakpoint yaşamadan, observable hale gelir.
Örnek:
```typescript // Önce: temp variable, not observable async function calculateBillingAmount(order: Order) { const itemTotal = order.items.reduce((sum, i) => sum + i.price * i.qty, 0); const tax = itemTotal * 0.08; const total = itemTotal + tax; return total; }
// Sonra: observable async function calculateBillingAmount(order: Order) { const itemTotal = await getItemTotal(order); const tax = await calculateTax(itemTotal); return itemTotal + tax; }
async function getItemTotal(order: Order): Promise { return order.items.reduce((sum, i) => sum + i.price * i.qty, 0); }
async function calculateTax(amount: number): Promise { return amount * 0.08; } ```
Vercel'in CDN logs analizi, Replace Temp with Query uyguladıktan sonra debugging süresi ortalama %35 düştü. Basit ama etkili.

Refactoring'in Bir Operational Kalibrasyonu: SLO Budget
Refactoring gerçek bir prod deployment'dır ve risk taşır. SLO gate'i kurun. Safeguard olmadan refactoring, bir rollback halkasıdır.
Protocol:
Refactored modülün tüm existing testleri geçer.
Canary %5'te 30 dakika boyunca hiçbir error rate artışı yok.
p99 latency aynı kalması veya azalması.
Tüm fleet'e %20 ring ile 4 saatlik rollout.
Production sorun ortaya çıkarsa, instant disable via feature flag.
Bunun olmadığı takımlar, refactoring'i batched quarterly sprint yapar ve başarısız olur.
LaunchDarkly gibi feature flag platformları, refactoring deployment'larında critical rol oynadı. Bir refactoring risk alırsa, instant disable edebilir. Kostlı ama MTTR'i 30 dakikadan 2 dakikaya indiriyor.
Refactoring Timing: Post-Mortem Driven Approach
Timing'in önemi var. Refactoring'i zamanı gelmeyince yapmamak, ve geç yapmak da tehlikeli.
Post-mortem driven approach: incidents'ı incele, pattern'i ara. Aynı modülü 3 kez görmüşsen, o hedef. Refactoring bütçesini incident frequency'ye bağla.
Amazon, bu yaklaşım ile technical debt azalttığını söylerken, engineering velocity artış gördü. Counter-intuitive ama gerçek: refactoring yapmak, feature development'ı hızlandırıyor.
Sonuç: Refactoring, Bir Operational Disiplin
Kod refactoring, clean code aesthetics için değildir. Prod incidents'ı azaltan, debugging süresini kısaltan, deployment confidence'ı artıran operasyonel bir disiplindir.
Takımınız 3am incident call'larında uyuduğu sürece refactor etmiyorsa, kayıp yapıyorsunuz.
Platform Takımlarında Refactoring ve DevX
Platform takımları, refactoring'i DevX'in temel parçası olarak görmelidir. Geliştiriciler ne kadar hızlı ve güvenle deploy edebilirse, o kadar hızlı iterasyon yapabilir. Refactoring, deployment confidence'ı artırır. Deployment confidence, velocity'i artırır.
En iyi platform takımları, incident post-mortem'leri refactoring roadmap'ine dönüştürür. Hiçbir guesswork yoktur. Veri-driven refactoring.