Git ile Kod İnceleme Süreci ve En İyi Uygulamalar
Bir pull request’e bakıyorsunuz ve 47 yorum var. Çoğu “bu değişken adı daha iyi olabilir” seviyesinde, ama arada gerçek bir bug gizlenmiş. Reviewer’ların yarısı yorgun, yarısı sıkılmış. Sonunda PR merge ediliyor ama o bug production’a giriyor. Bu senaryo tanıdık geliyorsa, kod inceleme sürecinizde bir şeyler ters gidiyordur.
Git tabanlı bir kod inceleme süreci sadece “başkası baksın, hata bulursun” mantığıyla çalışmaz. Doğru branch stratejisi, anlamlı commit mesajları, iyi yapılandırılmış PR’lar ve ekip içi mutabakat olmadan code review bir formaliteye dönüşür. Bu yazıda kurumsal ortamlarda ve küçük ekiplerde fiilen işe yarayan pratikleri paylaşacağım.
Branch Stratejisi: Temelin Temeli
Kod inceleme sürecinin kalitesi büyük ölçüde branch stratejinizle başlar. Feature branch’ler çok uzun yaşarsa merge conflict cehennemi, çok kısa yaşarsa yetersiz review süreci ortaya çıkar.
Gitflow hâlâ popüler ama her ekip için doğru değil. Eğer haftalık veya aylık release döngünüz varsa mantıklı. Ama sürekli deployment yapıyorsanız trunk-based development veya GitHub Flow çok daha az sürtünme yaratır.
Pratikte en çok işe yarayan yaklaşım şu: main veya master her zaman deploy edilebilir durumda. develop entegrasyon branch’i. Feature branch’ler kısa ömürlü, maksimum birkaç günlük.
# Feature branch açma - isim konvansiyonu önemli
git checkout -b feature/JIRA-1234-kullanici-giris-sayfasi
# Hotfix branch - production'da yangın var
git checkout -b hotfix/JIRA-5678-sifre-sifirlama-bug
# Release branch
git checkout -b release/v2.4.0
Branch isimlerini rastgele koymayın. fix-stuff, test2, ahmet-branch gibi isimler ekibe hiçbir şey söylemez. Ticket numarası + kısa açıklama formatı hem arama yapmayı kolaylaştırır hem de PR oluştururken otomatik linkleme sağlar.
Branch Koruma Kuralları
GitHub, GitLab veya Bitbucket fark etmeksizin main branch’inizi mutlaka koruma altına alın. Bu bir paranoya değil, minimum hijyen gereksinimi.
# GitLab CLI ile protected branch ayarı
glab api projects/:id/protected_branches
--method POST
--field name="main"
--field push_access_level=40
--field merge_access_level=40
--field allow_force_push=false
# Mevcut protected branch'leri listele
glab api projects/:id/protected_branches
GitHub tarafında bunu UI’dan yapmak daha pratik ama otomasyon için Terraform veya GitHub Actions kullanıyorsanız:
# GitHub CLI ile branch protection rule
gh api repos/:owner/:repo/branches/main/protection
--method PUT
--field required_status_checks='{"strict":true,"contexts":["ci/tests","ci/lint"]}'
--field enforce_admins=true
--field required_pull_request_reviews='{"required_approving_review_count":2}'
Commit Mesajları: Gelecekteki Kendinize Mektup
Altı ay sonra bir bug’ı araştırırken git log açıyorsunuz ve şunları görüyorsunuz: “fix”, “update”, “wip”, “asdfgh”, “son hali”. Bu ekibin ne yaptığını anlamak için her commit’i tek tek açıp diff’e bakmanız gerekiyor.
Conventional Commits standardı bu sorunu çözüyor. Öğrenme eğrisi düşük, tooling desteği mükemmel.
# Conventional Commits formatı
# <type>(<scope>): <description>
#
# [optional body]
#
# [optional footer(s)]
git commit -m "feat(auth): JWT token yenileme mekanizması eklendi
Kullanıcı session süresi dolmadan 5 dakika önce
otomatik token yenileme tetikleniyor. Bu sayede
aktif kullanıcıların zorla çıkış yapması engellendi.
Closes: JIRA-1234
Breaking change: /api/auth/refresh endpoint'i
artık POST yerine PUT kabul ediyor"
Type kategorileri:
- feat: Yeni özellik
- fix: Bug düzeltmesi
- docs: Sadece dokümantasyon değişikliği
- style: Kod davranışını etkilemeyen format değişiklikleri
- refactor: Ne bug fix ne feature, sadece yeniden yapılandırma
- test: Test ekleme veya düzeltme
- chore: Build süreci, dependency güncellemeleri
- perf: Performans iyileştirmesi
- ci: CI/CD konfigürasyon değişiklikleri
Bu format commitlint ile otomatik olarak doğrulanabilir. .commitlintrc.yml dosyası oluşturun ve pre-commit hook’a bağlayın:
# commitlint kurulumu
npm install -g @commitlint/cli @commitlint/config-conventional
# Pre-commit hook ile entegrasyon
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/sh
npx --no -- commitlint --edit "$1"
EOF
chmod +x .git/hooks/commit-msg
Pull Request Hazırlama: Reviewer’ın İşini Kolaylaştırın
Kötü bir PR şu özelliklere sahiptir: 50+ dosya değişikliği, “çeşitli düzeltmeler” başlığı, sıfır açıklama. Reviewer kim neyi neden değiştirdiğini çözmeye çalışırken saatleri harcıyor ve sonunda yorgunluktan önemli şeyleri kaçırıyor.
İyi bir PR küçük ve odaklıdır. “Ama bu değişiklikler birbirine bağlı” diyorsanız, muhtemelen özelliği daha küçük parçalara bölmeniz gerekiyor.
PR boyutu için pratik bir kural: 400 satırın üzerindeki diff’lerde review kalitesi dramatik şekilde düşüyor. Bu bir araştırma bulgusu, sezgi değil. 200-300 satır ideal bölge.
PR şablonu kullanmak işleri ciddi ölçüde iyileştiriyor. .github/pull_request_template.md dosyası:
## Değişiklik Özeti
<!-- Bu PR'da ne değiştirdiniz? Neden? -->
## Test Edildi mi?
- [ ] Unit testler geçiyor
- [ ] Manuel test yapıldı
- [ ] Edge case'ler düşünüldü
## Ekran Görüntüsü / Video
<!-- UI değişikliği varsa ekleyin -->
## Kontrol Listesi
- [ ] Kod standartlarına uygun
- [ ] Breaking change içermiyor (içeriyorsa açıklayın)
- [ ] Dokümantasyon güncellendi
- [ ] Dependency değişikliği yok (varsa açıklayın)
## İlgili Ticket
Closes #ISSUE_NUMBER
Review Süreci: Ne Bakılmalı, Nasıl Bakılmalı
Reviewer olarak iki farklı gözle bakmanız gerekiyor: “Bu kod doğru çalışıyor mu?” ve “Bu kod altı ay sonra anlaşılır mı?”
Öncelik sırası çok önemli. Birçok ekip style sorunlarına vakit harcarken logic hatalarını gözden kaçırıyor. Gözden geçirme sırası:
Birinci öncelik – Correctness: Kod doğru mu? Edge case’leri handle ediyor mu? Race condition var mı? SQL injection, XSS gibi güvenlik açıkları? Null pointer dereference? Yanlış error handling?
İkinci öncelik – Design: Kod genel mimariye uygun mu? SOLID prensiplerini ihlal eden bir şey var mı? Gereksiz complexity yaratılmış mı?
Üçüncü öncelik – Readability: Değişken ve fonksiyon isimleri anlamlı mı? Yorumlar gerekli ve doğru mu?
Dördüncü öncelik – Style: Linter bunu zaten yapmalı. Manuel review zamanınızı buna harcamayın.
# Reviewer olarak önce genel resmi görün
git log origin/main..HEAD --oneline
# Hangi dosyalar değişmiş?
git diff origin/main --stat
# Specific dosyaya odaklanmak için
git diff origin/main -- src/auth/jwt.service.ts
# Bir commit'in içine bakmak
git show abc123f --stat
Yorum Yazma Kültürü
Yorum yazmak da bir beceri. “Bu yanlış” yazmak yerine neden yanlış olduğunu ve nasıl düzeltilebileceğini açıklamak hem öğretici hem de daha az savunmacı tepkiye yol açıyor.
Prefix sistemi yorumları kategorize etmek için kullanışlı:
- [BLOCKING]: Bu merge edilmeden önce kesinlikle çözülmeli
- [SUGGESTION]: Yapılabilir ama zorunlu değil
- [QUESTION]: Anlayamadım, açıklar mısın?
- [NITPICK]: Çok küçük bir şey, merge’i engellemesin
- [PRAISE]: Bu iyi bir yaklaşım, tebrikler
Bu sistem reviewer ve author arasındaki yanlış anlamaları dramatik ölçüde azaltıyor.
CI/CD ile Otomatik Kalite Kontrol
İnsanların yakalamayı unuttuğu şeyleri makinelere yaptırın. Bir PR açıldığında otomatik olarak çalışması gereken kontroller:
# .github/workflows/pr-checks.yml
name: PR Quality Checks
on:
pull_request:
branches: [main, develop]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Commit message kontrolü
run: |
npx commitlint --from origin/main --to HEAD
- name: Kod linting
run: |
npm run lint
- name: Unit testler
run: |
npm test -- --coverage --coverageThreshold='{"global":{"lines":80}}'
- name: Security scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'HIGH,CRITICAL'
# PR boyutu kontrolü için custom script
# Bu script 400 satırdan büyük PR'ları uyarıyor
CHANGED_LINES=$(git diff origin/main --stat | tail -1 | grep -oP 'd+(?= insertion)')
if [ "${CHANGED_LINES:-0}" -gt 400 ]; then
echo "UYARI: Bu PR $CHANGED_LINES satır değişiklik içeriyor."
echo "Gözden geçirin: PR'ı daha küçük parçalara bölmek mümkün mü?"
exit 1
fi
Merge Stratejileri: Squash mı, Rebase mi, Merge mi?
Bu konuda ekipler çok farklı tercihler yapıyor ve her birinin trade-off’ları var.
Squash merge: Feature branch’teki tüm commit’leri tek bir commit’e indirger. main branch’i temiz kalır. Dezavantajı: Branch içindeki gelişim hikayesi kaybolur.
Rebase merge: Branch commit’lerini main‘in üstüne yeniden yazar. Linear history elde edersiniz ama conflict çözümü daha karmaşık olabilir.
Regular merge: Merge commit oluşturur. Branch history’si korunur ama git log çıktısı karmaşık görünebilir.
# Squash ile merge
git checkout main
git merge --squash feature/JIRA-1234-login-page
git commit -m "feat(auth): kullanıcı giriş sayfası tamamlandı (#1234)"
# Rebase ile temiz history
git checkout feature/JIRA-1234-login-page
git rebase origin/main
# Conflict varsa çöz, sonra:
git push --force-with-lease origin feature/JIRA-1234-login-page
# --force yerine --force-with-lease kullanmak önemli
# Başkasının push ettiği değişikliklerin üstüne yazılmasını engeller
--force-with-lease detayı küçük ama kritik. Takım ortamında --force kullanmak başkasının commit’lerini silmenize yol açabilir. --force-with-lease sadece sizin beklediğiniz remote state varsa force push yapar.
Conflict Çözme: Paniksiz Yaklaşım
Merge conflict gördüğünüzde ilk içgüdü “merge tool’u aç, hepsini kabul et, geç” oluyor. Bu yanlış. Her conflict’i anlayarak çözmek gerekiyor.
# Conflict olan dosyaları görmek
git status
# Conflict detayını anlamak için
git diff --diff-filter=U
# Hangi branch ne getirdi?
git log --merge --oneline
# Üç taraflı diff - base, ours, theirs
git checkout --conflict=diff3 conflicted-file.js
# Sonra dosyayı düzenle, sonra:
git add conflicted-file.js
git commit
# Eğer bir şeyler ters gittiyse her zaman geri dönebilirsiniz
git merge --abort
git rebase --abort
Conflict çözerken “ours” ve “theirs”‘ı karıştırmamak için:
git checkout --ours dosya.js: Sizin (current branch) versiyonugit checkout --theirs dosya.js: Gelen (merge edilen) versiyon
Rebase sırasında bu kavramlar ters çalışır. Rebase yaparken “ours” upstream branch’tir, “theirs” ise sizin commit’lerinizdir. Bu konfüzyona yol açar, kafanız karışırsa git log --merge ile hangi tarafı hangisinin değiştirdiğini kontrol edin.
CODEOWNERS ile Sorumlu Atama
Büyük repo’larda her PR’ı herkes review etmek zorunda kalmadan kritik dosyalar için otomatik reviewer ataması yapılabilir.
# .github/CODEOWNERS dosyası
# Global default - her şeyi bu kişi review eder
* @devops-team
# Infrastructure kodu - sadece infra ekibi
/infrastructure/ @infra-lead @senior-devops
# Güvenlik kritik dosyalar
/src/auth/ @security-team @senior-dev
# Database migration'lar - ekstra dikkat
/db/migrations/ @dba-team @backend-lead
# Frontend components
/src/components/ @frontend-team
# CI/CD pipeline değişiklikleri
/.github/ @devops-team
/Dockerfile @devops-team
/docker-compose*.yml @devops-team
Bu dosya sayesinde main branch’e dokunacak PR’lar otomatik olarak ilgili ekibin review’unu bekler. Yanlış kişi merge etmez, kritik değişiklikler gözden kaçmaz.
Stale Branch Temizliği
Merge edilen branch’lerin temizlenmesi küçük ama önemli bir hijyen meselesi. Yüzlerce stale branch biriktiğinde repo yönetimi zorlaşıyor.
# Merge edilmiş local branch'leri listele
git branch --merged main
# Merge edilmiş local branch'leri sil (main, develop hariç)
git branch --merged main | grep -vE "^*|main|develop|release" | xargs git branch -d
# Remote'da merge edilmiş branch'leri temizle
git remote prune origin
# Daha agresif temizlik - 30 günden eski merge edilmiş branch'ler
git branch -r --merged main | grep -v 'main|develop|HEAD' | while read branch; do
branch_date=$(git log -1 --format="%ci" $branch)
days_old=$(( ($(date +%s) - $(date -d "$branch_date" +%s)) / 86400 ))
if [ $days_old -gt 30 ]; then
echo "Siliniyor: $branch ($days_old gün önce merge edildi)"
git push origin --delete "${branch#origin/}"
fi
done
Bu scripti bir cron job veya scheduled CI job olarak çalıştırabilirsiniz. Aylık temizlik genellikle yeterli.
Gerçek Dünya Senaryosu: Hotfix Process
Production’da kritik bir bug var. Saat gece 02:00. Baskı altında doğru adımları atmak için önceden tanımlanmış bir process şart.
# 1. Main'den hotfix branch aç
git checkout main
git pull origin main
git checkout -b hotfix/v2.4.1-odeme-hatasi
# 2. Düzeltmeyi yap, test et
# ... kod değişiklikleri ...
# 3. Versiyon güncelle
echo "2.4.1" > VERSION
git add -A
git commit -m "fix(payment): null pointer hatası ödeme onaylama adımında giderildi
Sipariş toplamı 0 TL olduğunda (tam indirimli kupon)
payment gateway'e null amount gönderiliyordu.
Bu durum son 3 günde 47 başarısız işleme yol açtı.
Fixes: JIRA-9999
Severity: P0"
# 4. Hızlı review - en az 1 approval zorunlu
git push origin hotfix/v2.4.1-odeme-hatasi
# 5. Main'e merge
git checkout main
git merge --no-ff hotfix/v2.4.1-odeme-hatasi
git tag -a v2.4.1 -m "Hotfix: ödeme onaylama null pointer hatası"
git push origin main --tags
# 6. Develop'a da backport et - yoksa bir sonraki release'de tekrar çıkar
git checkout develop
git cherry-pick abc123f # hotfix commit hash
git push origin develop
# 7. Hotfix branch'i temizle
git push origin --delete hotfix/v2.4.1-odeme-hatasi
git branch -d hotfix/v2.4.1-odeme-hatasi
Adım 6 çok sık unutuluyor. Hotfix’i sadece main‘e merge ederseniz develop branch’inde bug devam eder ve bir sonraki feature release’iyle production’a tekrar girer.
Sonuç
Kod inceleme süreci teknik bir mesele olduğu kadar kültürel bir meseledir. En iyi araçlara ve processlere sahip olsanız bile ekip içinde psikolojik güven yoksa gerçek sorunlar review’da ortaya çıkmaz. Kimse “bu mimari karar yanlış” demekten çekiniyorsa yüzeysel yorumlarla geçiştirilen PR’lar üretirsiniz.
Teknik tarafta önceliklerinizi şöyle sıralayabilirsiniz: Önce branch koruma kurallarını ve CI otomasyonunu kurun, bu “düşük asılı meyveler” en hızlı return on investment sağlıyor. Sonra commit mesajı standardını belirleyin ve commitlint ile otomatize edin. CODEOWNERS dosyası oluşturun. PR şablonunuzu yazın. Bunlar bir-iki saatlik iş ama uzun vadede ekibin saatlerini kurtarıyor.
Küçük PR’lar üzerinde ısrar etmek belki en zor ama en değerli prensip. “Bu feature büyük, PR da büyük olmak zorunda” yaklaşımı neredeyse her zaman yanlış. Feature flag’ler, interface’leri önce merge etme, incomplete ama non-breaking kodu iteratif olarak gönderme gibi teknikler büyük feature’ları da küçük PR’lara dönüştürmenizi sağlar.
Son olarak: Review sürecinizi düzenli aralıklarla retrospektif yapın. “Son çeyrekte kaç bug code review’dan geçti? Hangi tür hatalar sürekli gözden kaçıyor? Review sürelerimiz makul mu?” Veri olmadan iyileştirme yapamazsınız. GitHub’ın ve GitLab’ın PR analytics özellikleri bu verinin büyük kısmını kutudan çıkarıyor, kullanın.
