Teknik Borç Nedir: Nasıl Ölçülür ve Azaltılır

Kod tabanınıza her baktığınızda içinizi sıkan o his var ya, işte o his teknik borcun ta kendisi. “Şimdilik böyle kalsın, sonra düzeltiriz” dediğimiz her satır, her kopyala-yapıştır çözümü, her belgelenmemiş fonksiyon… Bunların hepsi birikip zamanla bizi boğmaya başlıyor. Sistem yöneticisi ya da DevOps mühendisi olarak bu borcu sadece geliştiricilerin meselesi olarak görmek büyük hata. Infrastructure as Code, CI/CD pipeline’ları, otomasyon scriptleri… hepsinde teknik borç birikiyor ve günün birinde o prod ortamında patlıyor.

Teknik Borç Nedir, Gerçekten?

Ward Cunningham’ın 1992’de ortaya attığı bu kavramı çoğu kaynak “kısa vadeli çözümlerin uzun vadeli maliyeti” şeklinde özetler. Ama bu tanım biraz steril kalıyor. Gerçek dünyada teknik borç şu sorulara verilen “şimdilik” cevaplarının toplamıdır:

  • Bu Ansible playbook’u düzgün çalışıyor mu? Evet. Okunabilir mi? Hayır. Bakımı yapılabiliyor mu? Sormayın.
  • Bu SonarQube uyarısı neden var? Bilmiyorum, 6 aydır var, kimse dokunmuyor.
  • Bu CI pipeline neden 45 dakika sürüyor? Optimize edemedik, şimdilik böyle.

Teknik borç dört ana kategoriye giriyor:

  • Kasıtlı ve ihtiyatlı borç: Piyasaya çıkma baskısı altında bilinçli alınan kararlar. “Bunu biliyoruz, sonra düzelteceğiz” türü.
  • Kasıtlı ve ihtiyatsız borç: “Design pattern’lere gerek yok” diyen deneyimsiz kararlar.
  • Kasıtsız ve ihtiyatlı borç: Sonradan öğrenilen daha iyi yöntemlerle ortaya çıkan eski kod.
  • Kasıtsız ve ihtiyatsız borç: “Ne yapmaya çalışıyordum ki bunu?” dedirten kodlar.

Bize en çok pahalıya patlayan üçüncü ve dördüncü kategoriler.

Teknik Borcu Nasıl Ölçersiniz?

Soyut bir kavramı somutlaştırmanın yolu metriklerden geçiyor. SonarQube bu konuda en güçlü araçlardan biri. Bir projeyi SonarQube’e bağlamak için önce scanner’ı kuralım:

# SonarQube Scanner kurulumu (Linux)
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip
unzip sonar-scanner-cli-5.0.1.3006-linux.zip -d /opt/
ln -s /opt/sonar-scanner-5.0.1.3006-linux/bin/sonar-scanner /usr/local/bin/sonar-scanner

# Temel kullanım
sonar-scanner 
  -Dsonar.projectKey=my-project 
  -Dsonar.sources=. 
  -Dsonar.host.url=http://sonarqube.sirketim.local:9000 
  -Dsonar.login=sqp_xxxxxxxxxxxxxxxxxxxx

SonarQube’ün Technical Debt Ratio metriği şu formülle hesaplanıyor: Düzeltme maliyeti / Tüm kodu yeniden yazma maliyeti. Bu oran yüzde 5’in altındaysa “A” notu, yüzde 10’u geçiyorsa ciddi sorun var demek.

Ama tek metriks yetmez. Şu metrikleri birlikte takip etmenizi öneririm:

  • Code Smells: Doğrudan hata değil ama bakım zorluğu yaratan durumlar
  • Cognitive Complexity: Kodun zihinsel olarak anlaşılmasının ne kadar zor olduğu
  • Duplications: Kopyala-yapıştır oranı, yüzde 3’ün üzeri kötü işaret
  • Test Coverage: Satır bazlı kapsam, kritik modüllerde yüzde 80 altı tehlikeli
  • Maintainability Rating: A’dan E’ye kadar olan bakım kolaylığı notu

SonarQube Quality Gate Konfigürasyonu

Mevcut borcu ölçmek yeterli değil, yeni borç birikmesini de engellemeniz gerekiyor. Bunun için Quality Gate kullanıyoruz:

# SonarQube API ile Quality Gate oluşturma
curl -u admin:your_password -X POST 
  "http://sonarqube.local:9000/api/qualitygates/create" 
  -d "name=Strict-Gate"

