Skip to main content
BlogDevOps

Trunk-Based Development Berbasis Feature Flag

Bagaimana feature flag membuat trunk-based development aman — memisahkan deploy dari release, branch berumur pendek, kill switch, dan kebersihan flag yang menjaga semuanya tidak membusuk.

Matthews Wong

Software Engineer

Juli 20269 menit baca
Trunk-Based Development Berbasis Feature Flag

Foto oleh Hannes Grobe via Wikimedia Commons (CC BY-SA 4.0)

Ringkasan Utama

Trunk-based development berarti semua orang commit ke satu branch utama lewat branch berumur pendek yang di-merge dalam sehari-dua hari. Feature flag membuatnya aman: memisahkan deploy dari release, sehingga kode setengah jadi tetap bisa dikirim tersembunyi di balik flag. Flag juga menjadi kill switch. Tantangannya adalah kebersihan — setiap flag butuh pemilik, tanggal kedaluwarsa, dan tiket pembersihan.

Branch fitur berumur panjang adalah tempat kecepatan mati. Anda membuat branch dari main, bekerja tiga minggu, dan saat Anda membuka pull request trunk sudah bergerak seribu commit di depan Anda. Merge-nya jadi pertarungan berdarah. Saya pernah kehilangan sore penuh gara-gara konflik yang muncul hanya karena dua branch terpisah terlalu lama — kode yang masing-masing benar tapi secara kolektif tidak kompatibel.

Trunk-based development membalik model itu: integrasikan terus-menerus, dalam kenaikan kecil, agar konflik tidak pernah tumbuh lebih dari beberapa baris. Tapi itu memunculkan pertanyaan jelas — kalau saya merge pekerjaan yang belum selesai ke main setiap hari, bagaimana saya mengirim main ke produksi tanpa membuka fitur setengah jadi? Jawabannya adalah feature flag, dan disiplin di sekitarnya yang membedakan tim yang menyukai trunk-based development dari tim yang diam-diam meninggalkannya.

Deploy bukan release, dan menyamakan keduanya adalah bug-nya

Ide paling berguna di sini adalah memisahkan dua peristiwa yang biasanya dicampur orang. Deploy adalah tindakan teknis: biner baru berjalan di server Anda. Release adalah tindakan produk: pengguna nyata sekarang bisa melihat fitur itu. Pada workflow branch tradisional keduanya terjadi pada saat yang sama, itulah kenapa deploy terasa menakutkan — setiap push ke produksi juga sebuah peluncuran. Feature flag memisahkan keduanya. Anda men-deploy kode puluhan kali sehari, gelap, sementara release adalah keputusan terpisah yang dibuat kemudian dengan membalik flag, sering oleh product manager, bukan engineer.

Begitu deploy dan release terpisah, seluruh profil risiko berubah. Deploy yang hanya mengirim kode tidur tidak bisa merusak fitur yang dilihat pengguna, karena tidak ada kode itu yang berjalan. Rollback berhenti berarti revert dan deploy ulang; ia berarti matikan flag — perubahan konfigurasi yang menyebar dalam hitungan detik alih-alih siklus build-and-ship yang butuh menit. Perbedaan kecepatan itulah inti keseluruhannya.

// A release toggle wrapping an incomplete feature.
// This code is on main and deployed to prod — but dark.
if (await flags.isEnabled('checkout-v2', { userId: user.id })) {
  return renderCheckoutV2(cart);
}
return renderCheckoutLegacy(cart); // still the default for everyone

Branch berumur pendek, pull request kecil

Bagian branch dari trunk-based development lebih sederhana dari yang ditakutkan orang. Anda tetap memakai branch — Anda cuma menjaganya hidup selama beberapa jam atau sehari, bukan berminggu-minggu. Buat branch, lakukan perubahan yang fokus, buka pull request kecil, minta review, merge. Trik yang membuat ini berhasil untuk fitur besar adalah Anda tidak menunggu seluruh fitur selesai. Anda merge satu irisan vertikal di balik flag yang mati, lalu irisan berikutnya, lalu berikutnya. Main selalu bisa di-build, selalu lulus tes, selalu bisa di-deploy — fiturnya cuma tidak terlihat sampai flag mengizinkan.

  • Jaga branch berumur di bawah sehari — semakin lama branch hidup, semakin ia menyimpang dari trunk dan semakin buruk merge akhirnya.
  • Merge di balik flag yang mati alih-alih menunggu fitur lengkap; kode tak selesai di main tidak masalah jika tidak ada jalur yang mencapainya.
  • Wajibkan branch main tetap bisa dirilis di setiap commit — trunk merah memblokir seluruh tim, jadi lindungi dengan gate CI.
  • Utamakan banyak pull request kecil daripada satu raksasa; reviewer menangkap lebih banyak di lima puluh baris daripada di lima ratus.

Pasangkan branch berumur pendek dengan merge queue. Ia me-rebase dan menguji setiap pull request terhadap tip terbaru main sebelum merge, sehingga Anda tidak pernah mendaratkan perubahan yang lulus CI melawan trunk basi tapi rusak melawan trunk hari ini. Inilah yang menjaga main tetap hijau saat belasan orang berintegrasi setiap hari.

Tidak semua flag sama — kenali empat jenisnya

Kesalahan umum adalah memperlakukan setiap flag secara identik. Taksonomi yang dipopulerkan Pete Hodgson dan diadopsi luas di industri membagi flag berdasarkan berapa lama seharusnya ia hidup dan seberapa dinamis ia perlu. Release toggle ada hanya untuk menyembunyikan pekerjaan tak selesai dan harus mati dalam hitungan hari. Operational toggle — kill switch — mungkin hidup berbulan-bulan sebagai katup pengaman. Mencampur model mental ini membuat Anda berakhir dengan release toggle berumur lima tahun yang tak ada yang berani menghapus.

Jenis flagTujuanUmur
Release toggleSembunyikan pekerjaan tak selesai yang di-merge ke trunkHari hingga minggu
Operational (kill switch)Nonaktifkan subsistem cepat saat insidenBulan, berumur panjang
EksperimenUji A/B — arahkan kohort ke varianSelama eksperimen
PermissionBatasi fitur per paket, peran, atau hakBerumur panjang, bagian produk

Kill switch: flag yang membayar keberadaannya

Operational toggle adalah yang akan saya perjuangkan untuk dipertahankan bahkan di basis kode yang alergi flag. Bungkus dependensi berisiko apa pun — penyedia pembayaran baru, query laporan mahal, API pihak ketiga yang tidak sepenuhnya Anda percaya — dalam flag yang default menyala tapi bisa dimatikan seketika. Saat dependensi itu mulai timeout jam 2 pagi, Anda tidak membangunkan engineer untuk menulis revert. On-call membalik switch, sistem melepas jalur yang gagal, dan Anda debug pagi harinya. Kill switch mengubah insiden panggil-orang-sekarang menjadi lihat-nanti.

Flag adalah percabangan runtime, artinya kedua sisi harus benar-benar berfungsi. Jalur mati yang tak teruji adalah jebakan klasik: kill switch yang Anda balik saat insiden mengarahkan trafik ke kode yang belum berjalan di produksi berbulan-bulan. Uji kedua keadaan di CI, dan latih kill switch Anda secara terjadwal agar Anda tahu ia masih berfungsi sebelum Anda membutuhkannya.

Kebersihan flag, atau bagaimana teknik ini jadi buruk

Setiap feature flag adalah sebuah kondisional, dan setiap kondisional yang tak pernah Anda hapus adalah utang teknis permanen. Basis kode dengan ratusan flag basi menjadi mimpi buruk kombinatorial: jumlah keadaan yang mungkin berlipat ganda dengan setiap flag, dan tidak ada suite tes yang mencakup semuanya. LaunchDarkly dan Unleash keduanya membangun seluruh permukaan produk seputar siklus hidup flag justru karena tim buruk dalam pembersihan. Perbaikannya bukan kepahlawanan — ia memperlakukan penghapusan sebagai bagian dari menyelesaikan fitur, bukan proyek suatu-hari-nanti.

  1. Saat pembuatan, beri setiap flag pemilik dan tanggal kedaluwarsa — flag tanpa pemilik adalah flag yang tak akan pernah dihapus siapa pun.
  2. Begitu release toggle mencapai rollout 100 persen dan tetap stabil, buka tiket pembersihan segera — targetkan menghapusnya dalam sekitar 30 hari.
  3. Hapus baik definisi flag maupun cabang kode mati yang dijaganya — flag yang dihapus dari dashboard tapi ditinggal di kode lebih buruk, bukan lebih baik.
  4. Jalankan audit flag triwulanan dan biarkan CI gagal atau memperingatkan pada flag yang lewat kedaluwarsa agar utang tidak menumpuk diam-diam.

