Build CoreOrdered learning track

Release Candidate Flow

Learn Git In Action - Part 055

Release Candidate Flow sebagai mekanisme stabilisasi release berbasis commit identity, protected branch, tag immutable, test gates, dan audit evidence.

10 min read1881 words
PrevNext
Lesson 55126 lesson track24–68 Build Core
#git#release-engineering#release-candidate#version-control+2 more

Part 055 — Release Candidate Flow

Release candidate bukan sekadar branch bernama rc.

Release candidate adalah proposal release yang menunjuk ke satu commit identity tertentu dan sedang diuji apakah layak dipromosikan menjadi release final.

Mental model paling penting:

RC is not “almost released code”. RC is a commit under evaluation.

Kalau commit berubah, kandidatnya berubah. Kalau tag dipindah, identity-nya berubah. Kalau artifact dibuild ulang dari commit lain, evidence-nya rusak. Kalau fix dimasukkan tanpa jejak cherry-pick/revert yang jelas, audit trail menjadi kabur.

Git memberi kita building block:

  • branch sebagai movable pointer,
  • tag sebagai release marker,
  • commit hash sebagai immutable content identity,
  • cherry-pick sebagai patch replay lintas branch,
  • revert sebagai public-history compensation,
  • protected refs sebagai governance boundary.

Release candidate flow adalah cara menyusun semua itu menjadi sistem release yang bisa diuji, diulang, diaudit, dan dipulihkan.


1. Masalah yang Diselesaikan RC Flow

Tanpa RC flow, release sering berubah menjadi proses informal:

main kelihatan aman
build staging
ada bug kecil
commit fix langsung
build lagi
QA ulang sebagian
ada bug lain
fix lagi
tag release
semoga benar

Masalahnya bukan hanya chaos. Masalahnya adalah hilangnya identity boundary.

Pertanyaan penting tidak bisa dijawab secara deterministic:

  • commit mana yang sebenarnya diuji QA?
  • artifact production dibuat dari commit mana?
  • bug fix apa yang masuk setelah freeze?
  • siapa yang approve perubahan setelah RC pertama?
  • apakah final release sama dengan RC yang lulus?
  • apakah tag release pernah dipindahkan?
  • apakah release notes cocok dengan commit range yang dirilis?

RC flow menjawab dengan satu prinsip:

Every release candidate must be a named, immutable, auditable commit boundary.


2. RC sebagai State Machine

Release bukan event tunggal. Release adalah lifecycle.

Yang penting: setiap transisi harus punya evidence.

TransitionEvidence
Development → CutReleaseBranchbranch cut commit, scope freeze announcement
CutReleaseBranch → RC1RC tag, build artifact, CI result
RC rejecteddefect id, failing test, risk assessment
Fix appliedcommit id, cherry-pick source, approval
RC acceptedQA/security/perf signoff
RC → Releasefinal tag, artifact digest, release notes

3. Invariant RC Flow

RC flow yang sehat punya invariant. Kalau invariant ini dilanggar, release menjadi sulit dipercaya.

Invariant 1 — RC menunjuk ke commit spesifik

Jangan mendefinisikan RC sebagai “branch terbaru”.

Salah:

release/2.8 sekarang adalah RC.

Benar:

v2.8.0-rc.1 = commit 8f3a9c2...

Branch boleh bergerak. RC tidak boleh bergerak secara diam-diam.


Invariant 2 — Artifact dibuild dari commit/tag yang sama

Artifact release harus membawa metadata:

version: 2.8.0-rc.1
commit: 8f3a9c2d4e...
branch: release/2.8
dirty: false
buildTime: 2026-07-07T10:22:31Z

Kalau artifact tidak menyimpan commit SHA, debugging production berubah menjadi tebak-tebakan.


Invariant 3 — Perubahan setelah RC harus eksplisit

Setelah RC dibuat, perubahan tidak boleh “sekadar merge apa yang belum masuk”.

Setiap perubahan setelah RC harus punya alasan:

  • blocker bug,
  • security fix,
  • release infrastructure fix,
  • documentation/release notes correction,
  • test-only stabilization,
  • rollback/revert dari change berisiko.

Invariant 4 — Final release harus bisa diturunkan dari kandidat yang lulus