# Yeni kod için teknik borç oranı koşulu ekleme
curl -u admin:your_password -X POST 
  "http://sonarqube.local:9000/api/qualitygates/create_condition" 
  -d "gateId=2&metric=new_technical_debt&op=GT&error=30"

# Code coverage koşulu
curl -u admin:your_password -X POST 
  "http://sonarqube.local:9000/api/qualitygates/create_condition" 
  -d "gateId=2&metric=new_coverage&op=LT&error=80"

ESLint ile JavaScript/TypeScript Tarafında Ölçüm

Frontend ve Node.js tarafında ESLint olmadan iş yapmak artık mümkün değil. Sadece hata bulmak için değil, teknik borcu görünür kılmak için de kullanıyoruz:

# ESLint kurulumu ve başlatma
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

# Projeyi tarayıp rapor almak
npx eslint src/ --format json --output-file eslint-report.json

# Sadece hata sayısını öğrenmek için
npx eslint src/ --format compact 2>&1 | tail -1

# Otomatik düzeltilebilir sorunları bulmak
npx eslint src/ --fix-dry-run --format json | 
  python3 -c "import sys,json; data=json.load(sys.stdin); 
  print(f'Fixable: {sum(f["fixableErrorCount"]+f["fixableWarningCount"] for f in data)}')"

Tipik bir .eslintrc.js konfigürasyonu teknik borcu yakalamak için şöyle görünmeli:

# .eslintrc.js içeriği için cat kullanımı
cat > .eslintrc.js << 'EOF'
module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  rules: {
    // Karmaşıklık limiti - bu kuralı ihlal eden her fonksiyon teknik borçtur
    'complexity': ['error', { max: 10 }],
    // Fonksiyon uzunluğu
    'max-lines-per-function': ['warn', { max: 50 }],
    // Tekrarlanan kod engeli
    'no-duplicate-imports': 'error',
    // TypeScript spesifik
    '@typescript-eslint/no-explicit-any': 'warn',
    '@typescript-eslint/no-unused-vars': 'error'
  }
};
EOF
echo "ESLint konfigürasyonu oluşturuldu"

CI/CD Pipeline’a Entegrasyon: Borcu Geçit’te Durdurmak

Teknik borcu ölçmek güzel ama bunu otomatik bir kapıya çevirmezseniz anlamsız kalıyor. GitLab CI ile SonarQube entegrasyonu şöyle kurulabilir:

# .gitlab-ci.yml örneği
cat > .gitlab-ci.yml << 'YAML'
sonarqube-check:
  stage: test
  image: sonarsource/sonar-scanner-cli:latest
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  script:
    - sonar-scanner
      -Dsonar.qualitygate.wait=true
      -Dsonar.projectKey=${CI_PROJECT_NAME}
      -Dsonar.sources=src/
      -Dsonar.tests=tests/
      -Dsonar.python.coverage.reportPaths=coverage.xml
  allow_failure: false
  only:
    - main
    - merge_requests
YAML
echo "GitLab CI konfigürasyonu hazır"

Buradaki allow_failure: false kritik. Quality Gate geçemezse pipeline durmalı. Bu olmadan tüm ölçüm sistemi kağıt üzerinde kalıyor.

Teknik Borcu Azaltmak: Pratik Stratejiler

Ölçtünüz, gördünüz, “Tanrım bu kadar mı?” dediniz. Şimdi ne yapacaksınız? Yıllarca biriken borcu bir sprint’te silmek imkânsız. Ama sistematik yaklaşımla yönetilebilir hale getirebilirsiniz.

Boyscout Kuralı ve Teknik Borç Bütçesi

“Kampı bulduğundan temiz bırak” prensibi burada çok işe yarıyor. Her PR’da dokunduğunuz dosyayı biraz daha temiz bırakın. Bunu ölçmek için SonarQube’ün “New Code” özelliğini kullanın:

# SonarQube'den proje bazlı borç raporu çekme scripti
#!/bin/bash
SONAR_URL="http://sonarqube.local:9000"
SONAR_TOKEN="sqp_xxxxxxxxxxxxxxxxxxxx"
PROJECT_KEY="my-project"

# Teknik borç ve diğer metrikleri çek
curl -s -u "${SONAR_TOKEN}:" 
  "${SONAR_URL}/api/measures/component?component=${PROJECT_KEY}&metricKeys=sqale_index,sqale_debt_ratio,code_smells,duplicated_lines_density,coverage,cognitive_complexity" 
  | python3 -c "
