Changesets adalah salah satu cara paling praktis untuk membuat proses release di monorepo JavaScript tetap rapi, terutama ketika satu perubahan bisa memengaruhi beberapa package sekaligus. Alih-alih menaikkan versi secara manual, menulis changelog sendiri, lalu menebak urutan publish, tim bisa mendeklarasikan perubahan sejak pull request dibuat dan membiarkan proses versioning mengikuti metadata tersebut.

Untuk monorepo kecil sampai menengah, pendekatan ini sangat membantu saat Anda perlu menjawab pertanyaan seperti: package mana yang berubah?, apakah dampaknya patch, minor, atau major?, dependensi internal perlu ikut naik versi atau tidak?, dan bagaimana memastikan review release terjadi sebelum publish?. Artikel ini membahas alur praktis penggunaan Changesets, kapan ia lebih cocok dibanding release manual, serta contoh integrasi dasar ke GitHub Actions.

Mengapa Changesets cocok untuk monorepo JavaScript

Masalah utama di monorepo bukan sekadar banyak package, tetapi keterkaitan antar-package. Misalnya, perubahan di @acme/ui dapat memaksa @acme/web atau @acme/utils ikut diperbarui. Jika proses release dilakukan manual, risiko yang sering muncul adalah:

  • Versi package tidak sinkron dengan perubahan nyata.
  • Changelog terlambat ditulis atau terlalu umum.
  • Pull request berisi perubahan penting, tetapi dampak semver tidak pernah dibahas.
  • Package internal yang bergantung pada package lain tertinggal versinya.
  • Publish dilakukan dari mesin lokal tanpa jejak review yang jelas.

Changesets membantu dengan cara memindahkan keputusan release ke tahap pengembangan. Setiap perubahan penting dapat disertai file changeset kecil yang menjelaskan:

  • package mana yang terdampak,
  • kenaikan versi yang diinginkan,
  • ringkasan perubahan untuk changelog.

Saat waktunya rilis, Changesets akan menghitung versi baru, memperbarui package.json, menulis changelog, dan menyiapkan publish. Pendekatan ini lebih cocok dibanding release manual jika tim Anda:

  • mengelola lebih dari satu package dalam satu repo,
  • sering punya perubahan lintas package,
  • ingin review dampak release di pull request,
  • ingin mengurangi langkah manual saat publish.

Jika repo Anda hanya memiliki satu package dan release sangat jarang, proses manual mungkin masih cukup. Namun begitu ada beberapa package publik atau kombinasi package private/public, Changesets biasanya mulai terasa manfaatnya.

Struktur monorepo sederhana

Struktur berikut cukup umum untuk monorepo JavaScript/TypeScript:

repo/
  package.json
  pnpm-workspace.yaml
  .changeset/
  packages/
    ui/
      package.json
      src/
    utils/
      package.json
      src/
  apps/
    web/
      package.json
      src/

Contoh isi package.json root:

{
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "scripts": {
    "build": "turbo run build",
    "test": "turbo run test",
    "changeset": "changeset",
    "version-packages": "changeset version",
    "release": "changeset publish"
  },
  "devDependencies": {
    "@changesets/cli": "^2.0.0"
  }
}

Anda tidak harus memakai turbo. Intinya, monorepo memiliki workspace dan setiap package memiliki name, version, serta dependensi internal yang jelas.

Jika root package diset private: true, itu normal dan aman. Yang penting adalah status private/public pada masing-masing package yang akan atau tidak akan dipublish.

Instalasi dan inisialisasi Changesets

1. Instal CLI

npm install -D @changesets/cli

Atau gunakan package manager yang dipakai tim Anda. Yang penting, Changesets tersedia di root monorepo.

2. Inisialisasi

npx changeset init

Perintah ini biasanya membuat folder .changeset/ beserta file konfigurasi dasar. Setelah itu, struktur repo akan memiliki area khusus untuk menyimpan metadata release.

3. Periksa package yang terdeteksi

Pastikan semua workspace yang relevan benar-benar terbaca sebagai package. Jika ada package yang tidak terdeteksi, biasanya penyebabnya:

  • konfigurasi workspace salah,
  • package.json belum valid,
  • lokasi package tidak cocok dengan pola workspace.

Sebelum melangkah lebih jauh, cek juga mana package yang memang publik dan mana yang private. Package private tidak seharusnya ikut dipublish ke registry eksternal.

Konsep inti: changeset sebagai dokumen perubahan

Changeset adalah file markdown kecil yang menjelaskan dampak perubahan. File ini biasanya dibuat saat mengerjakan fitur atau perbaikan, lalu ikut direview dalam pull request.

Membuat changeset

npx changeset

CLI akan menanyakan package yang berubah, tipe bump versi, dan ringkasan perubahan.

Contoh hasil file di folder .changeset/:

---
"@acme/ui": minor
"@acme/utils": patch
---

Menambahkan komponen Modal baru di @acme/ui dan memperbaiki helper format tanggal di @acme/utils.

Arti file di atas:

  • @acme/ui naik minor karena ada fitur baru yang kompatibel dengan versi sebelumnya.
  • @acme/utils naik patch karena ada perbaikan tanpa perubahan API yang merusak.

Bagaimana menentukan semver dengan benar

Kesalahan paling umum dalam adopsi Changesets bukan pada tool-nya, tetapi pada penentuan semantic versioning:

  • patch: bug fix, perubahan internal, atau perbaikan kecil yang tidak mengubah kontrak publik.
  • minor: fitur baru yang tetap kompatibel.
  • major: perubahan yang memutus kompatibilitas API atau perilaku yang diharapkan pengguna.

Untuk package internal, tim sering tergoda memberi patch untuk semua hal. Itu berbahaya jika package tersebut dipakai luas oleh package lain atau aplikasi eksternal. Gunakan aturan sederhana: jika pengguna package perlu mengubah kode mereka, kemungkinan besar itu major.

Kasus perubahan lintas package

Misalnya @acme/utils mengubah nama fungsi publik. Dampaknya:

  • @acme/utils perlu bump major.
  • Package lain yang bergantung padanya mungkin perlu diperbarui dependensinya.
  • Jika package lain ikut berubah karena penyesuaian API, mereka bisa mendapatkan changeset juga, tergantung apakah perilaku publiknya ikut berubah.

Di sinilah Changesets membantu: relasi antar-package dianalisis saat proses versioning, sehingga versi dependensi internal dapat disesuaikan secara konsisten.

Alur kerja tim yang rapi dengan Changesets

1. Setiap perubahan yang layak dirilis harus punya changeset

Praktik paling aman adalah menganggap changeset sebagai bagian dari definisi selesai untuk perubahan yang memengaruhi package publik atau package internal penting. Pull request tanpa changeset boleh ditolak jika memang ada dampak release.

Pengecualian yang masuk akal:

  • perubahan dokumentasi,
  • refactor internal murni tanpa dampak publik,
  • perubahan CI atau tooling yang tidak mengubah package hasil publish.

2. Review changeset, bukan hanya kode

Saat review pull request, periksa dua hal:

  1. Apakah package yang dipilih sudah benar?
  2. Apakah level semver masuk akal?

Ini penting karena file changeset pada dasarnya adalah kontrak release. Sering kali bug release muncul bukan karena kodenya salah, tetapi karena perubahan besar dirilis sebagai patch.

3. Gabungkan pull request ke branch utama setelah lolos review

Perubahan yang sudah memiliki changeset bisa dikumpulkan di branch utama sampai waktu release. Ketika siap merilis, Changesets akan menggabungkan seluruh metadata perubahan yang sudah masuk dan menghasilkan versi baru yang konsisten.

4. Jalankan versioning di branch release atau via pull request otomatis

Alur yang umum:

  • developer menambahkan changeset di feature branch,
  • pull request direview dan di-merge ke branch utama,
  • otomasi membuat pull request release yang berisi perubahan versi dan changelog,
  • tim mereview hasil release tersebut,
  • setelah merge, proses publish dijalankan.

Alur ini lebih aman daripada publish langsung dari commit biasa karena ada titik kontrol tambahan sebelum artefak benar-benar dirilis.

Perintah inti: version dan publish

changeset version

Perintah ini membaca semua file changeset yang belum diproses, lalu:

  • menaikkan versi package yang terdampak,
  • memperbarui dependensi internal jika diperlukan,
  • menghasilkan atau memperbarui changelog.
npx changeset version

Setelah dijalankan, Anda akan melihat perubahan di beberapa package.json, mungkin juga CHANGELOG.md, dan file changeset terkait biasanya dianggap sudah dipakai dalam proses release.

changeset publish

Perintah ini mempublish package yang memang perlu dirilis berdasarkan versi yang sudah diperbarui.

npx changeset publish

Sebelum menjalankan publish, pastikan:

  • build sukses,
  • test penting lulus,
  • autentikasi ke registry tersedia di lingkungan CI,
  • hanya package publik yang memang boleh dipublish.

Jangan jadikan mesin lokal sebagai sumber kebenaran untuk publish jika tim Anda bekerja kolaboratif. Publish dari CI lebih mudah diaudit, diulang, dan diamankan.

Contoh perubahan nyata dalam monorepo

Contoh 1: fitur baru di satu package

Anda menambah komponen Tooltip ke @acme/ui. Tidak ada API lama yang rusak. Maka changeset yang masuk akal:

---
"@acme/ui": minor
---

Menambahkan komponen Tooltip untuk kebutuhan antarmuka yang konsisten.

Contoh 2: bug fix lintas dua package

Perbaikan di helper tanggal pada @acme/utils membuat aplikasi web tidak lagi memerlukan workaround. Jika aplikasi internal tidak dipublish, Anda mungkin cukup memberi changeset pada library yang dipublish:

---
"@acme/utils": patch
---

Memperbaiki parsing tanggal pada zona waktu tertentu.

Jika aplikasi juga punya proses release terpisah di monorepo, Anda bisa memutuskan apakah ia perlu versi baru berdasarkan proses internal tim. Tidak semua workspace harus diperlakukan sama seperti package library.

Contoh 3: breaking change pada package utilitas

Anda mengganti nama fungsi publik formatCurrency menjadi formatMoney. Ini adalah breaking change:

---
"@acme/utils": major
---

Mengganti API formatCurrency menjadi formatMoney. Pengguna perlu memperbarui pemanggilan fungsi.

Jika @acme/ui juga menggunakan fungsi lama dan harus disesuaikan, periksa apakah perubahan pada @acme/ui juga berdampak ke pengguna luar. Jika ya, beri changeset yang sesuai pada package itu juga.

Integrasi dasar dengan GitHub Actions

Tujuan integrasi CI biasanya ada dua:

  1. membuat pull request release saat ada changeset yang belum dirilis,
  2. mempublish package setelah pull request release digabungkan.

Contoh workflow dasar:

name: Release

on:
  push:
    branches:
      - main

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build --if-present

      - name: Test
        run: npm test --if-present

      - name: Create release PR or publish
        uses: changesets/action@v1
        with:
          version: npm run version-packages
          publish: npm run release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}

Ada beberapa hal penting dari pola ini:

  • workflow berjalan di branch utama, bukan dari laptop developer,
  • build dan test dijalankan sebelum release/publish,
  • token autentikasi disimpan sebagai secret CI,
  • proses version dan publish dipisahkan secara konseptual tetapi dikelola dalam alur yang sama.

Dalam praktiknya, Anda mungkin ingin menambahkan pembatasan seperti approval wajib pada pull request release, proteksi branch, atau environment khusus untuk publish.

Praktik aman untuk branch utama dan package private/public

Lindungi branch utama

Jika release bergantung pada commit di branch utama, maka branch tersebut harus memiliki proteksi minimum:

  • pull request wajib untuk merge,
  • status check wajib lulus,
  • reviewer minimal satu orang untuk perubahan penting,
  • opsional: larang push langsung.

Ini mengurangi risiko changeset yang salah atau perubahan versi yang tidak lolos validasi.

Bedakan package public dan private

Di monorepo, tidak semua package perlu dipublish. Beberapa package hanya dipakai internal. Pastikan package internal ditandai sesuai kebutuhan tim, dan jangan mengandalkan asumsi bahwa semua workspace boleh dirilis.

Yang perlu diperhatikan:

  • package publik harus punya metadata package yang benar,
  • package private tidak boleh masuk alur publish eksternal tanpa sengaja,
  • dependensi internal dari package publik ke package private perlu dipikirkan matang-matang karena bisa mempersulit distribusi.

Aturan praktisnya: jika sebuah package adalah implementasi internal murni, pertimbangkan tetap menjadikannya private agar batas publiknya jelas.

Jangan campur release eksperimen dengan branch utama

Untuk fitur besar yang belum stabil, jangan terburu-buru menambahkan changeset final ke alur release reguler jika kemungkinan besar API akan berubah lagi sebelum rilis. Lebih aman menahan merge, memakai feature branch yang lebih panjang, atau menyepakati strategi pre-release terpisah bila tim memang membutuhkannya.

Kesalahan umum dan cara debugging

1. Package tidak ikut naik versi padahal berubah

Biasanya penyebabnya:

  • tidak ada changeset untuk package tersebut,
  • perubahan hanya terjadi di kode tetapi tidak dianggap berdampak release,
  • workspace atau nama package tidak sesuai dengan yang diharapkan.

Solusi pertama: periksa file changeset dan nama package yang ditulis di dalamnya.

2. Versi dependensi internal tidak sinkron

Ini sering terjadi jika tim mengubah package.json secara manual sambil juga memakai Changesets. Hindari mengedit versi package hasil publish secara manual kecuali Anda benar-benar tahu implikasinya. Jadikan changeset version sebagai sumber utama untuk pembaruan versi.

3. Changelog terasa tidak berguna

Masalah ini hampir selalu berasal dari isi changeset yang terlalu pendek atau terlalu teknis tanpa konteks. Tulis ringkasan yang menjawab: apa yang berubah dan apa dampaknya bagi pengguna package.

Buruk:

Memperbaiki beberapa hal.

Lebih baik:

Memperbaiki serialisasi tanggal agar konsisten pada input dengan offset zona waktu.

4. Publish gagal di CI

Periksa urutan berikut:

  • apakah autentikasi tersedia sebagai secret,
  • apakah build menghasilkan artefak yang diperlukan,
  • apakah package yang dipublish memang bukan private,
  • apakah workflow berjalan pada branch dan event yang benar.

Jika perlu, pecah langkah CI menjadi instalasi, build, test, version, dan publish agar titik gagalnya lebih jelas.

Kapan Changesets lebih cocok dibanding release manual

Pilih Changesets jika:

  • repo memiliki banyak package atau workspace penting,
  • release perlu jejak review yang jelas,
  • tim ingin changelog dan versioning lebih konsisten,
  • sering ada perubahan lintas package,
  • publish ingin dipindahkan ke CI.

Release manual masih masuk akal jika:

  • hanya ada satu package,
  • release sangat jarang,
  • tim kecil dan tidak punya overhead koordinasi tinggi,
  • belum ada kebutuhan otomasi changelog atau versioning lintas package.

Namun begitu Anda mulai bertanya “package mana saja yang harus ikut naik versi?” atau “siapa yang memutuskan patch/minor/major?”, itu biasanya tanda bahwa release manual mulai tidak cukup.

Checklist adopsi untuk tim kecil-menengah

  • Tentukan workspace/package mana yang benar-benar masuk alur release.
  • Pasang @changesets/cli di root monorepo dan jalankan changeset init.
  • Sepakati aturan semver internal: kapan patch, minor, dan major dipakai.
  • Wajibkan changeset untuk setiap pull request yang memengaruhi package hasil publish.
  • Masukkan review file changeset ke checklist code review.
  • Gunakan branch protection pada branch utama.
  • Jalankan build dan test sebelum version/publish di CI.
  • Publish dari CI, bukan dari mesin lokal developer.
  • Pastikan package private/public dibedakan dengan jelas.
  • Tulis isi changeset yang berguna untuk changelog, bukan catatan internal yang terlalu samar.

Jika diterapkan dengan disiplin, Changesets memberi keseimbangan yang baik antara kontrol manual dan otomasi release. Tim tetap memutuskan dampak perubahan lewat pull request, tetapi eksekusi versioning, changelog, dan publish menjadi lebih konsisten dan jauh lebih mudah diaudit.