Feature Flags dengan Claude Code: rollout aman, eksperimen, dan kill switch
Panduan feature flags dengan Claude Code: rollout, eksperimen, kill switch, targeting, metrik, dan cleanup.
Mulai dari aturan operasi, bukan toggle
Feature flag adalah switch saat runtime: kode bisa dideploy, sementara fitur tetap mati, dibuka bertahap, atau dimatikan cepat saat ada insiden. Kesalahan pemula bukan menulis if (flag). Kesalahan yang mahal adalah memperlakukan release flag, experiment flag, dan kill switch sebagai hal yang sama. Ketiganya punya umur, owner, metrik, dan aturan cleanup yang berbeda.
Claude Code dapat membuat cabang UI dengan cepat. Implementasi produksi membutuhkan lebih banyak: default yang aman, targeting context, batas evaluasi server/client, log exposure, guardrail metrics, langkah rollback, dan tanggal penghapusan untuk flag sementara. Dalam workflow Masa untuk situs konten dan SaaS kecil, prompt terbaik bukan “tambahkan feature flags”; prompt yang lebih berguna adalah “apa yang bisa gagal, apa yang bisa dimatikan, dan bagaimana kita tahu rollout ini sehat”.
Gunakan dokumentasi primer sebagai acuan. OpenFeature memisahkan evaluation API yang dipakai aplikasi dari provider di belakangnya, lalu memakai evaluation context untuk data user, app, dan environment. LaunchDarkly mendokumentasikan release flags, experiment flags, dan kill switches. Unleash menjelaskan lifecycle Define, Develop, Production, Cleanup, Archived agar flag usang tidak tertinggal. Claude Code best practices juga menekankan jalur verifikasi yang jelas.
Referensi yang digunakan:
- OpenFeature introduction
- OpenFeature evaluation context
- LaunchDarkly creating flags
- LaunchDarkly targeting rules
- LaunchDarkly guarded rollouts
- Unleash feature flag lifecycle
- Claude Code best practices
Pisahkan release, eksperimen, dan kill switch
Klasifikasikan flag berdasarkan umur sebelum menulis kode. Release flag menyembunyikan pekerjaan yang belum selesai, membuka fitur ke audiens yang makin besar, lalu dihapus setelah rollout 100%. Experiment flag menguji hipotesis dan wajib mencatat exposure serta hasil. Kill switch adalah kontrol keselamatan yang lebih panjang untuk kegagalan API eksternal, lonjakan biaya, layanan rekomendasi lambat, atau otomasi berisiko.
| Kasus produk | Jenis | Metrik sukses | Aksi saat gagal |
|---|---|---|---|
| Checkout baru untuk 25% akun Pro | Release | Checkout selesai, error pembayaran | Matikan checkout_v2_release |
| Membandingkan CTA di pricing page | Eksperimen | Signup start, klik niat bayar | Hentikan eksperimen, tampilkan control |
| Memindahkan blok affiliate ke tengah artikel | Eksperimen | Klik produk, baca sampai selesai | Kembalikan blok ke footer artikel |
| Mematikan rekomendasi saat vendor incident | Kill switch | Latensi p95, rasio 5xx | Matikan recommendations_enabled |
Tanpa pengukuran, flag hanya tebakan. Bacaan terkait: A/B testing dengan Claude Code dan analytics dengan Claude Code. Untuk situs monetisasi, CTA bukan hanya klik; lindungi kualitas AdSense, read completion, revenue affiliate, dan niat konsultasi. Untuk mulai sendiri, ambil resource gratis; untuk tim, lanjutkan ke konsultasi bahasa Inggris.
Config minimal dan evaluator
Jangan mengikat business logic terlalu cepat ke satu vendor. Aplikasi sebaiknya mengevaluasi flag berdasarkan key, default value, dan context; provider di belakangnya nanti bisa LaunchDarkly, Unleash, OpenFeature, JSON, atau service internal. Contoh ini bisa disimpan sebagai flag-demo.ts dan dijalankan dengan npx tsx flag-demo.ts.
type FlagValue = boolean | string | number;
type FlagKind = "release" | "experiment" | "kill_switch";
type Plan = "free" | "pro" | "enterprise";
type Role = "user" | "admin";
type Operator = "equals" | "in";
type FlagContext = {
targetingKey: string;
plan: Plan;
country: string;
role: Role;
appVersion: string;
};
type FlagRule = {
attribute: keyof Omit<FlagContext, "targetingKey">;
operator: Operator;
values: string[];
value: FlagValue;
percentage?: number;
};
type FlagConfig = {
key: string;
kind: FlagKind;
enabled: boolean;
defaultValue: FlagValue;
offValue: FlagValue;
owner: string;
removeAfter?: string;
rules: FlagRule[];
};
const registry: Record<string, FlagConfig> = {
checkout_v2_release: {
key: "checkout_v2_release",
kind: "release",
enabled: true,
defaultValue: false,
offValue: false,
owner: "growth-platform",
removeAfter: "2026-07-15",
rules: [
{
attribute: "role",
operator: "equals",
values: ["admin"],
value: true,
},
{
attribute: "plan",
operator: "in",
values: ["pro", "enterprise"],
value: true,
percentage: 25,
},
],
},
pricing_copy_2026_06: {
key: "pricing_copy_2026_06",
kind: "experiment",
enabled: true,
defaultValue: "control",
offValue: "control",
owner: "monetization",
removeAfter: "2026-06-30",
rules: [
{
attribute: "country",
operator: "in",
values: ["JP", "US", "DE"],
value: "simple",
percentage: 50,
},
],
},
recommendations_enabled: {
key: "recommendations_enabled",
kind: "kill_switch",
enabled: true,
defaultValue: true,
offValue: false,
owner: "sre",
rules: [],
},
};
function bucketFor(flagKey: string, targetingKey: string): number {
const input = `${flagKey}:${targetingKey}`;
let hash = 0;
for (const char of input) {
hash = (hash * 31 + char.charCodeAt(0)) >>> 0;
}
return hash % 100;
}
function ruleMatches(
flagKey: string,
rule: FlagRule,
context: FlagContext,
): boolean {
const actual = String(context[rule.attribute]);
const matched =
rule.operator === "equals"
? actual === rule.values[0]
: rule.values.includes(actual);
if (!matched) return false;
if (rule.percentage === undefined) return true;
return bucketFor(flagKey, context.targetingKey) < rule.percentage;
}
export function evaluateFlag<T extends FlagValue = FlagValue>(
key: string,
context: FlagContext,
): T {
const flag = registry[key];
if (!flag) return false as T;
if (!flag.enabled) return flag.offValue as T;
for (const rule of flag.rules) {
if (ruleMatches(flag.key, rule, context)) {
return rule.value as T;
}
}
return flag.defaultValue as T;
}
const demoContexts: FlagContext[] = [
{
targetingKey: "user_001",
plan: "pro",
country: "JP",
role: "user",
appVersion: "1.8.0",
},
{
targetingKey: "user_002",
plan: "free",
country: "BR",
role: "admin",
appVersion: "1.8.0",
},
];
for (const context of demoContexts) {
console.log(context.targetingKey, {
checkout: evaluateFlag<boolean>("checkout_v2_release", context),
pricingCopy: evaluateFlag<string>("pricing_copy_2026_06", context),
recommendations: evaluateFlag<boolean>(
"recommendations_enabled",
context,
),
});
}
Detail pentingnya sengaja sederhana. Flag yang tidak dikenal jatuh ke fallback aman. Rollout persentase memakai targetingKey stabil, bukan Math.random(). Setiap flag sementara punya owner dan removeAfter. Registry bisa pindah ke control plane tanpa mengubah kontrak aplikasi.
Evaluasi di server, tampilkan di client
Semua hal terkait billing, otorisasi, kuota, stok, atau biaya backend harus dievaluasi di server. Client-side flags cocok untuk copy UI, layout, petunjuk onboarding, atau perubahan visual yang sudah diotorisasi. Jangan kirim aturan targeting rahasia ke browser dan jangan memakai tombol tersembunyi sebagai access control.
type User = {
id: string;
plan: "free" | "pro" | "enterprise";
role: "user" | "admin";
};
type RequestLike = {
headers: {
get(name: string): string | null;
};
};
export function buildFlagContext(
user: User,
request: RequestLike,
): FlagContext {
return {
targetingKey: user.id,
plan: user.plan,
role: user.role,
country: request.headers.get("x-country") ?? "US",
appVersion: process.env.NEXT_PUBLIC_APP_VERSION ?? "dev",
};
}
export function getServerFlagSnapshot(context: FlagContext) {
return {
checkoutV2: evaluateFlag<boolean>("checkout_v2_release", context),
pricingCopy: evaluateFlag<string>("pricing_copy_2026_06", context),
};
}
type PricingFlags = {
pricingCopy: string;
};
export function PricingCta({ flags }: { flags: PricingFlags }) {
const label =
flags.pricingCopy === "simple"
? "Mulai dari paket gratis"
: "Mulai uji coba gratis";
return <a href="/signup">{label}</a>;
}
Komponen React hanya menampilkan snapshot yang sudah dievaluasi server. Saat meminta Claude Code, tulis jelas bahwa permission dan billing tetap server-side, sementara client hanya memakai hasilnya.
Rollout dengan observability
Rollout aman bukan sekadar “mulai dari 1%”. Kamu perlu jadwal ramp, metrik, dan ambang rollback. Unleash gradual rollout menggabungkan persentase, stickiness, dan constraint. LaunchDarkly guarded rollout menghubungkan rollout dengan metrik, lalu bisa pause atau rollback saat ada regresi. Model operasi ini tetap berguna meski evaluator kamu kecil.
Catat tiga lapisan: exposure, metrik utama, dan guardrails. Exposure menjawab siapa melihat nilai flag apa. Metrik utama menjawab apakah perilaku yang dituju membaik. Guardrails menjawab apakah speed, error, kualitas revenue, support load, atau trust memburuk.
type FlagExposure = {
flagKey: string;
value: FlagValue;
targetingKey: string;
route: string;
evaluatedAt: string;
};
export function trackFlagExposure(event: FlagExposure) {
console.log(
JSON.stringify({
event_name: "feature_flag_exposure",
...event,
}),
);
}
Untuk checkout, pantau 5xx, payment failure, dan support ticket. Untuk blog monetisasi, jangan hanya lihat affiliate clicks; lihat read completion, bounce, Core Web Vitals, dan paid-intent clicks. Untuk fitur AI, pantau token cost, p95 latency, dan kuota per user. Klik naik tetapi kualitas pembeli turun bukan kemenangan.
Contoh kegagalan nyata
Pertama, assignment random di setiap page load. Jika user refresh lalu berubah dari A ke B, data exposure dan conversion rusak. Gunakan targeting key yang stabil.
Kedua, entitlement premium hanya disembunyikan di client. Tombol yang hilang di React tidak melindungi API. Feature flag mengatur UX, bukan menggantikan authorization.
Ketiga, default tidak aman. Release flag yang tidak dikenal biasanya harus mengembalikan false. Jika typo menghasilkan true, kamu baru saja launch tanpa sengaja.
Keempat, flag sementara tidak dihapus. Beberapa bulan kemudian, checkout_v2_release menjadi cabang misterius. Setelah keputusan dibuat, buka PR cleanup.
Kelima, aturan terlalu bertumpuk. Parent flag, child flag, dan percentage rollout yang overlap membuat sulit menjelaskan siapa sebenarnya yang melihat fitur.
Prompt aman untuk Claude Code
Claude Code bisa membaca file, mengubah kode, dan menjalankan test. Beri batas dan verifikasi sejak awal.
Tambahkan workflow feature flag ke repository ini.
Flag pertama adalah checkout_v2_release untuk staged rollout.
Constraints:
- Billing dan authorization dievaluasi di server.
- Release flag yang tidak dikenal mengembalikan false.
- Percentage rollout memakai targetingKey stabil.
- Registry memiliki owner dan removeAfter.
- Jangan ubah file yang tidak terkait.
Output wajib:
- Flag registry minimal dan fungsi evaluateFlag
- Tipe event exposure
- Minimal 3 use case produk
- Contoh kegagalan dan langkah rollback
- Command test yang dijalankan
Sebelum merge, pakai prompt review:
Review implementasi feature flag ini.
Fokus pada defaults, batas server/client, bucketing stabil,
event exposure yang hilang, tanggal cleanup, dan rollback behavior.
Urutkan temuan berdasarkan severity dan sebutkan file tepatnya.
Struktur ini membuat Claude Code lebih mungkin menghasilkan sistem yang bisa dioperasikan, bukan sekadar toggle visual.
Cleanup adalah bagian dari shipping
Setiap flag mulai menua sejak dibuat. Release flags dihapus setelah rollout penuh. Experiment flags dihapus setelah pemenang dipilih. Kill switches boleh tinggal, tetapi perlu owner, runbook, dan alert. Masukkan owner, removeAfter, metrik, dan rencana PR penghapusan ke template pull request.
Evaluator di artikel ini diverifikasi sebagai demo TypeScript yang bisa dijalankan: targetingKey yang sama masuk bucket yang sama, flag tidak dikenal memakai fallback aman, dan kill switch punya off value eksplisit. Catatan praktis Masa untuk konten monetisasi: ukur kualitas selain klik. Mulailah dari satu release flag, satu experiment flag, dan satu kill switch sebelum membangun platform lengkap.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Permission safety ladder Claude Code: perluas akses tanpa kehilangan kontrol
Naik dari read-only ke edit terbatas, command bukti, dan cek deploy dengan kontrol yang jelas.
Claude Code Small PR Proof Pack: perubahan kecil yang mudah direview
Paket bukti untuk PR Claude Code: diff, check, URL publik, jalur CTA, dan rollback.
Review gate Claude Code sebelum commit: diff, test, URL publik, dan CTA
Cara memakai Claude Code sebelum commit: diff scope, build, URL publik, link Gumroad, CTA konsultasi, missing test, dan file tidak terkait.