import sys, json
data = json.load(sys.stdin)
measures = {m['metric']: m['value'] for m in data['component']['measures']}
print('=== Teknik Borç Raporu ===')
debt_minutes = int(measures.get('sqale_index', 0))
debt_days = debt_minutes / 480
print(f'Toplam Teknik Borç: {debt_days:.1f} gün ({debt_minutes} dakika)')
print(f'Borç Oranı: %{measures.get("sqale_debt_ratio", "N/A")}')
print(f'Code Smells: {measures.get("code_smells", "N/A")}')
print(f'Kopya Kod: %{measures.get("duplicated_lines_density", "N/A")}')
print(f'Test Coverage: %{measures.get("coverage", "N/A")}')
"

Bu scripti haftalık cron job’a bağlayın, Slack’e ya da e-postaya gönderin. Rakamların gittiği yönü görmek motivasyon sağlıyor.

Karmaşıklığı Düşürmek: Somut Örnek

Yüksek cyclomatic complexity en sık karşılaşılan borç kaynağı. Şöyle bir fonksiyon düşünün:

# Bash'te karmaşık bir fonksiyonun refactor edilmesi örneği
# ÖNCE: İç içe geçmiş if'ler ile karmaşık yapı
check_server_health_old() {
  if ping -c 1 $1 > /dev/null 2>&1; then
    if curl -s -o /dev/null -w "%{http_code}" http://$1:8080/health | grep -q "200"; then
      if df -h / | awk 'NR==2{print $5}' | tr -d '%' | awk '{exit ($1 > 90) ? 1 : 0}'; then
        if free -m | awk 'NR==2{exit ($3/$2 > 0.9) ? 1 : 0}'; then
          echo "OK"
        else
          echo "MEMORY_CRITICAL"
        fi
      else
        echo "DISK_CRITICAL"
      fi
    else
      echo "HTTP_FAIL"
    fi
  else
    echo "UNREACHABLE"
  fi
}

# SONRA: Fonksiyonları ayır, erken return kullan
is_host_reachable() {
  ping -c 1 "$1" > /dev/null 2>&1
}

is_http_healthy() {
  local status_code
  status_code=$(curl -s -o /dev/null -w "%{http_code}" "http://$1:8080/health")
  [[ "$status_code" == "200" ]]
}

is_disk_ok() {
  local usage
  usage=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
  [[ "$usage" -lt 90 ]]
}

is_memory_ok() {
  free -m | awk 'NR==2{exit ($3/$2 > 0.9) ? 1 : 0}'
}

check_server_health() {
  local host="$1"
  is_host_reachable "$host"  || { echo "UNREACHABLE"; return 1; }
  is_http_healthy "$host"    || { echo "HTTP_FAIL"; return 1; }
  is_disk_ok                 || { echo "DISK_CRITICAL"; return 1; }
  is_memory_ok               || { echo "MEMORY_CRITICAL"; return 1; }
  echo "OK"
}

Refactor sonrası her fonksiyon test edilebilir, her biri tek sorumluluk taşıyor ve okunması çok daha kolay.

Infrastructure as Code’daki Teknik Borç

IaC’deki teknik borç genellikle görmezden geliniyor çünkü “kod değil” algısı var. Ama Terraform ya da Ansible’daki borç bazen uygulama kodundakinden çok daha tehlikeli. tflint ve checkov bu alanda yardımcı araçlar:

# Terraform teknik borç taraması
# tflint kurulumu
curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash

# Tarama çalıştırma
cd terraform/
tflint --recursive --format json > tflint-report.json

# Checkov ile güvenlik ve en iyi pratik kontrolü
pip3 install checkov
checkov -d . --framework terraform 
  --output-file-path ./reports/ 
  --soft-fail 
  --compact

# Sonuçları özetle
checkov -d . --framework terraform --output cli 2>&1 | 
  grep -E "(Passed|Failed|Skipped)" | tail -3

Teknik Borç Takibi için Dashboard Kurulumu

Ölçüm ve düzeltme döngüsünü görünür kılmak için basit bir Prometheus metrik exporter yazabilirsiniz:

#!/usr/bin/env python3
# sonarqube_exporter.py - Teknik borç metriklerini Prometheus formatına çevirir
import requests
import time
import os