Idealnya:

v2.8.0-rc.3 == v2.8.0

Secara Git, final tag bisa menunjuk commit yang sama dengan RC terakhir.

rc_commit=$(git rev-parse v2.8.0-rc.3^{commit})
git tag -a v2.8.0 "$rc_commit" -m "Release v2.8.0"

Jika final release punya commit tambahan dibanding RC terakhir, berarti kandidat terakhir sebenarnya belum diuji sebagai final.


4. Naming Convention

Naming bukan kosmetik. Naming adalah query interface untuk manusia dan tooling.

Branch

release/2.8
release/2.8.0
release/2026.07
support/2.7
hotfix/2.8.1/payment-timeout

Gunakan branch untuk line of development yang masih bergerak.

RC Tag

v2.8.0-rc.1
v2.8.0-rc.2
v2.8.0-rc.3

Gunakan tag untuk candidate identity.

Final Tag

v2.8.0
v2.8.1

Gunakan annotated tag untuk release final. Untuk organisasi dengan supply-chain requirement, gunakan signed tag.


5. Topology Dasar RC Branch

Interpretasi:

  • main terus berjalan.
  • release/2.8 menjadi stabilization line.
  • RC tag menandai kandidat yang diuji.
  • Fix setelah RC masuk ke release branch secara terkontrol.
  • Final tag menunjuk commit yang sudah lulus.

6. Dua Model Umum RC Flow

Ada dua model yang sering dipakai.

Model A — Release Branch Stabilization

Cocok untuk:

  • produk dengan QA cycle eksplisit,
  • release train,
  • enterprise product,
  • regulated system,
  • beberapa supported version aktif,
  • hotfix/backport rutin.

Flow:

main -> cut release branch -> rc tag -> stabilization -> final tag

Kelebihan:

  • stabilisasi tidak mengganggu main,
  • fix release bisa dikontrol,
  • maintenance branch natural,
  • audit mudah.

Kekurangan:

  • risiko drift dari main,
  • perlu forward-port fix,
  • butuh disiplin cherry-pick/backport.

Model B — Trunk-Based RC Tagging

Cocok untuk:

  • trunk-based development matang,
  • feature flag kuat,
  • automated test kuat,
  • deploy frequent,
  • branch lifetime sangat pendek.

Flow:

main -> rc tag -> validate -> final tag same commit

Kelebihan:

  • tidak ada release branch drift,
  • sederhana,
  • cocok untuk continuous delivery.

Kekurangan:

  • butuh main selalu releasable,
  • fix setelah RC berarti RC baru dari main atau revert/flag adjustment,
  • sulit jika QA cycle panjang dan main terus berubah.

7. Cutting Release Branch

Misal rilis 2.8.0 akan dipotong dari main.

Pertama, pastikan local state bersih.

git status --short
git fetch origin --prune --tags

Pastikan base commit eksplisit.

git switch main
git pull --ff-only origin main
BASE=$(git rev-parse HEAD)
echo "$BASE"

Buat release branch.

git switch -c release/2.8 "$BASE"
git push -u origin release/2.8

Catat evidence:

Release line: release/2.8
Cut from: main
Cut commit: <BASE_SHA>
Cut time: 2026-07-07T10:00:00+07:00
Scope boundary: commits reachable from <BASE_SHA>

8. Preparing RC Commit

Kadang RC butuh commit persiapan:

  • bump version,
  • update changelog,
  • update migration manifest,
  • freeze dependency lockfile,
  • update release notes,
  • set config default.

Contoh:

./scripts/set-version 2.8.0-rc.1
./scripts/generate-release-notes --from v2.7.0 --to HEAD

git add VERSION CHANGELOG.md release-notes/2.8.0.md
git commit -m "Prepare release candidate v2.8.0-rc.1"

Jangan campur RC prep dengan business fix. RC prep adalah metadata change.


9. Tagging RC

Buat annotated tag.

git tag -a v2.8.0-rc.1 -m "Release candidate v2.8.0-rc.1"
git push origin release/2.8
git push origin v2.8.0-rc.1

Untuk signed tag:

git tag -s v2.8.0-rc.1 -m "Release candidate v2.8.0-rc.1"
git tag -v v2.8.0-rc.1

