Feature Flags com Claude Code: rollouts seguros, experimentos e kill switches
Guia prático de feature flags com Claude Code: rollout, experimentos, kill switches, targeting, métricas e limpeza.
Comece pela regra operacional, não pelo toggle
Feature flag é um interruptor em tempo de execução: o código pode ser implantado enquanto uma funcionalidade fica desligada, é aberta aos poucos ou é desligada rapidamente em uma emergência. O erro de iniciante não é escrever if (flag). O erro caro é tratar release flag, experiment flag e kill switch como se tivessem a mesma vida útil.
Claude Code gera rápido uma ramificação de UI, mas produção exige mais: default seguro, targeting context, fronteira entre avaliação no servidor e no cliente, log de exposição, métricas de guardrail, passos de rollback e data para remover flags temporárias. Nos fluxos de Masa para sites de conteúdo e pequenos SaaS, o melhor prompt não é “adicione feature flags”; é “o que pode falhar, o que posso desligar e como saberemos se o rollout está saudável”.
Use documentação primária para definir o modelo. OpenFeature separa a API de avaliação usada pela aplicação do provider por trás e usa evaluation context para dados de usuário, app e ambiente. LaunchDarkly documenta release flags, experiment flags e kill switches. Unleash descreve o ciclo Define, Develop, Production, Cleanup e Archived para evitar flags esquecidas. A documentação de Claude Code também recomenda dar ao agente um caminho claro de verificação.
Referências usadas:
- OpenFeature introduction
- OpenFeature evaluation context
- LaunchDarkly creating flags
- LaunchDarkly targeting rules
- LaunchDarkly guarded rollouts
- Unleash feature flag lifecycle
- Claude Code best practices
Separe release, experimento e kill switch
Classifique pela vida útil antes de escrever código. Uma release flag esconde trabalho incompleto, abre para uma audiência crescente e desaparece depois de 100%. Uma experiment flag testa uma hipótese e precisa registrar exposição e resultado. Um kill switch é um controle de segurança mais duradouro para falha de fornecedor, pico de custo, recomendação lenta ou automação arriscada.
| Caso de produto | Tipo | Métrica de sucesso | Ação se falhar |
|---|---|---|---|
| Novo checkout para 25% das contas Pro | Release | Checkout concluído, erros de pagamento | Desligar checkout_v2_release |
| Comparar CTA da página de preços | Experimento | Início de cadastro, cliques com intenção paga | Parar experimento e servir control |
| Mover bloco affiliate para dentro do artigo | Experimento | Cliques em produto, leitura completa | Voltar o bloco ao rodapé |
| Desativar recomendações durante incidente de fornecedor | Kill switch | Latência p95, taxa 5xx | Desligar recommendations_enabled |
Sem medição, uma flag vira palpite. Leia também A/B testing com Claude Code e analytics com Claude Code. Em sites monetizados, a CTA não é só clique: proteja qualidade de AdSense, leitura completa, receita affiliate e intenção de consultoria. Para começar sozinho, use o material gratuito; para equipe, a consultoria em inglês é o próximo passo.
Configuração mínima e evaluator
Não prenda a lógica de negócio a um fornecedor logo no início. A aplicação deve avaliar por key, valor padrão e context; depois o provider pode ser LaunchDarkly, Unleash, OpenFeature, JSON ou um serviço interno. O exemplo abaixo cabe em flag-demo.ts e roda com 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,
),
});
}
Os detalhes importantes são simples de propósito. Flag desconhecida cai para fallback seguro. Rollout percentual usa targetingKey estável, não Math.random(). Cada flag temporária tem owner e removeAfter. O registry pode ir para um painel depois sem mudar o contrato da aplicação.
Avalie no servidor e exiba no cliente
Tudo que envolve billing, autorização, cotas, estoque ou custo backend deve ser avaliado no servidor. Flags no cliente servem para copy, layout, dicas de onboarding ou variações visuais já autorizadas. Não envie regras secretas ao navegador e não use botão escondido como controle de acesso.
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"
? "Começar pelo plano gratuito"
: "Iniciar teste grátis";
return <a href="/signup">{label}</a>;
}
O componente React só renderiza um snapshot já avaliado. No prompt para Claude Code, diga que autorização e billing ficam no servidor e que o cliente consome apenas o resultado.
Rollout com observabilidade
Rollout seguro não é apenas “comece em 1%”. Você precisa de plano de aumento, métricas e limites de rollback. Unleash combina porcentagem, stickiness e constraints. LaunchDarkly guarded rollouts conecta rollout a métricas e permite pausar ou reverter quando há regressão. Copie esse modelo mesmo com evaluator próprio.
Registre três camadas: exposição, métrica primária e guardrails. Exposição diz quem viu qual valor. Métrica primária mostra se o comportamento desejado melhorou. Guardrails mostram se velocidade, erros, qualidade de receita, suporte ou confiança pioraram.
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,
}),
);
}
No checkout, acompanhe 5xx, falhas de pagamento e tickets. Em blog monetizado, não olhe só cliques affiliate: veja leitura completa, bounce, Core Web Vitals e cliques com intenção paga. Em recursos de IA, acompanhe custo de tokens, latência p95 e cotas por usuário. Mais cliques com pior qualidade comercial não é vitória.
Falhas concretas
A primeira falha é sortear aleatoriamente em cada carregamento. Se o usuário recarrega e muda de A para B, dados de exposição e conversão ficam ruins. Use targeting key estável.
A segunda é esconder premium só no cliente. Um botão ausente no React não protege a API. Feature flag orienta UX, mas não substitui autorização.
A terceira é default inseguro. Uma release flag desconhecida normalmente deve voltar false. Se um typo vira true, você lançou sem querer.
A quarta é nunca remover flags temporárias. Meses depois, checkout_v2_release vira uma ramificação que ninguém entende. Quando a decisão sair, abra o PR de cleanup.
A quinta é aninhar regras demais. Parent flags, child flags e porcentagens sobrepostas tornam difícil explicar quem realmente vê a funcionalidade.
Prompts seguros para Claude Code
Claude Code pode ler arquivos, editar código e executar testes. Dê limites e verificação desde o início.
Adicione um workflow de feature flags a este repositório.
A primeira flag é checkout_v2_release para rollout gradual.
Restrições:
- Billing e autorização são avaliados no servidor.
- Release flags desconhecidas retornam false.
- Rollout percentual usa targetingKey estável.
- O registry inclui owner e removeAfter.
- Não modifique arquivos não relacionados.
Saída esperada:
- Registry mínimo e função evaluateFlag
- Tipo de evento de exposição
- Pelo menos 3 casos de produto
- Exemplos de falha e passos de rollback
- Comandos de teste executados
Antes do merge, use um prompt de review:
Revise esta implementação de feature flags.
Foque em defaults, fronteira servidor/cliente, bucketing estável,
eventos de exposição, datas de cleanup e comportamento de rollback.
Liste achados por severidade com arquivos exatos.
Com essa estrutura, Claude Code tende a produzir um sistema operável, não só um toggle decorativo.
Cleanup também faz parte do release
Toda flag começa a envelhecer no dia em que nasce. Release flags somem depois do rollout completo. Experiment flags somem quando o vencedor é escolhido. Kill switches podem ficar, mas precisam de owner, runbook e alertas. Inclua owner, removeAfter, métricas e PR de remoção planejado no template de pull request.
Verifiquei o evaluator deste artigo como demo TypeScript executável: o mesmo targetingKey cai no mesmo bucket, flags desconhecidas retornam fallback seguro e o kill switch tem off value explícito. A nota prática de Masa em conteúdo monetizado é medir qualidade além de cliques. Comece com uma release flag, uma experiment flag e um kill switch antes de adotar uma plataforma completa.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.