SONAR_URL = os.environ.get('SONAR_URL', 'http://sonarqube.local:9000')
SONAR_TOKEN = os.environ.get('SONAR_TOKEN', '')
PROJECTS = os.environ.get('SONAR_PROJECTS', 'project1,project2').split(',')

def get_metrics(project_key):
    metrics = 'sqale_index,sqale_debt_ratio,code_smells,coverage,bugs,vulnerabilities'
    response = requests.get(
        f'{SONAR_URL}/api/measures/component',
        params={'component': project_key, 'metricKeys': metrics},
        auth=(SONAR_TOKEN, '')
    )
    if response.status_code != 200:
        return {}
    data = response.json()
    return {m['metric']: float(m['value']) 
            for m in data['component']['measures'] 
            if 'value' in m}

def generate_prometheus_metrics():
    output = []
    for project in PROJECTS:
        metrics = get_metrics(project.strip())
        for metric, value in metrics.items():
            safe_project = project.strip().replace('-', '_')
            output.append(
                f'sonarqube_{metric}{{project="{safe_project}"}} {value}'
            )
    return 'n'.join(output)

# Her 5 dakikada bir güncelle
while True:
    metrics_output = generate_prometheus_metrics()
    with open('/var/lib/prometheus/sonarqube.prom', 'w') as f:
        f.write(metrics_output + 'n')
    print(f"Metrikler güncellendi: {time.strftime('%Y-%m-%d %H:%M:%S')}")
    time.sleep(300)

Bu exporter’ı systemd servisi olarak çalıştırın, Prometheus node exporter’ın textfile collector’ına yönlendirin, Grafana’da görselleştirin. Takımın her sabah baktığı bir dashboard olsun.

Önceliklendirme: Her Borcu Eşit Muamele Etmeyin

Tüm teknik borcu aynı anda çözmeye çalışmak başarısızlıkla biter. Önceliklendirme için şu kriterleri kullanıyorum:

  • Sıklıkla değiştirilen kod: git log --format="%f" src/ | sort | uniq -c | sort -rn | head -20 ile hangi dosyaların en sık değiştiğini görün. Sık değişen dosyalardaki borcu önce temizleyin.
  • Yüksek riski olan borç: Güvenlik açıkları, veri kaybı riski taşıyan kısımlar. SonarQube’ün “Blocker” ve “Critical” severity’leri buraya giriyor.
  • Yavaşlatan borç: CI pipeline’ı 45 dakika yapan optimize edilmemiş testler, her deploy’da 2 saatinizi alan manuel adımlar.
  • Düşük ödeme, yüksek faiz borç: Az iş ile büyük kazanç sağlayacak şeyler. Duplicated code’un tek yere çekilmesi, magic number’ların sabitlere alınması.

Teknik Borç Kültürü: Takımı İkna Etmek

En iyi araçlar bile takım kültürü olmadan işe yaramaz. Product owner’ı teknik borca ikna etmek için “faiz” metaforunu kullanın: “Bu işi şimdi 3 günde yapıyoruz, ama teknik borcu çözmezseniz 6 ay sonra aynı iş 8 gün sürecek.” Bunu SonarQube rakamlarıyla desteklediğinizde çok daha ikna edici oluyor.

Şunu da paylaşayım: Bir sprint’in yüzde 20’sini teknik borç azaltmaya ayırmak çok işe yarıyor. “Tech debt Friday” gibi ritüeller de kimi takımlarda tuttu. Önemli olan düzenlilik.

Sonuç

Teknik borç kaçınılmaz. Bunu kabul etmek, görünür kılmak ve sistematik olarak yönetmek arasında büyük fark var. SonarQube ile ölçün, ESLint ile önleyin, Quality Gate ile yeni borç birikmesini durdurun, CI/CD’ye entegre edin. IaC’yi de bu döngünün dışında bırakmayın.

En önemli adım birinci adım: Mevcut durumu ölçmek. Rakamları gördüğünüzde ne kadar borçlu olduğunuzu anlarsınız. Belki 47 gün, belki 200 gün. Korkmayın. Çözümsüz değil, sadece zaman istiyor. Ve her küçük iyileştirme, üzerine birikeceğiniz zemini sağlamlaştırıyor.

“Sonra düzeltiriz” dediğiniz her şey birikte sunucu patlayan, deployment gecenin 2’sinde gerçekleşen ve takımın morali dibe vuran o an olarak geri dönüyor. Borcun faizi gecikince katlandığı gibi.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir