Büyük Takımlarda Git Workflow ve Branch Stratejileri
On kişilik bir ekiple çalışırken Git’i yönetmek nispeten kolaydır. Herkes birbirini tanır, kim ne yapıyor bilirsiniz, çakışmalar olsa bile kahve içerken çözüp geçersiniz. Ama ekip büyüdükçe, özellikle 20-30 kişiyi geçince, aynı repo üzerinde çalışmak bambaşka bir boyut kazanır. Yanlış kurgulanmış bir branch stratejisi, sabah saatlerinizin büyük çoğunluğunu merge conflict çözmekle geçirmenize yol açar. Deneyimlerimden derlediğim bu yazıda, büyük takımlarda işe gerçekten yarayan yaklaşımları ve kaçınılması gereken tuzakları aktaracağım.
Neden “Bir Strateji” Şart?
Git son derece esnek bir araç. Bu esneklik bazen kötü alışkanlıkların normalleşmesine zemin hazırlıyor. “Herkes kendi bildiği gibi commit atsın” yaklaşımı küçük ekiplerde bile zamanla sorun çıkarır; büyük ekiplerde ise felaket reçetesidir.
Büyük takımlarda yaşanan en yaygın sorunları şöyle sıralayabilirim:
- Uzun süre açık kalan feature branch’ler: Bir geliştirici 3 hafta boyunca main’den kopar, merge etmeye kalktığında yüzlerce çakışmayla karşılaşır.
- Anlamsız commit mesajları: “fix”, “wip”, “asdf” gibi mesajlar bırakın tarih okumayı, kodu review etmeyi bile zorlaştırır.
- Doğrudan main’e push: “Küçük bir değişiklikti” diye düşünülen her commit, sonunda production’ı patlatır.
- Branch adlandırma kaosу:
ali-feature,yeni-deneme,fix2-final-FINALgibi branch adları ortamı cehenneme çevirir.
Stratejinin yokluğu sadece teknik bir sorun değil, aynı zamanda ekip koordinasyon sorunudur.
Gitflow: Eskinin Güveniliri
Gitflow, Vincent Driessen’in 2010 yılında ortaya attığı ve uzun yıllar boyunca endüstri standardı haline gelen bir modeldir. Özellikle versiyonlu yazılım geliştiren, belirli release döngüleri olan ekipler için hâlâ geçerliliğini koruyor.
Temel branch yapısı şöyledir:
- main: Sadece production-ready kod içerir. Direkt commit kesinlikle yasak.
- develop: Aktif geliştirmenin yapıldığı dal. Feature branch’ler buraya merge edilir.
- feature/*: Her yeni özellik için açılan geçici dallar.
- release/*: Release hazırlığı için develop’tan ayrılan dallar.
- hotfix/*: Production’da bulunan kritik hatalar için main’den açılan dallar.
# Gitflow başlatma (gitflow eklentisiyle)
git flow init
# Yeni bir feature başlatma
git flow feature start kullanici-profil-sayfasi
# Feature'ı tamamlama (develop'a merge eder)
git flow feature finish kullanici-profil-sayfasi
# Release branch açma
git flow release start 2.4.0
# Hotfix başlatma
git flow hotfix start kritik-guvenlik-acigi
Gitflow’un avantajı net bir yapı sunmasıdır. Herkes hangi branch’te ne yapılacağını bilir. Dezavantajı ise karmaşıklığı. Özellikle continuous delivery yapan ekipler için develop-main arasındaki bu çift katmanlı yapı gereksiz bir yük haline gelebilir.
GitHub Flow: Sadeliğin Gücü
GitHub’ın kendi geliştirdiği bu model, Gitflow’un aksine son derece minimalist. Temel prensibi şu: main her zaman deploy edilebilir olmalı.
Çalışma akışı şöyle işler:
- main’den feature branch aç
- Değişikliklerini yap, küçük ve anlamlı commit’ler at
- Pull Request (PR) aç
- Code review yapıldıktan sonra merge et
- Hemen deploy et
# Main'den yeni branch açma
git checkout main
git pull origin main
git checkout -b feature/odeme-sistemi-entegrasyonu
# Değişiklikler yapılıyor...
git add .
git commit -m "feat: Iyzico ödeme gateway entegrasyonu eklendi"
# Branch'i push et
git push origin feature/odeme-sistemi-entegrasyonu
# PR açıldıktan sonra, merge sonrası temizlik
git checkout main
git pull origin main
git branch -d feature/odeme-sistemi-entegrasyonu
GitHub Flow, CI/CD pipeline’ı güçlü olan ekiplerde son derece iyi çalışır. Microservice mimarisine geçen, sık deploy yapan modern ekipler için ideal. Tek dezavantajı: “main her zaman deploy edilebilir” kuralını gerçekten uygulamak için sağlam bir test altyapısına ihtiyaç var.
Trunk-Based Development: Gerçek Anlamda Sürekli Entegrasyon
Büyük tech şirketlerinin (Google, Facebook, Microsoft) kullandığı bu yaklaşımda herkes doğrudan main‘e (trunk’a) push eder ya da çok kısa ömürlü branch’ler kullanır. “Kısa ömürlü” derken, bir günden fazla açık kalan branch ciddiye alınmaz.
Bu modelin özü, entegrasyon sorunlarını erken yakalamaktır. Uzun süre ayrı kalan branch’ler büyük merge sorunlarına yol açar, trunk-based development bunu ortadan kaldırır.
# Her sabah yapılması gereken ilk şey
git checkout main
git pull --rebase origin main
# Küçük, atomik commit'ler
git add src/auth/login-validator.js
git commit -m "fix: Email validasyon regex'i Türkçe karakter desteği için güncellendi"
# Feature flag kullanımı - tamamlanmamış kod prod'a gider ama aktif değildir
# config/features.js dosyasında:
# ENABLE_NEW_DASHBOARD: process.env.NEW_DASHBOARD_ENABLED === 'true'
Trunk-based development’ın çalışması için feature flag mekanizması kritik öneme sahiptir. Yarı bitmis bir özelliği main’e merge edebilirsiniz, ama bir flag ile kapalı tutarsınız. Bu sayede entegrasyon sürekliliği korunur.
Bu yaklaşım herkese uymaz. Özellikle junior geliştirici oranı yüksek, test kültürü henüz oturmamış ekiplerde ciddi riskler taşır.
Pratik Branch Adlandırma Konvansiyonları
Ekip büyüdükçe branch adlandırması kritik bir mesele haline gelir. Tutarsız adlandırma, automation’ı zorlaştırır ve mental yükü artırır.
Önerdiğim format:
<tip>/<ticket-no>-<kisa-aciklama>
Somut örnekler:
# Feature branch'ler
git checkout -b feature/PRJ-342-sepet-urun-sil-buton
# Bug fix branch'ler
git checkout -b fix/PRJ-401-login-timeout-sorunu
# Hotfix branch'ler (production acil)
git checkout -b hotfix/PRJ-509-odeme-null-pointer
# Chore/altyapı işleri
git checkout -b chore/PRJ-210-webpack-5-migration
# Branch listesini düzenli görmek için
git branch --sort=-committerdate | head -20
Ticket numarasını branch adına eklemek, Jira/Linear gibi proje yönetim araçlarıyla otomatik entegrasyona da kapı açar. Bir PR açıldığında ticket otomatik “In Review” durumuna geçebilir.
Commit Mesajı Standartları: Conventional Commits
Büyük ekiplerde commit mesajı standardı olmadan tarih okumak işkenceye döner. Conventional Commits spesifikasyonu bu problemi çözer ve aynı zamanda otomatik changelog üretimini mümkün kılar.
Format:
<tip>(<kapsam>): <açıklama>
[isteğe bağlı gövde]
[isteğe bağlı footer]
# Doğru commit örnekleri
git commit -m "feat(auth): Google OAuth2 entegrasyonu eklendi"
git commit -m "fix(api): Kullanıcı listesi pagination sorunu giderildi"
git commit -m "docs(readme): Kurulum adımları Türkçe'ye çevrildi"
git commit -m "refactor(db): Kullanıcı sorguları index kullanacak şekilde optimize edildi"
git commit -m "perf(images): WebP formatına geçiş ile sayfa yükleme %40 hızlandı"
git commit -m "chore(deps): Jest 29'a güncellendi"
# Breaking change bildirimi
git commit -m "feat(api)!: v2 endpoint formatına geçildi
BREAKING CHANGE: /api/v1 endpointleri artık desteklenmiyor.
Migration rehberi: docs/api-v2-migration.md"
Bu standartı ekibe zorla benimsetmenin en iyi yolu commitlint ile pre-commit hook kullanmaktır:
# commitlint kurulumu
npm install --save-dev @commitlint/cli @commitlint/config-conventional husky
# .commitlintrc.json
echo '{"extends": ["@commitlint/config-conventional"]}' > .commitlintrc.json
# Husky hook'u ayarlama
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
Artık standart dışı bir commit mesajı yazmaya kalktığınızda sistem sizi engelleyecektir. “Ben hata yapmam” diyen takım arkadaşı için de geçerli.
Merge Stratejileri: Squash mı, Rebase mi, Merge mi?
Bu konu büyük ekiplerde en çok tartışılan meselelerden biri. Her stratejinin kendi trade-off’ları var.
Squash Merge: Bir feature branch’teki tüm commit’leri tek bir commit’te birleştirir. Main branch’in tarihi temiz kalır ama feature branch’in detaylı geçmişini kaybedersiniz.
# Squash merge örneği
git checkout main
git merge --squash feature/kullanici-profili
git commit -m "feat(profil): Kullanıcı profil sayfası eklendi (#342)"
Rebase ve Fast-Forward: Branch’in geçmişini main’in üzerine yeniden yazar. Doğrusal bir tarih oluşturur, ama public branch’lerde kullanmak tehlikelidir.
# Interactive rebase ile commit'leri temizleme
git checkout feature/odeme-entegrasyonu
git rebase -i main
# Açılan editörde:
# pick 3a4b5c İlk draft
# squash 7d8e9f WIP commit
# squash 1a2b3c fix typo
# reword 4c5d6e feat: Ödeme gateway bağlantısı kuruldu
Merge Commit: Tüm geçmişi korur, ama karmaşık bir graf oluşturabilir. Merge commit sayısı fazlaysa git log okumak zorlaşır.
# No-fast-forward merge (merge commit'i zorunlu kılar)
git merge --no-ff feature/yeni-ozellik -m "Merge: Yeni özellik entegrasyonu (#421)"
Önerim: GitHub Flow kullanıyorsanız squash merge, Gitflow kullanıyorsanız no-ff merge tercih edin. Tutarlılık, hangi stratejiyi seçtiğinizden daha önemli.
Büyük Ekiplerde Code Review Akışını Optimize Etmek
PR süreçleri yavaşladığında ekip verimliliği ciddi şekilde düşer. Bunu önlemek için bazı pratik önlemler:
CODEOWNERS dosyası ile hangi dosyanın sahibinin kim olduğunu tanımlayabilirsiniz. Bu sayede ilgili kişiler otomatik reviewer olarak eklenir:
# .github/CODEOWNERS dosyası örneği
# Global sahip
* @ahmet-yilmaz
# Frontend dosyaları
/src/frontend/ @frontend-takim
# Altyapı değişiklikleri
/infrastructure/ @devops-takim
/docker/ @devops-takim
# Güvenlik kritik dosyalar
/src/auth/ @guvenlik-sorumlusu @tech-lead
Branch koruma kuralları da büyük ekiplerde şart. GitHub’da repository settings’ten yapılandırılan bu kurallar şunları sağlayabilir:
- Direkt push engeli (sadece PR ile merge)
- En az X reviewer onayı zorunluluğu
- CI check’lerinin geçmesi zorunluluğu
- Stale approval’ların sıfırlanması (yeni push gelince eski onaylar iptal)
# GitHub CLI ile branch koruma kuralı ekleme
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}'
Gerçek Dünya Senaryosu: Acil Hotfix Süreci
Saat 23:00, production’da kritik bir güvenlik açığı keşfedildi. Ekibinizin yarısı uyuyor, CI pipeline tam ortada. İşte bu durumda nasıl hareket etmeli:
# 1. Adım: Main'in son halini al
git checkout main
git pull origin main
# 2. Adım: Hotfix branch'i aç
git checkout -b hotfix/PRJ-601-sql-injection-auth-endpoint
# 3. Adım: Sadece gerekli değişikliği yap, başka hiçbir şeye dokunma
# (Bu kuralı ihlal eden her "ufak düzeltme" sizi daha büyük sorunlara sürükler)
vim src/api/auth/login.controller.js
# 4. Adım: Test et, commit at
git add src/api/auth/login.controller.js
git commit -m "fix(auth): SQL injection açığı parametreli sorgu kullanılarak kapatıldı
CVE referansı: CVE-2024-XXXX
Etkilenen endpoint: POST /api/auth/login
Çözüm: Raw string interpolation yerine prepared statement kullanıldı"
# 5. Adım: Main'e merge et
git checkout main
git merge --no-ff hotfix/PRJ-601-sql-injection-auth-endpoint
git tag -a v2.4.1 -m "Güvenlik hotfix: SQL injection açığı kapatıldı"
git push origin main --tags
# 6. Adım: Değişikliği develop'a da yansıt (Gitflow kullanıyorsanız)
git checkout develop
git merge --no-ff hotfix/PRJ-601-sql-injection-auth-endpoint
git push origin develop
# 7. Adım: Hotfix branch'ini temizle
git branch -d hotfix/PRJ-601-sql-injection-auth-endpoint
git push origin --delete hotfix/PRJ-601-sql-injection-auth-endpoint
Hotfix sürecinde yaşanan en yaygın hata, “şu da hızlıca düzeltelim” tuzağına düşmektir. Hotfix branch’i tek bir soruna odaklanmalı, kapsamı kesinlikle genişletilmemelidir.
Stale Branch Yönetimi
Ekip büyüdükçe repo’da biriken atıl branch sayısı da artar. Aylarca dokunulmayan branch’ler hem repo’yu kirletir hem de yanlışlıkla kullanılma riski taşır. Bu işi otomatize etmek gerekiyor:
# 30 günden uzun süredir güncellenmemiş branch'leri listele
git for-each-ref --sort=committerdate refs/remotes/
--format='%(committerdate:short) %(refname:short)' |
awk -v date="$(date -d '30 days ago' +%Y-%m-%d)" '$1 < date'
# Belirli bir pattern'daki eski branch'leri bulk sil (dikkatli kullanın!)
git branch -r | grep 'origin/feature/' |
while read branch; do
last_commit=$(git log -1 --format="%ci" $branch)
echo "$last_commit $branch"
done | sort
# Yerel takip edilmeyen remote branch'leri temizle
git remote prune origin
# Merge edilmiş branch'leri listele ve sil
git branch --merged main | grep -v "main|develop|release" |
xargs -r git branch -d
Bu scripti bir CI job olarak haftalık çalıştırmanızı öneririm. Ekibe bildirim göndererek “şu branch’ler 30 gün sonra silinecek” uyarısı yapılabilir.
Monorepo Ortamında Git Stratejisi
Birden fazla servis ya da uygulama tek bir repo’da yaşıyorsa (monorepo), branch stratejisi daha da kritik hale gelir. Her değişiklik etkilenen servisleri açıkça belirtmeli:
# Monorepo'da kısmı checkout (Git 2.25+)
git sparse-checkout init --cone
git sparse-checkout set services/payment-service shared/utils
# Sadece belirli dizindeki değişiklikleri takip eden log
git log --oneline -- services/auth-service/
# Belirli bir servisi etkileyen son değişiklikler
git log --since="2 weeks ago" --oneline -- services/user-service/
# Monorepo'da tag stratejisi: servis adı prefix'i kullan
git tag -a payment-service/v1.3.2 -m "Payment service: Refund akışı eklendi"
git tag -a auth-service/v2.1.0 -m "Auth service: MFA desteği eklendi"
Monorepo’da Nx, Turborepo veya Lerna gibi araçlar kullanıyorsanız, bu araçların Git entegrasyonunu aktif etmek, sadece değişen servislerin CI’da build edilmesini sağlar. Bu, büyük ekiplerde CI süresini dramatik biçimde kısaltır.
Sonuç
Büyük ekiplerde Git workflow seçimi, teknik bir tercih olmaktan öte ekibinizin kültürünü ve çalışma ritminizi şekillendirir. Gitflow, GitHub Flow ve trunk-based development’ın hangisinin sizin için doğru olduğunu belirlerken şu soruları sorun: Ne sıklıkla deploy yapıyorsunuz? Test altyapınız ne kadar güçlü? Ekibinizdeki junior-senior dengesi nasıl?
Hiçbir model kutsal değil. Ben sektörde gördüğüm en başarılı ekiplerin, bir modeli olduğu gibi uygulamak yerine kendi ihtiyaçlarına göre adapte ettiğini gözlemledim. Önemli olan esneklik değil, tutarlılık. Hangi stratejiyi seçerseniz seçin, onu yazıya döküp ekibin erişebileceği bir yerde tutun. “Herkes zaten biliyor” dediğiniz kurallar, genellikle kimsenin tam olarak bilmediği kurallardır.
Son bir not: Bu yazıda anlattığım her şeyi bir anda uygulamaya kalkmayın. Önce commit mesajı standardını oturtun, sonra branch koruma kurallarını ekleyin, ardından review süreçlerini iyileştirin. Kademeli iyileştirme, radikal dönüşüm denemelerinden her zaman daha kalıcı sonuç verir.