Di sisi tooling, OpenFeature layak diketahui. Ia adalah proyek incubating CNCF — spesifikasi netral-vendor dan sekumpulan SDK yang menaruh API standar di depan backend flag apa pun yang Anda pakai, entah itu LaunchDarkly, Unleash, GO Feature Flag, atau penyimpanan self-hosted. Anda menulis kode terhadap antarmuka OpenFeature sekali, dan mengganti penyedia nanti menjadi perubahan konfigurasi alih-alih penulisan ulang. Untuk setup self-hosted, portabilitas itu persis jenis asuransi anti-lock-in yang saya inginkan.

Adopsi OpenFeature bahkan jika Anda mulai dengan satu penyedia. Menulis kode terhadap API netral-vendor sejak hari pertama hampir tidak berbiaya dan memberi Anda kebebasan bermigrasi backend flag kemudian tanpa menyentuh logika bisnis — yang penting saat cerita harga atau self-hosting hari ini berubah.

Dari mana saya akan mulai besok

Anda tidak butuh platform untuk memulai. Mulai dengan satu release toggle pada fitur berikutnya yang jika tidak akan duduk di branch panjang — boolean yang dibaca dari config sudah cukup untuk membuktikan workflow-nya. Tambahkan kill switch ke dependensi paling berisiko Anda. Tuliskan pemilik dan kedaluwarsa di deskripsi pull request. Begitu fitur sepenuhnya live, hapus flag di sprint yang sama. Lakukan itu tiga atau empat kali dan trunk-based development berhenti menjadi metodologi menakutkan dan menjadi sekadar cara Anda bekerja: main selalu hijau, deploy membosankan, dan release adalah keputusan yang Anda buat dengan sengaja.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa perbedaan antara deploy dan release dalam trunk-based development?

Deploy adalah peristiwa teknis — kode baru berjalan di server Anda. Release adalah peristiwa produk — pengguna nyata sekarang bisa melihat fitur. Feature flag memisahkan keduanya sehingga Anda bisa men-deploy kode gelap berkali-kali sehari dan merilis kemudian dengan membalik flag, sering sebagai keputusan produk alih-alih rekayasa.

Bagaimana feature flag memungkinkan trunk-based development?

Mereka membuat Anda bisa merge pekerjaan tak selesai ke branch utama dengan aman. Jalur kode tak lengkap dibungkus dalam flag yang dimatikan, sehingga main selalu bisa di-build dan di-deploy sementara fitur tetap tersembunyi. Ini berarti Anda bisa berintegrasi dalam kenaikan harian kecil alih-alih memelihara branch fitur berumur panjang yang menyimpang dari trunk.

Apa itu feature flag kill switch?

Kill switch adalah operational toggle yang membungkus dependensi berisiko — penyedia pembayaran, query berat, API pihak ketiga tak terpercaya — yang default menyala tapi bisa dimatikan seketika. Saat insiden, on-call menonaktifkan jalur yang gagal dalam hitungan detik alih-alih menulis dan men-deploy revert kode, mengubah keadaan darurat menjadi perbaikan berprioritas rendah.

Bagaimana cara membersihkan feature flag basi?

Beri setiap flag pemilik dan kedaluwarsa saat pembuatan. Begitu release toggle mencapai rollout 100 persen dan stabil, buka tiket pembersihan dan targetkan menghapusnya dalam sekitar 30 hari, menghapus baik definisi flag maupun cabang kode mati. Jalankan audit triwulanan dan biarkan CI memperingatkan pada flag kedaluwarsa agar utang tidak menumpuk diam-diam.

Apa itu OpenFeature dan haruskah saya memakainya?

OpenFeature adalah proyek incubating CNCF yang menyediakan spesifikasi netral-vendor dan SDK, memberi Anda API standar di depan backend flag apa pun seperti LaunchDarkly, Unleash, atau penyimpanan self-hosted. Menulis kode terhadapnya sejak hari pertama hampir tidak berbiaya dan membuat Anda bisa mengganti penyedia kemudian sebagai perubahan konfigurasi alih-alih penulisan ulang, menghindari lock-in di tingkat kode.