Verify identity:

git rev-parse v2.8.0-rc.1^{commit}
git show --no-patch --decorate v2.8.0-rc.1

10. Build from Tag, Not Branch

CI/CD harus build dari tag atau commit SHA, bukan mutable branch.

Baik:

git checkout --detach v2.8.0-rc.1
./gradlew clean build

Lebih eksplisit:

COMMIT=$(git rev-parse v2.8.0-rc.1^{commit})
git checkout --detach "$COMMIT"
./gradlew clean build

Buruk:

git checkout release/2.8
./gradlew build

Kenapa buruk? Karena branch bisa bergerak di antara “candidate created” dan “artifact built”.


11. RC Evidence Bundle

Setiap RC sebaiknya menghasilkan evidence bundle.

release: 2.8.0
candidate: v2.8.0-rc.1
commit: 8f3a9c2d4e...
sourceRef: refs/tags/v2.8.0-rc.1
artifact:
  name: case-platform-api-2.8.0-rc.1.jar
  sha256: <artifact_digest>
ci:
  pipeline: <url/id>
  result: passed
qualityGates:
  unit: passed
  integration: passed
  e2e: passed
  securityScan: passed
  migrationDryRun: passed
approvals:
  engineering: approved
  qa: pending
  security: pending

Evidence bundle ini bisa berupa file di artifact repository, release management system, atau audit record.


12. Validating RC

Validasi RC harus dibedakan dari validasi branch.

Validasi branch menjawab:

Apakah branch saat ini sehat?

Validasi RC menjawab:

Apakah commit kandidat X layak menjadi release Y?

Minimal gate:

GateTujuan
Unit testlocal correctness
Integration testservice/database contract
E2E testcritical journey
Migration dry-runschema/data safety
Security scanvulnerability/secrets/license
Performance smokeregression kasar
Rollback rehearsaldeploy/revert safety
Release notes reviewuser-facing accuracy

13. RC Rejection

RC rejection harus menghasilkan decision record.

Contoh:

Candidate: v2.8.0-rc.1
Decision: rejected
Reason: REG-4812 approval escalation fails for delegated reviewer
Severity: release blocker
Fix source: main commit 12ab9fe
Fix strategy: cherry-pick to release/2.8
Approver: release captain + domain owner
Next candidate: v2.8.0-rc.2

Jangan hanya menulis “RC failed”. Tulis apa yang gagal dan bagaimana akan diperbaiki.


14. Applying Fix After RC

Ada tiga sumber fix setelah RC:

  1. fix dibuat langsung di release branch,
  2. fix sudah ada di main, lalu di-cherry-pick,
  3. change berisiko di-revert dari release branch.

14.1 Fix langsung di release branch

Cocok jika bug hanya relevan untuk release line.

git switch release/2.8
# edit
git add .
git commit -m "Fix approval escalation in release 2.8"

Wajib forward-port ke main jika bug juga relevan di main.


14.2 Cherry-pick dari main

Cocok jika fix sudah masuk main.

git switch release/2.8
git fetch origin
git cherry-pick -x 12ab9fe

-x menambahkan metadata asal commit. Ini berguna untuk audit dan duplicate detection.


14.3 Revert dari release branch

Cocok jika RC gagal karena change yang harus dikeluarkan dari release.

git switch release/2.8
git revert <bad_commit>

Jika bad commit adalah merge commit, butuh parent selection.

git revert -m 1 <bad_merge_commit>

Gunakan dengan sangat hati-hati. Revert merge punya konsekuensi jangka panjang terhadap integrasi ulang.


15. Creating RC2

Setelah fix masuk:

./scripts/set-version 2.8.0-rc.2
git add VERSION
git commit -m "Prepare release candidate v2.8.0-rc.2"

git tag -a v2.8.0-rc.2 -m "Release candidate v2.8.0-rc.2"
git push origin release/2.8
git push origin v2.8.0-rc.2

Bandingkan RC1 ke RC2:

git log --oneline v2.8.0-rc.1..v2.8.0-rc.2
git diff --stat v2.8.0-rc.1 v2.8.0-rc.2
git diff --name-status v2.8.0-rc.1 v2.8.0-rc.2

