Tukar version bump manual, changelog di Confluence, dan release branch dengan Conventional Commits, Git tag, dan satu release PR otomatis.
Alur rilis yang umum terlihat bertanggung jawab: naikkan versi di package.json dengan tangan, tulis changelog di Confluence, merge working branch ke develop, potong sebuah release branch, merge develop ke dalamnya, lalu buka satu PR lagi ke master. Ia selalu berhasil mengirim. Ia juga diam-diam memajakimu setiap kali.
teks1. Naikkan versi package.json dengan tangan 2. Tulis changelog di Confluence 3. Merge working branch → develop (PR) 4. Buat release branch 5. Merge develop → release branch 6. Merge release branch → master (PR) 7. Produksi ter-deploy
Tiap langkah adalah tempat untuk lupa menaikkan versi, untuk membiarkan changelog menjauh dari kode yang sebenarnya berubah, atau untuk menjaga sebuah branch QA beku tetap hidup lama setelah tim berhenti membutuhkannya. Tanpa Git tag, tidak ada penanda tunggal yang jujur tentang apa yang sedang dijalankan produksi.
MASALAH SEBENARNYA Alur lama tidak salah. Ia hanya membungkus seremoni manual di sekitar sesuatu yang bisa dilakukan CI dengan andal dan tak pernah lupa.
Kupas seremoninya jadi empat bagian bergerak dan gambarannya jadi sederhana.
teksGit tag = versi rilis resmi CHANGELOG.md = release notes, hidup di dalam repo Conventional Commit = input yang meng-generate notes itu Release Please = bot yang membuka release PR, menulis changelog, memberi tag, dan memotong GitHub Release
Deployment produksi bergantung pada rilis, bukan pada tiap push ke master. Dan untuk aplikasi privat yang tak pernah dipublikasikan ke npm, versi di package.json itu opsional. Git tag adalah identitas rilis, dan ia terbaca persis seperti yang diharapkan manusia.
teksv1.0.0 v1.1.0 v1.1.1
Ketukan yang sama tetap ada, tapi separuh yang membosankan jadi otomatis.
teksfeature branch ↓ squash merge develop ↓ deploy staging develop → master (merge commit, bukan squash) ↓ Release Please membuka release PR ↓ merge PR-nya Git tag + GitHub Release ↓ deploy produksi
Feature branch di-squash-merge ke develop. Merge dari develop ke master mempertahankan tiap commit-nya. Release Please membaca commit feat: dan fix: bermakna yang mendarat di master, dan produksi hanya ter-deploy setelah sebuah rilis dipotong.
Ini satu keputusan yang menjadi tumpuan seluruh workflow, dan ia mengarah ke dua arah berbeda tergantung kamu di mana.
Feature ke develop: squash and merge. Kamu ingin satu commit bersih per tiket, bukan jejak wip, fix typo, oops yang berisik dari branch. Judul squash tunggal itulah yang akan dikutip changelog, jadi ia harus berupa Conventional Commit.
teks# commit di branch (derau) feat(navbar): flatten Shop All mega-menu feat(shop-all): revamp filters, hero banner & page surfaces feat(home): reorder sections & restore Beauty Impact # judul squash yang mendarat di develop (sinyal) feat(shop-all): revamp homepage, Shop All, and navbar
Develop ke master: merge commit sungguhan. Squash di sini akan meruntuhkan tiap feat dan fix jadi satu chore: merge develop to master yang kabur, dan Release Please tidak akan melihat apa pun yang layak dirilis. Sebuah merge commit mempertahankan semua commit bersih agar bot masih bisa membacanya.
SATU ATURAN Squash branch kecil ke develop. Jangan pernah squash develop ke master.
Release notes hanya sebaik judul commit yang menyuapinya. Kosakata kecil dan membosankan sudah cukup.
teksfeat(scope): fitur atau peningkatan yang dilihat pengguna fix(scope): perbaikan bug yang dilihat pengguna perf(scope): peningkatan performa refactor(scope): penataan ulang internal, perilaku tak berubah chore(scope): tooling atau pemeliharaan docs(scope): dokumentasi saja style(scope): perapian format atau styling
Prefix-nya menentukan kenaikan versi, dan itulah inti dari melewati bump manual.
teksfeat: → rilis minor (1.1.0) fix: → rilis patch (1.1.1) feat! / BREAKING CHANGE: → rilis major (2.0.0) chore / docs / style / refactor: → biasanya tanpa release note
Tulis judul yang bisa dibaca orang asing di sebuah changelog. Yang bagus menggambarkan perubahan produk; yang buruk tidak menggambarkan apa-apa.
teks# bagus feat(shop-all): revamp homepage, Shop All, and navbar fix(cart): prevent duplicate checkout submission perf(product-list): reduce initial render cost # buruk Neo 2260 update stuff fix bug merge develop
JEBAKANNYA Kalau judul squash bukan Conventional Commit, Release Please mengabaikannya. Run Action yang hijau tanpa release PR hampir selalu berarti persis ini.
Satu workflow mengawasi master dan mengerjakan pembukuannya. release-type: simple adalah strategi untuk repo generik yang tidak punya file versi bahasa untuk dirawat, jadi package.json dibiarkan utuh.
.github/workflows/release-please.ymlname: Release Please on: push: branches: - master permissions: contents: write pull-requests: write issues: write jobs: release-please: runs-on: ubuntu-latest steps: - name: Run Release Please id: release uses: googleapis/release-please-action@v4 with: release-type: simple target-branch: master
Pada tiap push ke master, action memindai Conventional Commits sejak tag rilis terakhir, lalu membuka atau memperbarui satu Release Please PR. Di PR itulah CHANGELOG.md ditulis ulang. Merge ia, dan action membuat Git tag dan GitHub Release. Kalau branch produksimu main, ganti kedua baris master jadi main, karena nama branch di config harus cocok dengan kenyataan.
Banyak repo punya riwayat bertahun-tahun dan nol tag rilis. Jangan menulis ulang riwayat dan jangan menambal changelog lama. Beri tag pada branch produksi saat ini sekali saja, dan biarkan otomasi mengambil alih dari sana.
Terminalgit checkout master git pull origin master git tag -a v1.0.0 -m "v1.0.0 baseline release" git push origin v1.0.0
Beri benih CHANGELOG.md dengan satu entri jujur agar file-nya ada dan ceritanya punya titik awal.
md# Changelog ## v1.0.0 Rilis baseline awal. Ini menandai keadaan produksi saat ini sebelum mengadopsi release notes otomatis. Semua setelah titik ini dilacak otomatis.
Dari v1.0.0 dan seterusnya, tiap rilis di-generate dari commit yang datang sesudahnya. Entri changelog yang sudah jadi sengaja tetap pendek, karena ia merangkum perubahan produk alih-alih menggemakan riwayat commit mentah.
md## [1.1.0](https://github.com/org/repo/compare/v1.0.0...v1.1.0) (2026-06-18) ### Features * revamp homepage, Shop All, and navbar
Satu tiket jadi satu baris. Kalau dua perubahan benar-benar independen, itu pertanda keduanya ingin jadi dua PR.
Push ke master seharusnya tidak men-deploy produksi. Sebuah rilis yang dipotonglah yang seharusnya. Pergeseran tunggal itu menjamin tiap deploy produksi punya tag di belakangnya, yang membuat rollback dan audit jadi sepele. Cara paling bersih merangkainya adalah menjaga Release Please dan deploy di workflow yang sama, tiap langkah deploy digerbangi oleh output release_created milik action.
.github/workflows/release-and-deploy.ymlname: Release Please and Production Deploy on: push: branches: - master permissions: contents: write pull-requests: write issues: write jobs: release-and-deploy: runs-on: ubuntu-latest steps: - name: Run Release Please id: release uses: googleapis/release-please-action@v4 with: release-type: simple target-branch: master - name: Checkout release commit if: ${{ steps.release.outputs.release_created }} uses: actions/checkout@v4 - name: Setup Node if: ${{ steps.release.outputs.release_created }} uses: actions/setup-node@v4 with: node-version: 22 cache: yarn - name: Install dependencies if: ${{ steps.release.outputs.release_created }} run: yarn install --frozen-lockfile - name: Build if: ${{ steps.release.outputs.release_created }} run: yarn build - name: Deploy production if: ${{ steps.release.outputs.release_created }} run: echo "Taruh perintah deploy produksi di sini"
release_created hanya true setelah Release Please PR di-merge dan sebuah rilis benar-benar dipotong. Tiap push yang tidak menghasilkan rilis menjalankan langkah pertama lalu berhenti, jadi produksi tak pernah ter-deploy mendahului metadata rilisnya sendiri.
Beberapa sudut tajam muncul di minggu pertama lalu tak pernah lagi setelah kamu tahu.
Run Action yang sukses tidak selalu berarti ada release PR. Kalau satu-satunya commit sejak tag terakhir adalah merge commit atau judul non-konvensional seperti Neo 2260, tidak ada yang bisa dirilis dan bot dengan benar tidak membuka apa-apa. Setelah squash merge, hanya judul squash final yang penting, commit asli di branch sudah hilang.
Pola satu-workflow di atas bukan preferensi gaya, ia sebuah workaround. GitHub Release yang dibuat GITHUB_TOKEN default tidak akan memicu workflow terpisah yang mendengarkan release: published. Menggerbangi deploy pada release_created di dalam satu workflow menghindari itu sepenuhnya.
ATURAN TIM Judul Conventional Commit di tiap PR. Squash ke develop, merge-commit ke master. Jangan pernah mengedit versi aplikasi privat dengan tangan. Beri tag baseline sekali. Deploy saat rilis, bukan saat push. Pecah perubahan tak terkait jadi PR terpisah.
Langkah manual bukan musuhnya; seremoni berulanglah musuhnya. Biarkan engineer menghabiskan penilaian mereka pada satu hal yang hanya bisa mereka putuskan, dan biarkan pipeline menangani versioning, changelog, tag, dan pemicu deploy.
POLA PIKIRNYA Manusia memutuskan kesiapan rilis. Otomasi menangani mekanika rilis.