Tips & Tricks (Diperbarui: 2/6/2026)

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.

Feature Flags dengan Claude Code: rollout aman, eksperimen, dan kill switch

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:

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 produkJenisMetrik suksesAksi saat gagal
Checkout baru untuk 25% akun ProReleaseCheckout selesai, error pembayaranMatikan checkout_v2_release
Membandingkan CTA di pricing pageEksperimenSignup start, klik niat bayarHentikan eksperimen, tampilkan control
Memindahkan blok affiliate ke tengah artikelEksperimenKlik produk, baca sampai selesaiKembalikan blok ke footer artikel
Mematikan rekomendasi saat vendor incidentKill switchLatensi p95, rasio 5xxMatikan 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.

#Claude Code #feature flags #rollout aman #TypeScript #observability
Gratis

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.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.