Merilis fitur baru langsung ke seluruh pengguna sering kali berisiko. Jika ada bug pada alur pembayaran, query yang berat, atau integrasi pihak ketiga yang tidak stabil, dampaknya bisa luas dan sulit ditangani. Feature flag adalah pendekatan untuk memisahkan deploy dari release: kode bisa sudah ada di produksi, tetapi perilakunya baru diaktifkan untuk kelompok pengguna tertentu ketika tim siap.
Di ekosistem Laravel, Laravel Pennant menyediakan cara yang rapi untuk mendefinisikan dan mengevaluasi feature flag. Pendekatan ini cocok untuk gradual rollout, eksperimen internal, pembatasan fitur per tenant, hingga mematikan fitur bermasalah tanpa harus buru-buru melakukan rollback aplikasi.
Artikel ini fokus pada implementasi yang praktis: mulai dari setup, definisi flag, evaluasi berbasis user atau tenant, rollout bertahap, penggunaan di controller, Blade, dan middleware, sampai best practice operasional agar tim dapat mengadopsi strategi rilis yang lebih aman.
Mengapa feature flag penting dalam aplikasi Laravel
Tanpa feature flag, tim biasanya punya dua pilihan yang kurang ideal: menahan deploy sampai fitur benar-benar siap, atau deploy ke semua pengguna sekaligus. Keduanya punya konsekuensi. Menahan deploy memperbesar ukuran perubahan dan menyulitkan debugging. Sebaliknya, rilis serentak meningkatkan radius dampak jika ada masalah.
Feature flag memberi beberapa keuntungan nyata:
- Rilis bertahap: aktifkan fitur untuk tim internal, lalu sebagian kecil pengguna, baru kemudian semua pengguna.
- Kill switch: matikan fitur yang bermasalah tanpa perlu rollback seluruh aplikasi.
- Eksperimen: uji perilaku baru untuk segmen tertentu.
- Kontrol per tenant: cocok untuk SaaS dengan paket atau kontrak berbeda.
- Pengurangan risiko: deploy dan release menjadi dua langkah terpisah.
Namun, feature flag juga bukan tanpa biaya. Flag yang dibiarkan terlalu lama akan menambah kompleksitas cabang logika, memperbesar beban testing, dan membuat kode sulit dibaca. Karena itu, flag harus dikelola sebagai bagian dari proses engineering, bukan hanya trik sementara.
Setup Laravel Pennant
Jika aplikasi Anda belum menggunakan Pennant, langkah awalnya adalah memasang paket dan menjalankan konfigurasi yang diperlukan.
composer require laravel/pennant
php artisan pennant:install
php artisan migratePerintah instalasi biasanya menyiapkan file konfigurasi dan, tergantung driver yang digunakan, tabel yang dibutuhkan untuk menyimpan nilai flag. Dalam praktiknya, Anda perlu memahami bahwa Pennant dapat mengevaluasi flag berdasarkan konteks tertentu, misalnya user yang sedang login atau entitas lain seperti tenant.
Setelah terpasang, periksa file konfigurasi Pennant. Hal penting yang biasanya perlu diperhatikan:
- Driver penyimpanan: apakah state flag disimpan di database, cache, atau mekanisme lain yang didukung konfigurasi aplikasi.
- Strategi evaluasi: apakah flag dihitung dinamis setiap kali dipanggil, atau nilainya dipersist untuk aktor tertentu.
- Lingkungan: pada local dan staging, Anda mungkin ingin perilaku flag lebih mudah dioverride untuk debugging.
Catatan: Jangan mengandalkan variabel environment sebagai pengganti feature flag untuk kebutuhan per user atau per tenant. ENV cocok untuk konfigurasi aplikasi global, tetapi tidak fleksibel untuk rollout bertahap atau evaluasi berdasarkan aktor tertentu.
Mendefinisikan feature flag dengan benar
Pennant memungkinkan Anda mendefinisikan flag secara terpusat. Biasanya definisi diletakkan di service provider, sehingga logika aktivasi tidak tersebar di banyak file.
<?php
namespace App\Providers;
use App\Models\User;
use Illuminate\Support\ServiceProvider;
use Laravel\Pennant\Feature;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Feature::define('new-checkout', function (User $user) {
return $user->is_internal;
});
Feature::define('invoice-v2', function (User $user) {
return $user->email_verified_at !== null;
});
}
}Di contoh di atas, new-checkout hanya aktif untuk user internal, sedangkan invoice-v2 aktif bagi user yang sudah terverifikasi. Pendekatan ini sederhana, tetapi penting untuk menjaga agar logika flag tetap singkat dan mudah dipahami. Jika evaluasi butuh query kompleks atau banyak dependensi, pindahkan ke class atau service khusus agar definisi tetap bersih.
Best practice penamaan flag
Nama flag sebaiknya deskriptif, stabil, dan berorientasi perilaku, bukan implementasi teknis sementara. Gunakan pola yang konsisten seperti:
new-checkoutsearch-v2beta-report-exporttenant-custom-branding
Hindari nama yang ambigu seperti test-feature, flag1, atau temporary-ui. Nama yang buruk menyulitkan audit, dashboard operasional, dan komunikasi lintas tim.
Kategori flag yang berguna
Secara praktis, Anda bisa membedakan flag menjadi beberapa kategori:
- Release flag: untuk merilis fitur baru bertahap.
- Ops flag: sebagai saklar darurat atau kill switch.
- Experiment flag: untuk eksperimen atau validasi hipotesis.
- Entitlement flag: untuk membedakan kemampuan per paket atau tenant.
Kategorisasi ini membantu menentukan umur flag. Release flag biasanya harus dibersihkan setelah rollout selesai, sedangkan entitlement flag bisa bertahan lebih lama karena terkait model bisnis.
Evaluasi berbasis user dan tenant
Salah satu kekuatan feature flag adalah kemampuan untuk mengevaluasi fitur berdasarkan aktor tertentu. Dalam aplikasi B2C, aktor paling umum adalah user. Dalam aplikasi SaaS B2B, sering kali yang lebih relevan adalah tenant atau organisasi.
Evaluasi berbasis user
Untuk kasus user, penggunaan dasarnya cukup langsung:
use Laravel\Pennant\Feature;
if (Feature::for(auth()->user())->active('new-checkout')) {
// tampilkan checkout baru
} else {
// fallback ke checkout lama
}Pola Feature::for(...) penting ketika Anda ingin eksplisit menentukan aktor evaluasi. Ini lebih aman dibanding mengandalkan konteks implisit, terutama pada job, command, atau service yang berjalan di luar request HTTP biasa.
Evaluasi berbasis tenant
Jika aplikasi Anda multi-tenant, lebih tepat mengevaluasi flag terhadap model tenant. Misalnya tenant enterprise mendapat fitur lebih dulu.
<?php
namespace App\Providers;
use App\Models\Tenant;
use Illuminate\Support\ServiceProvider;
use Laravel\Pennant\Feature;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Feature::define('advanced-analytics', function (Tenant $tenant) {
return $tenant->plan === 'enterprise';
});
}
}Penggunaannya:
$tenant = app('currentTenant');
if (Feature::for($tenant)->active('advanced-analytics')) {
// aktifkan modul analytics lanjutan
}Pendekatan ini lebih konsisten dibanding mengecek paket tenant di banyak tempat secara manual. Semua keputusan tetap terpusat di layer flag.
Jebakan umum: jangan campur evaluasi per user dan per tenant tanpa aturan yang jelas. Misalnya, jika flag seharusnya aktif untuk tenant tertentu, tetapi di sebagian controller Anda mengeceknya terhadap user, hasilnya bisa tidak konsisten.
Rollout bertahap dan eksperimen internal
Rollout bertahap biasanya dilakukan dalam beberapa tahap operasional:
- Aktifkan hanya untuk tim internal.
- Buka untuk tenant tertentu atau pelanggan pilot.
- Tingkatkan ke sebagian kecil trafik atau cohort tertentu.
- Aktifkan untuk seluruh pengguna jika metrik stabil.
Untuk tahap awal, user internal adalah target yang paling aman:
Feature::define('new-dashboard', function (User $user) {
return $user->is_internal;
});Jika ingin membuat pilot rollout ke sejumlah akun tertentu, gunakan daftar eksplisit dari database atau konfigurasi administratif. Sebaiknya jangan hardcode daftar ID di banyak tempat. Simpan di tabel atau service agar bisa diaudit dan diubah dengan aman.
Untuk eksperimen internal, flag juga berguna untuk mengisolasi UI atau API baru sebelum dipublikasikan. Misalnya tim produk ingin mencoba alur onboarding baru hanya untuk staf dan customer success. Dengan begitu, feedback bisa dikumpulkan sebelum fitur dibuka lebih luas.
Jika Anda membutuhkan rollout berbasis persentase, pastikan pendekatannya deterministik. Artinya user yang sama sebaiknya konsisten tetap masuk atau tidak masuk cohort yang sama, bukan berubah-ubah setiap request. Implementasinya biasanya dilakukan dengan hashing ID aktor terhadap ambang tertentu. Jika tim membangun strategi ini sendiri, pastikan logikanya stabil dan terdokumentasi.
Penggunaan di controller, Blade, dan middleware
Di controller
Controller adalah tempat umum untuk memilih alur bisnis berdasarkan flag.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Laravel\Pennant\Feature;
class CheckoutController extends Controller
{
public function show(Request $request)
{
$user = $request->user();
if (Feature::for($user)->active('new-checkout')) {
return view('checkout.v2');
}
return view('checkout.v1');
}
}Prinsip pentingnya: gunakan flag untuk memilih perilaku pada batas yang jelas, bukan menyebarkan banyak if kecil di seluruh method. Jika fitur baru sangat kompleks, pertimbangkan dua service terpisah, misalnya LegacyCheckoutService dan NewCheckoutService, lalu flag hanya memilih service mana yang dipakai.
Di Blade
Di layer tampilan, flag bermanfaat untuk menyembunyikan menu, tombol, atau komponen tertentu.
@php($user = auth()->user())
@if ($user && Laravel\Pennant\Feature::for($user)->active('new-dashboard'))
@include('dashboard.partials.v2-banner')
@endifGunakan secukupnya. Jika terlalu banyak percabangan di Blade, tampilan akan sulit dirawat. Sebaiknya keputusan besar tetap dilakukan di controller atau view model, lalu Blade hanya menerima data yang sudah siap ditampilkan.
Di middleware
Middleware cocok untuk melindungi route tertentu agar hanya bisa diakses jika flag aktif.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Laravel\Pennant\Feature;
use Symfony\Component\HttpFoundation\Response;
class EnsureFeatureIsActive
{
public function handle(Request $request, Closure $next, string $feature): Response
{
$user = $request->user();
if (! $user || ! Feature::for($user)->active($feature)) {
abort(404);
}
return $next($request);
}
}Daftarkan middleware ini, lalu gunakan pada route:
Route::middleware(['auth', 'feature:new-checkout'])
->get('/checkout/v2', [CheckoutController::class, 'show']);Untuk endpoint yang benar-benar belum siap dipublikasikan, middleware memberi perlindungan yang lebih baik dibanding hanya menyembunyikan link di UI.
Strategi mematikan fitur dengan aman
Salah satu alasan utama menggunakan feature flag adalah kemampuan untuk mematikan fitur dengan cepat. Namun, ini hanya aman jika aplikasi memang dirancang dengan fallback yang jelas.
Beberapa prinsip penting:
- Sediakan jalur fallback: jika
new-checkoutmati, pastikan checkout lama masih berfungsi. - Hindari migrasi yang memutus kompatibilitas: perubahan skema database sebaiknya kompatibel dengan versi lama dan baru selama masa rollout.
- Jangan gabungkan banyak perubahan kritis di satu flag: semakin besar cakupan satu flag, semakin sulit mematikan sebagian perilakunya.
- Pantau error dan metrik bisnis: aktivasi flag tanpa observabilitas membuat tim terlambat mendeteksi masalah.
Dalam praktik database, strategi yang aman biasanya mengikuti pola expand and contract. Tambah kolom atau struktur baru terlebih dahulu tanpa menghapus yang lama, biarkan dua versi aplikasi bekerja berdampingan selama rollout, lalu bersihkan setelah migrasi stabil dan flag dipensiunkan.
Audit perubahan, testing, dan operasional tim
Audit perubahan
Siapa yang mengaktifkan flag, kapan, dan untuk siapa adalah informasi penting. Jika organisasi Anda memiliki panel admin untuk mengubah nilai flag, pastikan ada audit trail yang mencatat:
- nama flag,
- aktor yang melakukan perubahan,
- waktu perubahan,
- scope perubahan, misalnya tenant atau user tertentu,
- alasan perubahan atau tiket insiden terkait.
Tanpa audit, debugging insiden akan lebih sulit karena tim tidak tahu apakah masalah berasal dari deploy kode atau perubahan konfigurasi flag.
Testing
Feature flag menambah cabang logika, sehingga test harus mencakup kedua kondisi: aktif dan nonaktif. Minimal, buat test untuk perilaku utama di dua skenario tersebut.
public function test_checkout_uses_new_flow_when_flag_is_active(): void
{
$user = User::factory()->create(['is_internal' => true]);
$this->actingAs($user)
->get('/checkout')
->assertSee('Checkout V2');
}
public function test_checkout_falls_back_to_old_flow_when_flag_is_inactive(): void
{
$user = User::factory()->create(['is_internal' => false]);
$this->actingAs($user)
->get('/checkout')
->assertSee('Checkout V1');
}Selain test fungsional, pertimbangkan juga:
- Integration test untuk route yang dilindungi middleware flag.
- Test regresi untuk fallback ketika flag dimatikan.
- Contract test jika fitur memengaruhi respons API.
Jebakan umum yang sering terjadi
- Flag tidak dibersihkan: setelah rollout 100%, cabang lama tetap ada dan menambah utang teknis.
- Logika evaluasi terlalu berat: setiap request memicu query tambahan yang sebenarnya bisa dihindari.
- Ketergantungan tersembunyi: UI mati, tetapi job, webhook, atau endpoint internal masih mengasumsikan fitur aktif.
- Nama flag berubah-ubah: menyulitkan audit dan membuat konfigurasi tidak konsisten antar lingkungan.
- Tidak ada owner flag: tidak jelas siapa yang bertanggung jawab menghapus atau mengelola flag.
Praktik yang baik adalah menambahkan metadata internal untuk setiap flag: owner, tujuan, tanggal dibuat, dan target penghapusan. Meski metadata ini tidak selalu menjadi bagian fitur framework, tim bisa mendokumentasikannya di kode, ADR, atau sistem internal.
Rekomendasi adopsi untuk tim
Jika tim baru mulai memakai feature flag di Laravel, jangan langsung menaruh semua perilaku di balik flag. Mulailah dari kasus yang paling bernilai:
- Fitur baru dengan risiko bisnis tinggi, seperti checkout atau billing.
- Fitur khusus tenant atau paket berlangganan.
- Perubahan besar pada UI yang ingin diuji internal lebih dulu.
Lalu tetapkan aturan sederhana:
- Setiap flag harus punya nama yang jelas dan owner.
- Setiap release flag harus punya rencana penghapusan.
- Perubahan flag penting harus tercatat dan dapat diaudit.
- Test harus mencakup kondisi aktif dan nonaktif.
- Fallback harus dirancang sejak awal, bukan setelah insiden terjadi.
Dengan disiplin ini, Laravel Pennant bukan sekadar alat untuk mematikan tombol fitur, melainkan fondasi release strategy yang lebih aman. Tim dapat melakukan deploy lebih sering, mengurangi radius dampak perubahan, dan merespons insiden lebih cepat tanpa harus mengorbankan kontrol operasional.
Pada akhirnya, nilai utama feature flag bukan pada kemampuannya menyembunyikan fitur, tetapi pada kemampuannya memberi kendali. Dalam sistem produksi, kendali semacam ini sering kali lebih penting daripada kecepatan menulis kode itu sendiri.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!