Gitflow #
Git tanpa workflow yang disepakati adalah resep kekacauan — branch yang tidak jelas tujuannya, merge langsung ke main karena terburu-buru, hotfix yang lupa di-merge balik ke develop, dan rilis yang tidak bisa di-rollback dengan bersih. Gitflow adalah salah satu jawaban atas masalah ini: model branching yang memisahkan concern pengembangan fitur, persiapan rilis, dan perbaikan production ke dalam branch yang memiliki tujuan dan aturan yang jelas. Tapi Gitflow juga memiliki biaya — ia kompleks, verbose, dan bisa menjadi bottleneck untuk tim yang bergerak cepat. Artikel ini membahas Gitflow dari strukturnya yang fundamental, alur kerja end-to-end, perbandingan dengan workflow alternatif, dan yang sering menjadi perdebatan: kapan harus deploy dari tag vs dari branch main.
Mengapa Gitflow Ada #
Vincent Driessen memperkenalkan Gitflow pada 2010 ketika dunia software masih sangat berbeda — continuous deployment belum umum, tim lebih besar, dan siklus rilis lebih panjang (mingguan atau bulanan). Ia membutuhkan model yang bisa:
Masalah yang Gitflow coba selesaikan:
1. Pengembangan fitur yang paralel tanpa mengganggu stabilitas
→ Setiap developer bisa kerja di branch sendiri tanpa takut
"merusak" kode yang sedang ditest atau akan dirilis
2. Proses rilis yang terpisah dari pengembangan aktif
→ Tim bisa terus develop fitur baru sementara tim lain
melakukan final testing dan bugfix untuk rilis yang akan datang
3. Hotfix yang tidak mengganggu sprint yang sedang berjalan
→ Bug production diperbaiki dari main langsung, bukan dari develop
yang mungkin sudah ada setengah fitur belum selesai
4. History yang bersih dan bisa di-audit
→ Setiap versi rilis jelas — tag di main menunjukkan persis
kode apa yang ada di production pada waktu tertentu
Lima Branch Gitflow dan Perannya #
gitGraph commit id: "initial" branch develop checkout develop commit id: "dev setup" branch feature/login checkout feature/login commit id: "add login UI" commit id: "add login logic" checkout develop merge feature/login id: "merge login" branch feature/payment checkout feature/payment commit id: "add payment" checkout develop merge feature/payment id: "merge payment" branch release/1.0.0 checkout release/1.0.0 commit id: "bump version" commit id: "fix minor bugs" checkout main merge release/1.0.0 id: "release v1.0.0" tag: "v1.0.0" checkout develop merge release/1.0.0 id: "sync develop" checkout main branch hotfix/1.0.1 checkout hotfix/1.0.1 commit id: "fix critical bug" checkout main merge hotfix/1.0.1 id: "hotfix v1.0.1" tag: "v1.0.1" checkout develop merge hotfix/1.0.1 id: "sync hotfix"
Branch main — Production History
#
Branch main (atau master) adalah representasi dari kode yang ada atau pernah ada di production. Setiap commit di sini merupakan versi yang pernah dirilis, ditandai dengan tag versi.
Aturan main:
✗ Tidak boleh ada direct commit ke main
✗ Tidak boleh merge feature branch langsung ke main
✓ Hanya menerima merge dari release/* dan hotfix/*
✓ Setiap merge ke main harus disertai tag versi
Mengapa ketat?
→ Main adalah "single source of truth" untuk production
→ Jika main berisi kode yang belum ditest atau belum siap,
rollback menjadi sulit dan tidak jelas versi mana yang aman
Branch develop — Integration Branch
#
Branch develop adalah tempat semua fitur yang sudah selesai berkumpul sebelum dirilis. Ia adalah “production-in-progress” — mencerminkan apa yang akan ada di rilis berikutnya.
Aturan develop:
✗ Tidak boleh direct commit fitur ke develop
✓ Menerima merge dari feature/*, release/*, dan hotfix/*
✓ Harus selalu dalam kondisi "bisa di-build dan di-test"
✓ CI harus berjalan di setiap push ke develop
Mengapa develop terpisah dari main?
→ Memisahkan "sedang dikerjakan" dari "sudah production-ready"
→ Developer bisa integrate dan test bersama tanpa
mempengaruhi stabilitas main
Branch feature/* — Pengembangan Fitur
#
Setiap fitur baru dikerjakan di branch terpisah, dibuat dari develop, dan di-merge kembali ke develop saat selesai.
# Membuat feature branch
git checkout develop
git pull origin develop
git checkout -b feature/user-profile
# Setelah selesai — merge ke develop via PR/MR
git checkout develop
git merge --no-ff feature/user-profile # --no-ff menjaga history merge
git branch -d feature/user-profile
git push origin develop
# Naming convention yang disarankan:
feature/login-google
feature/payment-integration
feature/order-history-export
feature/JIRA-123-add-search # bisa include ticket ID
Aturan feature branch:
✓ Dibuat dari develop (bukan dari main!)
✓ Satu branch per fitur atau per story
✓ Dijaga agar tidak terlalu lama hidup (idealnya < 1–2 sprint)
✓ Di-merge ke develop via Pull Request yang harus di-review
✓ Dihapus setelah di-merge
Anti-pattern:
✗ Feature branch yang hidup berminggu-minggu tanpa update dari develop
→ Divergence semakin besar → merge conflict semakin besar
✗ Satu developer tidak pernah sync dengan develop terbaru
→ Integration hell saat akhirnya merge
Branch release/* — Persiapan Rilis
#
Saat develop sudah memiliki semua fitur yang akan dirilis, dibuat release branch untuk final preparation — bugfix terakhir, update versi, update changelog — tanpa ada fitur baru.
# Membuat release branch
git checkout develop
git checkout -b release/1.3.0
# Aktivitas di release branch:
# - Fix bug minor yang ditemukan saat testing
# - Update versi di package.json / go.mod / build.gradle
# - Update CHANGELOG.md
# - Update dokumentasi
# TIDAK boleh: menambahkan fitur baru!
# Setelah siap dirilis:
git checkout main
git merge --no-ff release/1.3.0
git tag -a v1.3.0 -m "Release v1.3.0"
git checkout develop
git merge --no-ff release/1.3.0 # sync perubahan di release ke develop!
git branch -d release/1.3.0
Tujuan release branch:
→ Isolasi "polishing" rilis dari pengembangan fitur baru yang sedang berjalan
→ Tim QA bisa focus testing di release/1.3.0 sementara tim dev
sudah mulai mengerjakan fitur untuk 1.4.0 di develop
→ Bugfix di release tidak menunda sprint berikutnya
Yang boleh di release branch:
✓ Bugfix minor hasil testing
✓ Update versi dan changelog
✓ Update dokumentasi rilis
Yang TIDAK boleh:
✗ Menambahkan fitur baru (fitur baru masuk ke develop, bukan release)
✗ Membiarkan release branch hidup terlalu lama
Branch hotfix/* — Perbaikan Darurat Production
#
Ketika ada bug kritikal di production yang harus diperbaiki sekarang, tidak bisa menunggu release cycle berikutnya, hotfix branch dibuat langsung dari main.
# Bug kritikal ditemukan di production v1.3.0
git checkout main
git checkout -b hotfix/1.3.1
# Perbaiki bug
# Update versi ke 1.3.1
# Update changelog
# Merge ke main dan tag
git checkout main
git merge --no-ff hotfix/1.3.1
git tag -a v1.3.1 -m "Hotfix v1.3.1: fix payment gateway timeout"
# WAJIB merge ke develop juga!
git checkout develop
git merge --no-ff hotfix/1.3.1 # agar fix tidak hilang di rilis berikutnya
git branch -d hotfix/1.3.1
Merge hotfix ke develop adalah langkah yang paling sering terlupakan. Jika hotfix tidak di-merge ke develop, perbaikan bug yang sama harus dilakukan lagi saat release berikutnya — karena develop tidak memiliki fix tersebut. Ini dikenal sebagai version drift dan bisa menyebabkan bug yang sudah “diperbaiki” muncul kembali di rilis berikutnya.
Deployment: Tag vs Branch main #
Ini adalah topik yang paling sering menimbulkan perdebatan, terutama bagi engineer yang berpindah antara tim dengan workflow berbeda.
flowchart LR
subgraph WrongWay["Cara yang kurang tepat untuk Gitflow"]
WDev["develop"]
WMain["main (HEAD)"]
WProd["Production"]
WDev -->|"merge"| WMain
WMain -->|"deploy dari branch HEAD"| WProd
Note1["main HEAD bersifat mutable\nbisa berubah kapan saja"]
end
subgraph RightWay["Cara yang benar untuk Gitflow"]
RDev["develop"]
RMain["main"]
RTag["tag v1.4.0\n(immutable)"]
RProd["Production"]
RDev -->|"merge via release"| RMain
RMain -->|"create tag"| RTag
RTag -->|"deploy dari tag"| RProd
Note2["tag bersifat immutable\nrollback selalu konsisten"]
end
style Note1 fill:#E74C3C,color:#fff
style Note2 fill:#27AE60,color:#fffMengapa tag, bukan branch main?
Branch bersifat mutable:
Commit baru bisa ditambahkan kapan saja
main@HEAD hari ini tidak sama dengan main@HEAD minggu lalu
→ Deploy dari HEAD berarti tidak bisa direproduksi secara konsisten
Tag bersifat immutable:
v1.4.0 selalu menunjuk ke commit yang sama
Tidak bisa berubah setelah dibuat
→ Deploy dari tag berarti bisa di-rollback ke versi yang persis sama
Contoh konkret manfaat tag:
Incident di production! Butuh rollback ke versi sebelumnya.
Dengan tag:
kubectl set image deployment/api api=gcr.io/project/api:v1.3.0
→ Selalu dapat image yang sama, guaranteed
Dengan branch HEAD:
main di waktu rollback mungkin berbeda dari main saat deploy pertama
→ Harus lacak SHA commit yang tepat — lebih sulit dan rawan kesalahan
# GitHub Actions — CI/CD yang trigger dari tag
name: Deploy to Production
on:
push:
tags:
- 'v*.*.*' # trigger saat ada tag baru dengan format vX.Y.Z
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Extract version from tag
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
- name: Build Docker image
run: |
docker build -t gcr.io/myproject/api:${{ env.VERSION }} .
docker push gcr.io/myproject/api:${{ env.VERSION }}
- name: Deploy to production
run: |
kubectl set image deployment/api \
api=gcr.io/myproject/api:${{ env.VERSION }}
# Keuntungan:
# → Deploy hanya terjadi saat ada keputusan eksplisit (create tag)
# → Setiap deployment bisa di-reproduce dan di-rollback dengan bersih
# → Audit trail yang jelas: kapan versi apa di-deploy
Gitflow vs GitHub Flow vs Trunk-Based Development #
Gitflow bukan satu-satunya workflow yang ada. Penting untuk tahu alternatifnya agar bisa memilih yang tepat.
Gitflow:
Struktur: 5 branch types (main, develop, feature, release, hotfix)
Cocok untuk: Enterprise, tim besar, release berbasis versi
Kelebihan: Kontrol rilis sangat ketat, mendukung multiple version
Kekurangan: Kompleks, banyak merge overhead, tidak ideal untuk daily deploy
GitHub Flow:
Struktur: main + feature branches saja
Cocok untuk: Startup, tim kecil, continuous deployment
Alur: feature branch → PR → merge ke main → deploy otomatis
Kelebihan: Sangat sederhana, cepat, cocok untuk CD
Kekurangan: Tidak ada isolation untuk QA, kurang cocok untuk multiple version
Trunk-Based Development:
Struktur: Semua developer commit ke satu branch (main/trunk)
Cocok untuk: Tim yang sangat mature dengan disiplin tinggi
Alur: Commit kecil langsung ke main, feature flags untuk hide incomplete features
Kelebihan: Tidak ada long-lived branches, tidak ada merge hell
Kekurangan: Butuh disiplin tinggi, feature flags management jadi kompleks
Panduan memilih workflow:
Tim kecil (< 5 orang) + deploy harian:
→ GitHub Flow atau Trunk-Based
→ Gitflow terlalu overhead
Tim sedang (5–20 orang) + release mingguan/dua mingguan:
→ Gitflow atau GitHub Flow dengan tambahan release branch
→ Sweet spot yang paling umum di industri
Tim besar (> 20 orang) + release bulanan atau versioned:
→ Gitflow
→ Kontrol dan struktur yang Gitflow berikan sangat berharga
Produk dengan multiple version yang harus disupport:
→ Gitflow — mendukung maintenance multiple release branches
→ GitHub Flow tidak punya mekanisme ini
Naming Convention yang Konsisten #
Naming convention yang konsisten memungkinkan CI/CD automation dan mempermudah navigasi.
# Feature branches
feature/<short-description>
feature/user-authentication
feature/payment-integration
feature/TICKET-123-export-report # include ticket ID jika ada
# Release branches
release/<semver>
release/1.3.0
release/2.0.0-beta
# Hotfix branches
hotfix/<semver>
hotfix/1.3.1
hotfix/1.3.2
# Tags
v<semver>
v1.3.0
v1.3.1
v2.0.0
Semantic Versioning di Gitflow #
Gitflow dan SemVer (Semantic Versioning) sangat natural berpasangan.
Format: MAJOR.MINOR.PATCH
MAJOR: perubahan yang breaking — tidak backward compatible
v1.x.x → v2.0.0
→ Biasanya dari release branch dengan major version bump
→ Sangat jarang, biasanya disertai migration guide
MINOR: fitur baru yang backward compatible
v1.2.x → v1.3.0
→ Dari release branch reguler
→ Beberapa fitur baru, tidak ada yang breaking
PATCH: bugfix backward compatible
v1.3.0 → v1.3.1
→ Dari hotfix branch
→ Hanya perbaikan bug, tidak ada fitur baru
Contoh kronologi real:
v1.0.0 → release awal
v1.0.1 → hotfix: fix login timeout
v1.1.0 → release: tambah fitur notifikasi
v1.1.1 → hotfix: fix notifikasi duplikat
v1.2.0 → release: tambah fitur export
v2.0.0 → release: perombakan API (breaking change)
Anti-Pattern Gitflow yang Harus Dihindari #
Feature Branch yang Terlalu Lama Hidup #
// ✗ Anti-pattern:
feature/redesign-checkout → hidup selama 3 bulan
→ develop sudah jauh lebih maju
→ Merge conflict yang sangat besar saat akhirnya merge
→ "Merge hell" yang makan waktu berhari-hari
// ✓ Solusi: Break down fitur besar menjadi fitur kecil
feature/checkout-step-1-cart-review → 3 hari
feature/checkout-step-2-address → 3 hari
feature/checkout-step-3-payment → 4 hari
feature/checkout-step-4-confirmation → 2 hari
→ Masing-masing di-merge ke develop secara berkala
→ Tidak ada divergence yang besar
→ Fitur yang belum selesai di-hide dengan feature flag
Tidak Merge Hotfix ke Develop #
# ✗ Anti-pattern: hotfix di-merge ke main, tapi lupa ke develop
git checkout main
git merge hotfix/1.3.1
git tag v1.3.1
# STOP — tidak merge ke develop!
# Akibat: bug yang sama muncul lagi di v1.4.0!
# ✓ Solusi: selalu merge ke develop juga
git checkout main
git merge --no-ff hotfix/1.3.1
git tag -a v1.3.1 -m "Hotfix: fix payment timeout"
git checkout develop
git merge --no-ff hotfix/1.3.1 # WAJIB!
git push origin develop
Branch Protection yang Tidak Dikonfigurasi #
// ✗ Anti-pattern: tidak ada branch protection
Siapapun bisa push langsung ke main atau develop
→ Satu push yang tidak sengaja bisa merusak production
→ Code review tidak mandatory
// ✓ Solusi: branch protection rules di GitHub/GitLab
Untuk branch main:
- Require pull request before merging
- Require approvals: 1–2 reviewer
- Require status checks to pass (CI harus hijau)
- Restrict pushes: hanya CI bot yang boleh merge
- Do not allow bypassing the above settings
Untuk branch develop:
- Require pull request before merging
- Require approvals: 1 reviewer minimum
- Require status checks to pass
Deploy dari develop ke Production #
// ✗ Anti-pattern yang sering ditemukan di lapangan:
developer merge feature ke develop
→ CI/CD deploy develop langsung ke production
→ Padahal develop mungkin berisi setengah fitur yang belum selesai!
// ✓ Solusi: buat release branch dahulu
develop (sudah stable) → release/x.y.z → testing → merge ke main → tag → deploy
// Mapping yang benar:
develop → staging environment (untuk preview, bukan production!)
main → production (via tag)
Checklist Gitflow #
SETUP AWAL:
□ Branch main dan develop ada dan dikonfigurasi sebagai protected branches
□ Branch protection rules diaktifkan: required PR, required review, CI must pass
□ .gitignore dan .gitattributes dikonfigurasi dengan benar
□ Naming convention branch didokumentasikan dan disepakati tim
□ Semantic versioning disepakati (format tag: vX.Y.Z)
FEATURE DEVELOPMENT:
□ Setiap fitur dikerjakan di feature branch terpisah
□ Feature branch dibuat dari develop (bukan dari main)
□ Feature branch di-sync dengan develop secara berkala (pull/rebase)
□ Feature branch di-merge ke develop via PR dengan review minimal 1 orang
□ Feature branch dihapus setelah di-merge
RELEASE:
□ Release branch dibuat dari develop saat semua fitur siap
□ Tidak ada fitur baru di release branch — hanya bugfix dan finalization
□ Versi di-bump di release branch (package.json, go.mod, changelog)
□ Release branch di-merge ke main DAN develop setelah rilis
□ Tag dibuat di main setelah merge (annotated tag: git tag -a vX.Y.Z)
HOTFIX:
□ Hotfix branch dibuat dari main (bukan dari develop!)
□ Setelah fix, merge ke main DAN develop
□ Patch version di-bump
□ Tag dibuat di main
CI/CD:
□ CI berjalan di setiap push ke feature, develop, release, dan hotfix branch
□ Deployment ke production di-trigger dari tag (bukan dari branch HEAD)
□ Deployment ke staging di-trigger dari develop
□ Rollback procedure didefinisikan dan ditest
Ringkasan #
- Gitflow adalah disiplin rilis, bukan hanya branching model — nilainya ada pada konsistensi. Gitflow yang diterapkan setengah-setengah memberikan overhead tanpa manfaat.
- Lima branch dengan tujuan yang sangat berbeda — main (production history), develop (integration), feature (pengembangan), release (finalisasi rilis), hotfix (perbaikan darurat). Setiap branch punya aturan dari mana dibuat dan ke mana di-merge.
- Hotfix harus di-merge ke develop, tidak hanya ke main — ini adalah langkah yang paling sering terlupakan dan menyebabkan bug yang sama muncul kembali di rilis berikutnya (version drift).
- Deploy dari tag, bukan dari branch HEAD — tag bersifat immutable dan reproducible. Branch HEAD bisa berubah kapan saja. Tag memungkinkan rollback yang bersih dan audit trail yang jelas.
- CI/CD trigger dari tag pattern —
on: push: tags: 'v*.*.*'di GitHub Actions memastikan deployment hanya terjadi saat ada keputusan eksplisit untuk merilis, bukan setiap kali ada commit ke main.- Feature branch harus kecil dan berumur pendek — feature branch yang hidup berminggu-minggu adalah tanda bahwa fitur terlalu besar. Break down menjadi sub-fitur yang bisa di-merge lebih sering.
- Branch protection adalah keharusan — tanpa branch protection, semua aturan Gitflow bisa dilanggar dengan satu
git push --force. Konfigurasi required PR, required review, dan required CI.- Gitflow bukan untuk semua tim — untuk startup early-stage atau tim yang deploy harian, GitHub Flow jauh lebih pragmatis. Pilih workflow berdasarkan kebutuhan tim, bukan tren.
- Release branch memungkinkan paralel development — tim bisa terus mengerjakan fitur untuk versi berikutnya di develop sementara tim lain melakukan final testing di release branch.
- Semantic versioning melengkapi Gitflow — hotfix menghasilkan PATCH bump, release reguler menghasilkan MINOR bump, breaking change menghasilkan MAJOR bump. Konsisten dengan ini membuat changelog dan upgrade path lebih mudah dipahami.
← Sebelumnya: Postmortem Berikutnya: Trunk Based Development →