Feature flag dengan Pennant
Feature flag mengubah rilis dari peristiwa besar jadi sakelar yang bisa dibalik dalam sedetik. Untuk aplikasi yang tidak boleh mati, itu perbedaan antara rollback dua puluh menit dan pemulihan seketika.
Intisari
- Fitur didefinisikan sekali, lalu dicek dengan
Feature::active('nama')di mana pun. - Nilainya bisa bergantung pengguna โ cocok untuk peluncuran bertahap dan uji A/B.
- Hasilnya disimpan (database atau cache), jadi seorang pengguna selalu mendapat pengalaman yang konsisten.
- Mematikan fitur bermasalah tidak butuh deploy.
- Flag punya umur; yang tidak dibersihkan berubah jadi percabangan kode yang tidak ada yang berani hapus.
Mendefinisikan dan memakai
composer require laravel/pennant
php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider"
php artisan migrate
// AppServiceProvider::boot()
use Laravel\Pennant\Feature;
Feature::define('editor-baru', fn (User $user) => match (true) {
$user->is_staf => true, // staf lebih dulu
$user->created_at->isToday() => true, // pengguna baru
default => Lottery::odds(1, 100), // 1% sisanya
});
Feature::define('paywall', fn () => config('toko.paywall_aktif'));
if (Feature::active('editor-baru')) {
return view('artikel.editor-baru');
}
Feature::when('editor-baru',
whenActive: fn () => view('artikel.editor-baru'),
whenInactive: fn () => view('artikel.editor-lama'),
);
// Untuk pengguna tertentu
Feature::for($user)->active('editor-baru');
@feature('editor-baru')
<x-editor-baru />
@else
<x-editor-lama />
@endfeature
Route::get('/editor', ...)->middleware(EnsureFeaturesAreActive::using('editor-baru'));
Lottery::odds(1, 100) dievaluasi sekali per pengguna, lalu disimpan. Ini
penting: tanpa penyimpanan, seorang pengguna bisa mendapat editor baru pada satu permintaan dan editor lama
pada permintaan berikutnya โ pengalaman yang membingungkan dan bug yang mustahil direproduksi. Pennant
menyimpan hasilnya, jadi keputusannya stabil.
Kenapa ini penting untuk aplikasi yang tidak boleh mati
| Tanpa flag | Dengan flag |
|---|---|
| Rilis fitur = deploy | Rilis fitur = balik sakelar |
| Fitur bermasalah = rollback penuh | Matikan satu fitur; sisanya tetap jalan |
| Semua pengguna sekaligus | 1% dulu, amati, lalu naikkan |
| Cabang panjang yang sulit digabung | Kode masuk main lebih awal, dalam keadaan mati |
| Pemulihan hitungan puluhan menit | Pemulihan hitungan detik |
Manfaat yang paling kurang disadari: cabang git jadi pendek. Tanpa flag, fitur besar hidup di cabang
selama berminggu-minggu dan penggabungannya jadi peristiwa yang menakutkan. Dengan flag, kode digabung ke
main setiap hari dalam keadaan mati โ konfliknya kecil, dan risikonya tersebar.
Mengelola
php artisan pennant:purge # bersihkan semua nilai tersimpan
php artisan pennant:purge editor-baru # satu fitur saja
Feature::for($user)->activate('editor-baru');
Feature::for($user)->deactivate('editor-baru');
Feature::activateForEveryone('editor-baru');
Feature::deactivateForEveryone('editor-baru'); // โ tombol darurat
// Muat sekaligus supaya tidak satu query per pemeriksaan
Feature::for($user)->loadMissing(['editor-baru', 'paywall']);
loadMissing() mencegah N+1 versi feature flag. Memeriksa lima flag di dalam loop yang
berjalan untuk lima puluh pengguna berarti dua ratus lima puluh pemeriksaan. Muat semuanya di awal permintaan,
dan biayanya jadi satu query.
Menguji
it('menampilkan editor baru saat fitur aktif', function () {
Feature::define('editor-baru', true);
actingAs($editor)->get('/editor')->assertSee('Editor Baru');
});
it('tetap menampilkan editor lama saat fitur mati', function () {
Feature::define('editor-baru', false);
actingAs($editor)->get('/editor')->assertSee('Editor Lama');
});
Uji kedua cabangnya. Flag yang hanya diuji dalam keadaan menyala berarti jalur mati tidak pernah diperiksa โ dan justru jalur itulah yang akan kamu andalkan saat terjadi masalah pukul dua pagi.
Kebersihan flag
| Jenis flag | Umur | Setelahnya |
|---|---|---|
| Peluncuran fitur | Hari sampai minggu | Hapus flag dan kode lamanya |
| Uji A/B | Sampai eksperimennya selesai | Hapus, pilih pemenangnya |
| Sakelar operasional (mis. matikan pencarian) | Permanen | Simpan, dokumentasikan |
| Fitur khusus pelanggan | Permanen | Ini sebenarnya izin, bukan flag |
Flag yang tidak dibersihkan adalah utang teknis yang berbunga. Setiap flag menggandakan jumlah kemungkinan jalur kode; sepuluh flag berarti secara teori ada ribuan kombinasi yang tidak pernah ada yang menguji. Beri setiap flag peluncuran tanggal kedaluwarsa saat kamu membuatnya, dan perlakukan penghapusannya sebagai bagian dari pekerjaan fitur itu โ bukan sebagai pekerjaan terpisah yang tidak pernah dijadwalkan.
Latihan: bungkus satu fitur nyata dengan flag yang aktif untuk 10% pengguna, tulis tes untuk kedua
cabangnya, lalu jalankan Feature::deactivateForEveryone() dan pastikan aplikasi kembali ke
perilaku lama tanpa deploy apa pun.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.