Release captain harus bisa menjelaskan setiap baris dalam diff RC1→RC2.


16. RC Delta Budget

Setelah RC pertama, perubahan harus makin kecil.

Rule of thumb:

RC1: full release scope
RC2: blocker fixes only
RC3: blocker fixes or revert only
RC4+: release leadership review required

Jika RC delta terus besar, itu sinyal bahwa release dipotong terlalu awal atau quality gate di main lemah.


17. Promoting RC to Final Release

Jika v2.8.0-rc.3 lulus, final tag sebaiknya menunjuk commit yang sama.

RC=v2.8.0-rc.3
FINAL=v2.8.0
COMMIT=$(git rev-parse "$RC^{commit}")

git tag -a "$FINAL" "$COMMIT" -m "Release $FINAL"
git push origin "$FINAL"

Verify:

test "$(git rev-parse v2.8.0-rc.3^{commit})" = "$(git rev-parse v2.8.0^{commit})"

Jika hasilnya beda, release final bukan RC yang lulus.


18. Mermaid: RC Promotion Topology

Catatan: Mermaid menampilkan tag final di commit berikut dalam contoh visual tertentu, tetapi konsep yang harus dijaga adalah final tag menunjuk commit kandidat yang lulus, bukan branch yang bergerak sesudahnya.


19. Release Notes dari RC Boundary

Release notes harus dibuat dari range yang jelas.

Untuk release minor:

git log --first-parent --oneline v2.7.0..v2.8.0

Untuk delta antar RC:

git log --oneline v2.8.0-rc.2..v2.8.0-rc.3

Untuk changed files:

git diff --name-status v2.7.0 v2.8.0

Untuk risky areas:

git log --oneline -- db/migrations config security policy

Release notes bukan dump commit log. Release notes adalah human-facing summary yang traceable ke commit boundary.


20. RC Flow untuk Regulated Systems

Dalam sistem regulasi, compliance, enforcement, atau case management, RC flow harus menjawab:

  • rule engine version mana yang dirilis?
  • permission matrix mana yang berubah?
  • workflow state transition mana yang berubah?
  • escalation deadline logic mana yang berubah?
  • data migration mana yang dijalankan?
  • evidence artifact mana yang mendukung signoff?
  • siapa approve perubahan setelah freeze?

Tambahkan metadata:

Regulatory impact:
  workflow: Enforcement Lifecycle v4
  affected states:
    - Investigation.Open
    - Enforcement.PendingApproval
  affected roles:
    - CaseSupervisor
    - DelegatedReviewer
  migration: MIG-2026-07-approval-escalation.sql
  rollback: documented
  auditEvidence: attached

Git commit/tag memberi identity. Proses release memberi accountability.


21. Failure Modes

Failure Mode 1 — RC dibuild dari branch

Gejala:

QA lulus untuk artifact A, production deploy artifact B.

Penyebab:

Build job checkout release/2.8, branch bergerak setelah RC dibuat.

Solusi:

Build dari tag atau commit SHA.

Failure Mode 2 — RC tag dipindahkan

Gejala:

v2.8.0-rc.1 hari ini beda dengan v2.8.0-rc.1 kemarin.

Konsekuensi:

  • audit rusak,
  • artifact provenance rusak,
  • QA evidence ambigu,
  • dependency consumer bisa mengambil commit berbeda.

Solusi:

  • jangan pindahkan tag public,
  • buat v2.8.0-rc.2,
  • protect tag pattern v*,
  • audit remote tag mutation.

Failure Mode 3 — Fix masuk RC tanpa forward-port

Gejala:

Bug fixed in release/2.8 but appears again in 2.9.

Penyebab:

Fix hanya dibuat di release branch.

Solusi:

Track every release-branch-only fix with forward-port issue.

Failure Mode 4 — RC delta terlalu besar

Gejala:

RC1 → RC2 contains 40 commits.

Interpretasi:

  • freeze tidak nyata,
  • quality gate sebelum cut lemah,
  • scope belum stabil,
  • branch dipakai sebagai development branch terselubung.

Solusi:

  • reject RC process,
  • recut release,
  • move unready features behind flags,
  • restore strict blocker-only policy.

