Feature flag não é gambiarra. É seguro contra deploy irreversível.
TL;DR
- Feature flag é mecanismo de controle de exposição, não débito técnico: separa o ato de deploy (colocar código em produção) do ato de release (expor a feature ao usuário).
- Existem três tipologias com ciclo de vida e donos distintos — release, operação e experimentação — e confundi-las é o que produz entulho, não a flag em si.
- Rollback lógico via flag desliga a feature em produção em milissegundos, sem `git revert`, sem janela de manutenção e sem rollback de banco.
- O risco real são flags abandonadas: criadas para duas semanas, fixadas em 100% e nunca removidas. A remoção tardia degrada o sistema, não a presença da flag.
- Sem dono nomeado, prazo de remoção e regra de criação, a flag vira `if feature_enabled` espalhado. O mecanismo só funciona sob contrato social explícito.
~ 14 min de leitura · 2357 palavras
O deploy não é o release. O que essa frase significa na prática.
Deploy e release são verbos diferentes, ainda que a cultura de engenharia costume tratá-los como sinônimos. Deploy é a promoção de um artefato para um ambiente executável: o binário está em produção, o pod está rodando, a rota responde. Release é a exposição deliberada daquela mudança ao usuário final. Quando as duas operações andam acopladas no mesmo `git push` para a branch principal, o time compra, sem perceber, um seguro que não pediu: o rollback vira `git revert` + novo deploy + migração reversa, se houver, e janela de manutenção.
A frase descreve uma assimetria operacional concreta. O DORA Report, mantido pelo Google Cloud em parceria com a Puppet, trata deploy e release como variáveis independentes no cálculo de *delivery performance* — times *elite* chegam a dezenas de deploys por dia sem que isso signifique dezenas de releases. O desacoplamento é o que permite reverter uma exposição ruim em segundos, sem reescrever uma linha, sem tocar em pipeline.
# Flag release como configuração, não como branch
features:
checkout_v2:
enabled: true
rollout_percent: 0
cohort: internal_only
owner: squad-checkout
expires_at: 2026-09-30
Quando a flag existe como configuração — e não como branch de longa duração — o merge para `main` deixa de ser uma decisão de exposição. A exposição passa a ser alteração de chave em um serviço de configuração (LaunchDarkly, Unleash, Split.io, ou um arquivo versionado quando o orçamento é curto). O `git revert` cobre bug de código; a flag cobre decisão de exposição. São duas ferramentas para duas falhas diferentes. Tratar a flag como gambiarra é confundir as duas e perder o segundo mecanismo.
Cloudflare, Shopify e GitHub publicam postmortems em que a mitigação inicial raramente é revertida por commit novo. Em incidentes de billing, de roteamento ou de feature mal calibrada, o botão de desligar precede o hotfix. A diferença entre um incidente de 5 minutos e um de 5 horas costuma estar aí, não na qualidade do `git revert`.
As três tipologias da flag: release, operação e experimentação.
A expressão “feature flag” é usada como guarda-chuva para três mecanismos com pouco em comum. O Framework das Três Tipologias da Flag deste blog separa as finalidades em três tipos, cada um com dono, prazo e ciclo de remoção próprios.
| Tipologia | Função | Exemplo típico | Dono | Remoção |
|---|---|---|---|---|
| Release | Controlar exposição de feature em validação em produção | novo checkout em 5% dos usuários | squad da feature | após 100% de adoção estável |
| Operação | Ligar e desligar comportamento técnico já em produção | kill switch de integração com provedor | engenharia de plataforma | quando o provedor provê o próprio controle |
| Experimentação | Infraestrutura para A/B test com métrica de negócio | duas variantes de CTA por conversão | produto + dados | ao fim do teste, com leitura de resultado |
Misturar as três tipologias produz os piores efeitos. Uma flag de release usada como kill switch carrega prazo de remoção incompatível com a urgência do segundo uso. Uma flag de experimentação mantida além do ciclo do teste começa a enviesar a métrica. A primeira coisa ao criar uma flag é declarar a tipologia — declaração que não muda com o tempo. Uma flag de release que vira operação exige novo cadastro, novo dono e nova expiração.
A tipologia também determina a *forma* da flag. Flags de release aceitam segmentação por usuário, cohort, percentual, geografia. Flags de operação costumam ser booleanas globais, com latência mínima, em cache distribuído. Flags de experimentação exigem consistência forte de atribuição: o mesmo usuário precisa ver a mesma variante nas duas requisições.
Sem essa distinção, o time acumula flags que se parecem no código — todas lidas via `featureFlag(“nome”)` — mas exigem governança diferente. O resultado é que ninguém sabe o que está protegido, o que está em teste, ou o que deveria ter sido removido há seis meses.
Rollout progressivo: percentual, cohort e canário. Quando cada um.
Expor 100% de uma feature nova de uma vez é, em produção com tráfego real, o mesmo que apostar a estabilidade do sistema em um único lançamento. O rollout progressivo existe para transformar a exposição em função contínua, e há três formas canônicas de implementá-lo.
Percentual simples distribui a feature por hash de identificador: 1%, 5%, 25%, 100%. É a forma mais barata, exige apenas hash determinística, e serve para detectar regressões de latência ou erro em uma fração pequena do tráfego. O problema aparece quando a feature depende de região, plano ou segmento: 1% global pode pegar 1% de cada plano, ou 100% do plano premium por azar do hash. Em SaaS B2B, é desastre. Em consumer com tráfego massivo, é ruído aceitável.
Cohort restringe a exposição a um subconjunto definido: usuários internos, plano enterprise, geografia específica, contas com mais de 90 dias de tenure. É a forma correta quando a feature tem custo não-linear, depende de configuração de conta, ou expõe dados que ainda não foram auditados por jurídico. Stripe, Shopify e Atlassian tratam cohort como mecanismo padrão para features que tocam billing.
Canário mantém a feature em 100% de um subconjunto deliberadamente escolhido (uma região, um cluster Kubernetes, um canary deployment no ArgoCD) e deixa a outra fatia como controle. É o mais sofisticado: exige métrica de comparação entre canário e controle, e exige que o time esteja disposto a ler a métrica antes de expandir. O canário é caro e lento, mas é o único que detecta regressões de métrica de negócio — não apenas de erro e latência, que o percentual pega.
A escolha entre os três não é estética. O Framework do Ciclo de Vida da Flag que organiza este blog trata a tipologia da exposição como decisão tão importante quanto a tipologia da flag: percentual serve para release técnico, cohort serve para release de produto, canário serve quando a feature muda comportamento de receita.
# Pseudocódigo de rollout por cohort com fallback para percentual
if user in cohort("enterprise", region="BR"):
return feature.checkout_v2()
elif random_hash(user.id) < current_rollout_percent:
return feature.checkout_v2()
return feature.checkout_v1()
O erro recorrente é tratar o canário como se fosse percentual: expandir a 100% porque “não quebrou”, sem comparar métrica de negócio. Quebrar uma feature em produção e dobrar conversão são, para o sistema, indistinguíveis até que alguém olhe o gráfico de comparação. O mecanismo existe; a leitura é separada.
Rollback lógico: a feature morre em produção sem reescrever código.
O `git revert` é a ferramenta certa para bug de código. Para bug de exposição — a feature está funcionando, mas exposta para o público errado, ou com impacto de negócio negativo — o `git revert` introduz custo desproporcional: novo commit, novo build, novo deploy, nova janela de monitoramento, e a feature continua existindo no HEAD com o caminho antigo comentado.
O rollback lógico via feature flag desliga a exposição sem tocar em código. A flag vai de `true` para `false`, a feature deixa de ser servida, o caminho antigo passa a ser o único atendido, e o log de auditoria do serviço de flag registra a mudança. A operação leva o tempo de uma chamada de API: em providers como LaunchDarkly ou Unleash, o toggle propaga em menos de 200ms para uma frota global. Em self-hosted, o limite prático é a invalidação de cache, raramente acima de 1 segundo.
O efeito é imediato. A feature morre em produção sem ninguém reescrever código. Nenhum deploy novo é necessário. Nenhum pod é reiniciado. Nenhuma migração de banco precisa ser revertida — porque a feature não toca schema quando desenhada para ser flagável. O dado persistido continua íntegro; o que muda é a porta de entrada que serve o usuário.
# Kill switch via flag de operação
if feature_flag("billing_provider_x", default=True):
charge_via_provider_x(order)
else:
charge_via_provider_y(order) # fallback já em produção
Esse desenho é o que separa feature flag de “variável de ambiente booleana”. A flag precisa ser *hot* — leitura em cada request, sem cache agressivo, com propagação em segundos. Variável de ambiente exige redeploy para mudar. A diferença operacional é o que define se o mecanismo serve como rollback lógico ou apenas como documentação de intenção.
Em postmortem público da Cloudflare em 2017, parte da mitigação inicial foi desativar regras de WAF problemáticas via toggle, sem deploy. No incidente de billing da Shopify em 2019, a primeira ação também foi desligar a feature no serviço de flag, antes do fix de código chegar. O padrão se repete porque o mecanismo foi desenhado para isso: tornar a decisão de exposição reversível no tempo de uma decisão, não no tempo de um deploy.
A regra operacional é direta: se a feature toca dados persistidos em produção, o rollback lógico é o primeiro recurso, não o último. Hotfix de madrugada para desligar uma feature deveria ser sinal de que a flag de operação não existia — não de que o time é lento.
O risco real são as flags abandonadas.
A objeção legítima contra feature flag não é “é gambiarra”. É “o time acumula flag e nunca remove”. A flag abandonada é o equivalente técnico de variável global: a leitura continua ali, ninguém sabe quem é o dono, ninguém sabe se pode desligar, o toggle fica em `true` por inércia, e seis meses depois o caminho antigo virou caminho crítico sem ninguém ter decidido conscientemente.
A flag abandonada drena o sistema de três formas. Acumula custo de leitura: cada request passa pela função de avaliação, cada cache carrega a chave. Esconde intenção: a feature que era para duas semanas virou permanente, e o time que entrou depois não sabe se a flag protege algo ou se é herança. Corrompe o rollout: o percentual de 100% que ninguém atualiza vira teto, e a próxima flag com nome parecido gera sobreposição silenciosa.
O Framework do Ciclo de Vida da Flag que organiza este blog propõe quatro fases com data de saída explícita: nascimento, ramp-up, generalização e remoção. A data de saída é definida na criação, registrada no cadastro da flag, e revisada em review quinzenal. Sem data de saída, a flag não deveria ser criada — o mesmo princípio que se aplica a TODO sem prazo em código.
A remoção tem dois estágios. Primeiro, a flag sai da configuração e a feature passa a ser o caminho único; o `if feature_enabled(…)` vira apenas o caminho novo, o caminho antigo é deletado. Segundo, o serviço de flag revoga a chave, e qualquer leitura residual passa a retornar o valor default — geralmente `false`, o que é seguro para flag de release e perigoso para flag de operação (por isso o default correto é parte do desenho).
O custo de remover a flag é desproporcional ao custo de mantê-la. O post sobre Onboarding clean code dogmático: o custo que o PR não pega neste blog trata o mesmo erro em outra superfície: o dogma de “remover cedo” ignora que o custo de remover só aparece no momento de remover, e o momento certo é quando o ciclo da feature permite, não quando o dogma de limpeza exige. O paralelo é direto: a flag deve ser removida quando a feature completa seu ciclo de adoção, não antes, não depois.
O sinal prático de flag abandonada é simples: rodar `grep -r “feature_flag(” src/` e encontrar chaves sem dono listado. Cada chave sem dono é uma feature que pode ser desligada por acidente, ou uma feature que ninguém lembra que existe, ou — pior — uma feature que sustenta um caminho crítico que ninguém se lembra de manter.
Quem é o dono da flag. O contrato social que sustenta o mecanismo.
Feature flag é, antes de ferramenta, contrato social entre três papéis: quem cria a feature, quem opera a plataforma, e quem responde pelo produto. Sem os três papéis conscientes, a flag vira artefato órfão.
Quem cria a feature é o squad ou engenheiro que implementa o código. Esse papel define a tipologia, o percentual inicial, e — crucialmente — a data de expiração. A data não é arbitrária: é a data em que, no melhor cenário, a feature atingirá 100% de adoção estável. Sem essa data, a flag nasce sem prazo de saída.
Quem opera a plataforma é a engenharia de plataforma, SRE ou DevOps que mantém o serviço de flag disponível, com latência baixa, propagação rápida e auditoria. Esse papel é o guardião do tempo de propagação — se o toggle leva 30 segundos para chegar a 100% da frota, o mecanismo deixa de servir como rollback lógico. O DORA Report correlaciona lead time for changes com disponibilidade do serviço de flag: quanto mais rápido o time detecta e reverte, mais rápido o time aprende.
Quem responde pelo produto é o PM ou tech lead que decide se a exposição continua, acelera, desacelera ou reverte. Esse papel consome a métrica de impacto da flag e decide a próxima ação. Sem essa leitura, a flag muda de percentual só por default — o que não é decisão, é inércia.
O contrato entre os três papéis tem três cláusulas. A cláusula de criação: toda flag nasce com tipologia, dono, percentual inicial e data de expiração registrados. A cláusula de revisão: a cada duas semanas, o cadastro é revisado, flags próximas do vencimento são decididas. A cláusula de remoção: ao chegar na data de expiração, ou ao completar 100% de adoção estável, a flag sai da configuração e o código é limpo.
Sem essas três cláusulas formalizadas em algum lugar — pode ser documento de Confluence, template de PR, regra de CI que barra merge de flag sem `owner:` declarado — o mecanismo degrada. A flag é simples de implementar; o contrato é simples de escrever; o que custa é manter as três cláusulas vivas ao longo de meses.
A flag como ferramenta é tecnologia dos anos 1970, quando toggles em software de telecomunicação permitiam ligar e desligar rotas sem intervenção física. O que mudou desde então foi a escala do contrato social que sustenta o mecanismo. Em times de 5 pessoas, o contrato é informal. Em times de 50 ou 500, o contrato precisa ser explícito ou o mecanismo vira fonte de incidente.
Leitura relacionada
- Pipeline CI/CD complexo: 15 etapas para 23 microsserviços — distingue bug de código de decisão de exposição no pipeline e mostra onde a flag entra como camada extra, não como substituta de CI.
- Onboarding clean code dogmático: o custo que o PR não pega — paralelo direto sobre o erro de aplicar “remover cedo” como dogma, o mesmo que faz a flag morrer antes da hora.
- Docker e Kubernetes não são DevOps: complicação disfarçada — flag opera em camada diferente de plataforma: blue-green troca a infraestrutura inteira, flag troca o caminho do código.

2 Comentários