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:#fff
Mengapa 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 patternon: 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 →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact