Testi olmayan büyük bir legacy kod tabanında değişiklik yapmanız gerekiyor. Nasıl ilerlersiniz?
Cevap
Temel ilke: önce güvenlik ağı kur, sonra küçük adımlarla değiştir. Michael Feathers'ın "Working Effectively with Legacy Code" kitabındaki tanımla legacy kod, testi olmayan koddur. Büyük bir yeniden yazım (big rewrite) yerine kademeli iyileştirme hedeflenir.
Adım adım yaklaşım
- Anlayın: kodu okuyun, çalıştırın, log ve metriklere bakın. Kim kullanıyor, hangi akışlar kritik? Git geçmişi ve eski PR'lar kararların nedenini açıklayabilir.
- Değişiklik noktasını belirleyin: dokunacağınız yeri ve etkileyeceği bağımlılıkları çıkarın.
- Karakterizasyon testleri yazın: kodun şu anki davranışını (hatalı olsa bile) kayıt altına alan testler. Amaç doğruluğu değil, değişmezliği doğrulamaktır.
- Seam (dikiş) yaratın: test edilemeyen bağımlılıkları (DB, saat, dış servis) arayüz veya parametre ile dışarıdan verilebilir hâle getirin.
- Küçük, güvenli refactoring adımları: extract method, rename, dependency injection; her adımdan sonra testleri çalıştırın.
- Asıl değişikliği yapın: artık testlerle korunan bölgede, tercihen TDD ile.
- Kademeli yayın: feature flag, canary ve yakın izleme.
Örnek: test edilemeyen fonksiyonu açmak
# Önce: saat ve veritabanı içeride sabit, test edilemez
def send_reminders():
now = datetime.now()
for user in Database().get_users():
if user.due_date < now:
EmailClient().send(user.email, "Hatırlatma")
# Sonra: bağımlılıklar parametre (seam), varsayılanlarla geriye uyumlu
def send_reminders(now=None, db=None, mailer=None):
now = now or datetime.now()
db = db or Database()
mailer = mailer or EmailClient()
for user in db.get_users():
if user.due_date < now:
mailer.send(user.email, "Hatırlatma")Artık sahte db ve mailer ile karakterizasyon testi yazılabilir; mevcut çağıranlar hiçbir şey değiştirmek zorunda kalmaz.
Faydalı teknikler
- Approval / snapshot testing: karmaşık çıktıları (rapor, JSON) bir kez kaydedip sonrakileri karşılaştırmak.
- Sprout method/class: yeni mantığı ayrı ve test edilebilir bir fonksiyonda yazıp eski koddan sadece çağırmak.
- Strangler Fig: sistemin parçalarını yeni bir servise kademeli olarak taşımak; eski kısım zamanla "boğulur".
- Gölge trafik (shadow traffic): yeni implementasyonu gerçek isteklerle paralel çalıştırıp sonuçları karşılaştırmak.
Kaçınılması gerekenler
- Davranış değişikliği ile refactoring'i aynı commit'te yapmak.
- "Bu kod çok kötü, baştan yazalım" kararını; büyük yeniden yazımlar sık sık gecikir ve eski sistemin belgelenmemiş iş kurallarını kaybeder.
- Anlamadığınız "gereksiz" görünen kodu silmek (Chesterton'un çiti: neden orada olduğunu öğrenmeden kaldırmayın).
İlgili Yazılım Geliştirme Süreçleri soruları
- Semantic Versioning (SemVer) nedir? 2.4.1 sürümündeki her sayı neyi ifade eder?
- Teknik borç (technical debt) nedir? Ekipte teknik borcu nasıl yönetir ve iş tarafına nasıl anlatırsınız?
- CI/CD nedir? Continuous delivery ile continuous deployment arasındaki fark nedir? Tipik bir pipeline'da hangi adımlar olur?
- TDD (Test-Driven Development) nedir? Red-Green-Refactor döngüsünü bir örnekle anlatın.
- Test piramidi nedir? Unit, integration ve end-to-end testler arasındaki farklar nelerdir?
- Code review'da nelere dikkat edersiniz? İyi bir pull request nasıl hazırlanır?
Bu konuda başka bir soru biliyor musun?
Mülakatta karşılaştığın soruları ekle, onaylandıktan sonra yayına alınsın.