Failure Mode 5 — Final tag bukan RC yang lulus

Gejala:

git rev-parse v2.8.0-rc.3^{commit}
git rev-parse v2.8.0^{commit}
# different

Solusi:

  • treat final as unvalidated candidate,
  • rerun gates,
  • create new RC if necessary,
  • do not silently publish.

22. RC Branch Protection

Release branch protection minimum:

release/*:
  - require pull request
  - require status checks
  - require release captain review
  - require domain owner for risky paths
  - block force push
  - block deletion
  - require signed commits/tags where applicable

Protected tag pattern:

v*

Atau lebih spesifik:

v[0-9]*
v*-rc.*

Tujuan: mencegah mutation diam-diam pada release identity.


23. Command Playbook

Cut branch

git fetch origin --prune --tags
git switch main
git pull --ff-only origin main
git switch -c release/2.8
git push -u origin release/2.8

Create RC1

./scripts/set-version 2.8.0-rc.1
git add VERSION CHANGELOG.md
git commit -m "Prepare release candidate v2.8.0-rc.1"
git tag -a v2.8.0-rc.1 -m "Release candidate v2.8.0-rc.1"
git push origin release/2.8 v2.8.0-rc.1

Apply blocker fix from main

git switch release/2.8
git cherry-pick -x <fix_commit>

Create RC2

./scripts/set-version 2.8.0-rc.2
git add VERSION
git commit -m "Prepare release candidate v2.8.0-rc.2"
git tag -a v2.8.0-rc.2 -m "Release candidate v2.8.0-rc.2"
git push origin release/2.8 v2.8.0-rc.2

Promote to final

COMMIT=$(git rev-parse v2.8.0-rc.2^{commit})
git tag -a v2.8.0 "$COMMIT" -m "Release v2.8.0"
git push origin v2.8.0

24. RC Checklist

Before RC cut:

  • target version chosen,
  • release scope frozen,
  • target commit recorded,
  • open blockers reviewed,
  • migration strategy reviewed,
  • feature flags checked,
  • dependency lockfile stable,
  • release branch protection enabled.

Before RC tag:

  • working tree clean,
  • release branch up to date,
  • version bump committed,
  • changelog/release notes generated,
  • tag created as annotated/signed tag,
  • tag pushed once,
  • artifact built from tag/commit.

Before final release:

  • final tag points to accepted RC commit,
  • artifact digest recorded,
  • test gates passed,
  • approval evidence stored,
  • rollback plan documented,
  • release notes finalized,
  • runtime version metadata verified.

25. Lab — Simulate RC Flow Locally

Create a toy repo.

mkdir rc-lab
cd rc-lab
git init

echo "v1" > app.txt
git add app.txt
git commit -m "Initial app"

echo "feature" >> app.txt
git commit -am "Add feature"

git switch -c release/1.0

echo "1.0.0-rc.1" > VERSION
git add VERSION
git commit -m "Prepare release candidate v1.0.0-rc.1"
git tag -a v1.0.0-rc.1 -m "Release candidate v1.0.0-rc.1"

Simulate blocker fix.

echo "bugfix" >> app.txt
git commit -am "Fix release blocker"

echo "1.0.0-rc.2" > VERSION
git commit -am "Prepare release candidate v1.0.0-rc.2"
git tag -a v1.0.0-rc.2 -m "Release candidate v1.0.0-rc.2"

Promote RC2.

COMMIT=$(git rev-parse v1.0.0-rc.2^{commit})
git tag -a v1.0.0 "$COMMIT" -m "Release v1.0.0"

Verify.

git log --graph --decorate --oneline --all
test "$(git rev-parse v1.0.0-rc.2^{commit})" = "$(git rev-parse v1.0.0^{commit})"

26. Key Takeaways

  • RC is a commit under evaluation, not a moving branch.
  • Branch is stabilization line; tag is candidate identity.
  • Build from tag or commit SHA, not from mutable branch name.
  • Every RC rejection should produce a clear decision record.
  • Every post-RC change must be intentional, approved, and explainable.
  • Final release should point to the accepted RC commit whenever possible.
  • RC flow is not bureaucracy. It is a control system for release identity.

References

Lesson Recap

You just completed lesson 55 in build core. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.