Teknik borcu ölçmek: hissiyattan sayıya
“Teknik borcumuz çok” bir ölçüm değil. Git geçmişinden çıkarılabilen dört vekil metrikle borcu takip edilebilir hale getirmenin ve çeyreklik bir refactor listesi üretmenin yolu.
"Teknik borcumuz çok" cümlesi bir ölçüm değil, bir ruh hali. Sprint planlamasında söylendiğinde kimse itiraz etmiyor, kimse de bir şey yapmıyor. Çünkü cümlenin arkasında karşılaştırılabilir bir sayı yok; ne geçen çeyrekle kıyaslanabiliyor ne de "hangi dosya" sorusuna cevap veriyor.
Teknik borcu tam olarak ölçmek mümkün değil. Ama vekil metriklerle takip edilebilir bir hale getirmek mümkün ve bunun için pahalı bir araca da gerek yok. Git geçmişi ve dosya sisteminde zaten yeterli veri duruyor.
Değişim sıklığı × karmaşıklık
Tek başına karmaşıklık yanıltıcı. Beş yıldır dokunulmamış 900 satırlık bir parser, karmaşık olabilir ama sizi yavaşlatmıyor. Asıl acı veren dosya, hem karmaşık hem de sürekli değişen dosya.
Adam Tornhill'in yaygınlaştırdığı yaklaşım bu iki ekseni çarpıyor. Değişim sıklığı için git yeter:
git log --since="12 months ago" --name-only --pretty=format: \
| grep -E '\.(ts|tsx|py|go)$' \
| sort | uniq -c | sort -rn | head -30
Karmaşıklık için kaba ama işe yarayan bir vekil, girinti derinliği toplamı. Cyclomatic complexity hesaplamak için dil başına ayrı araç kurmaya değmiyorsa:
# dosya başına ortalama girinti derinliği (sekme=4 boşluk sayılır)
for f in $(git ls-files '*.ts' '*.tsx'); do
awk '{ gsub(/\t/," "); match($0, /^ */);
if (length($0) > 0) { s += RLENGTH/4; n++ } }
END { if (n) printf "%.2f %d\n", s/n, n }' "$f" \
| awk -v f="$f" '{ print $1, $2, f }'
done | sort -rn | head -30
İki listeyi birleştirdiğinizde ortaya bir kadran çıkıyor. Sağ üst köşe — çok değişen, çok karmaşık — refactor bütçesinin gideceği yer. Sol üst köşe — çok değişen, basit — muhtemelen sorun değil. Sağ alt — karmaşık ama durgun — rahat bırakın.
Sahiplik dağılımı
İkinci metrik daha az konuşuluyor ama tahmin gücü yüksek: bir dosyaya kaç farklı kişi dokunuyor.
git log --since="12 months ago" --format='%an' --name-only \
| awk '/^[^ ]/ && NF {a=$0; next} NF {print $0"\t"a}' \
| sort -u | cut -f1 | uniq -c | sort -rn | head -20
Tek bir kişinin dokunduğu dosya, o kişi ekipten ayrıldığında borç haline geliyor. On kişinin dokunduğu dosya ise büyük ihtimalle sınırları belirsiz bir "utils" çöplüğü. İkisi de risk, ama farklı tedavi istiyor: biri bilgi paylaşımı, diğeri modül ayrıştırması.
Değişimin maliyeti
Üçüncü metrik doğrudan iş tarafına konuşuyor: benzer büyüklükteki işlerin süresi zamanla artıyor mu?
Bunu ölçmek için hikâye puanına güvenmeyin — enflasyona uğruyor. Bunun yerine kod tarafında ölçülebilir bir vekil kullanın: bir commit'in ortalama dokunduğu dosya sayısı.
git log --since="24 months ago" --pretty=format:'%H %ad' --date=format:'%Y-%m' \
--name-only \
| awk '/^[0-9a-f]{40} / { if (h) print m, c; h=$1; m=$2; c=0; next }
NF { c++ } END { if (h) print m, c }' \
| awk '{ s[$1]+=$2; n[$1]++ } END { for (m in s) printf "%s %.1f\n", m, s[m]/n[m] }' \
| sort
Aylık ortalama sürekli yükseliyorsa, tek bir davranış değişikliği için giderek daha fazla yere dokunmanız gerekiyor demektir. Bu, yayılmış bağımlılığın en dürüst göstergesi. Sayı yatay seyrediyorsa mimari tarafta acil bir sorun yok; borç başka yerde.
Testin gerçekten koruduğu yer
Toplam kapsam yüzdesi kötü bir metrik. Yüzde 80 kapsam, kritik ödeme akışının test edilmediği gerçeğini gizleyebiliyor. Anlamlı olan, birinci maddede bulduğunuz sıcak dosyaların kapsamı.
Kapsam raporunuzu (coverage-final.json, coverage.xml, ne varsa) sıcak dosya listesiyle kesiştirin. Çıkan tablo genelde şunu gösteriyor: en çok değişen dosyalar en az test edilen dosyalar. Bunun sebebi tembellik değil; o dosyalar zaten test yazması zor olduğu için karmaşıklaşmış oluyor.
Sayıları neye çevireceksiniz
Bu dört metrik bir "borç skoru" üretmek için değil. Tek bir sayıya indirgemek, bilgiyi yok ediyor ve kaçınılmaz olarak oyunlaştırılıyor.
İşe yarayan çıktı bir liste: çeyrek başına üç dosya. Yukarıdaki kadranın sağ üst köşesinden, sahiplik riski yüksek olanlardan ve test kapsamı düşük sıcak dosyalardan seçilmiş üç dosya. Bunlar için ayrılan zaman planlamada görünür bir kalem olsun; "boş zamanda hallederiz" maddesi hiçbir zaman hallolmuyor.
Ölçümü otomatikleştirmek de basit. Yukarıdaki komutları bir script'e koyup ayda bir çalıştırın, çıktıyı repoda bir markdown dosyasına yazın ve commit'leyin. Üç ay sonra elinizde bir eğri olur; eğri, tek seferlik bir fotoğraftan çok daha ikna edici.
# aylik-borc.sh — cron ya da CI schedule ile ayda bir
{
echo "# $(date +%Y-%m) teknik borç görünümü"
echo; echo "## En çok değişen 20 dosya"; echo '```'
git log --since="12 months ago" --name-only --pretty=format: \
| grep -E '\.(ts|tsx)$' | sort | uniq -c | sort -rn | head -20
echo '```'
} > "docs/borc/$(date +%Y-%m).md"
Tartışma bundan sonra değişiyor. "Teknik borcumuz çok" yerine "şu üç dosya son bir yılda 140 kez değişti, kapsamları %20'nin altında ve son commit'lerde ortalama dokunulan dosya sayısı 6'dan 11'e çıktı" cümlesi kuruluyor. İkincisine bütçe ayrılıyor, birincisine ayrılmıyor.
- teknik borç
- git
- metrik
- refactor