O que é feature flag: guia prático para desenvolvimento

Resumo

Feature flags são condicionais que decidem em tempo de execução qual comportamento ativar, sem novo deploy. Elas permitem merge seguro de código inacabado, rollouts graduais e fallback rápido. Mas 75% das flags viram dívida técnica. Este guia cobre os quatro tipos, limpeza eficiente e quando pular flags.

Mesa de engenheiro com uma fileira de interruptores de alternância física ao lado de um laptop

O que é feature flag? É um condicional no seu código que decide em tempo de execução se um comportamento específico está ativado ou desativado, sem novo deploy. Essa é a ideia principal.

Na prática, uma feature flag é uma instrução if cuja resposta vem de uma configuração, uma linha de banco de dados ou um serviço de flags, em vez de estar hardcoded.

O conceito é simples. Viver com 200 delas em um repositório não é, e essa segunda parte é realmente sobre o que este guia trata.

Como é uma feature flag no código?

Aqui está a versão mais simples e útil:

if (flags.isEnabled("new-checkout", { userId })) {
  return renderNewCheckout();
}
return renderOldCheckout();

Os dois caminhos de código estão no mesmo build. O valor da flag vive em algum lugar que você pode alterar sem fazer redeploy: uma variável de ambiente, um arquivo JSON, uma tabela ou um serviço hospedado. Mude o valor e o comportamento muda na próxima avaliação.

Essa separação é o ponto. Deploy é colocar código nos servidores. Release é deixar os usuários o verem. Flags separam esses dois eventos, então fazer merge para a main não significa mais "todos recebem isso agora".

Mão ligando um único interruptor de alternância com indicador de luz verde

Por que as equipes usam feature flags?

Três razões aparecem continuamente em equipes reais de 5 a 50 engenheiros.

Nenhuma dessas requer um vendor. Uma flag pode ser um booleano em uma tabela de configuração. Pule a plataforma até precisar de rollouts percentuais, audit trails ou não-engenheiros toggling coisas.

Quais são os quatro tipos de feature flags?

O amplamente citado artigo sobre feature toggles de Pete Hodgson no site de Martin Fowler classifica flags por quanto tempo vivem e com que frequência a decisão muda. As categorias continuam sendo o modelo mental mais claro.

A coluna de lifespan importa mais. Uma release flag que sobrevive ao seu release é um bug que você ainda não encontrou. Uma permission flag que é deletada em um sprint de cleanup é uma falha que você ainda não teve.

Nomeie e rotule o tipo quando criar a flag. Seis meses depois, ninguém se lembra de qual categoria "new-nav-v2" era.

Grade de planejamento de equipe com anotações agrupadas por categoria

Como fazer rollout de uma flag sem prejudicar usuários?

A sequência chata funciona melhor.

  1. Coloque o código com a flag desativada. Verifique se nada mudou.

  2. Ative para seu próprio time em produção. Use por um dia.

  3. Ative para 1% a 5% dos usuários, sticky por user ID para que ninguém alterne entre variantes durante a sessão.

  4. Observe taxa de erro, latência e uma métrica de negócio. Decida o threshold antes de começar, não enquanto olha para um dashboard.

  5. Aumente para 100%, espere um período definido, então remova a flag.

A etapa cinco é a que as equipes pulam. Vamos chegar a por que isso custa mais do que você pensa.

Uma nota prática sobre avaliação: faça leitura de flags barata e fail-safe. Se o serviço de flags está down, seu código precisa de um default. Escolha o default por flag, de propósito. Um kill switch deve falhar para "seguro", enquanto uma nova feature deve falhar para "off".

Por que feature flags se tornam dívida técnica?

Cada flag é um fork no seu código. Duas flags fazem quatro caminhos possíveis, dez flags fazem 1.024, e você quase certamente testou um punhado deles. O guia de dívida técnica de flags da GrowthBook cita pesquisas mostrando que cerca de 75% dos componentes de toggle ainda estavam em codebases até 49 semanas após sua introdução, mesmo que a maioria dos desenvolvedores dissesse que planejava removê-los.

Hodgson coloca bem no mesmo artigo de Fowler: equipes experientes tratam toggles como inventário com custo de carregamento, e trabalham para manter esse inventário baixo.

A história clássica de horror é Knight Capital em 2012. Uma flag de feature aposentada foi reutilizada para novo comportamento enquanto código antigo ainda estava em um servidor. Esse descompasso contribuiu para aproximadamente $460 milhões em perdas em menos de uma hora. Sua flag obsoleta provavelmente não fará isso. Ela fará algo mais silencioso: um refactor que quebra um branch que ninguém sabia que existia, ou um novo hire gastando uma tarde descobrindo qual de dois caminhos de checkout é real.

Prateleira empoeirada com caixas esquecidas e interruptores antigos

Como você encontra cada flag em um codebase existente?

Essa é a pergunta que os docs do vendor pulam, e é onde a maioria das equipes fica presa. O dashboard de flags diz o que está configurado. Ele não diz onde cada flag é lida no código, ou se o caminho de código atrás de uma flag "off" ainda é alcançável.

Comece com a abordagem barata:

# toda chave de flag literal lida via seu wrapper
rg -n 'isEnabled\("' src/ | sort

# flags definidas mas nunca referenciadas
comm -23 <(jq -r 'keys[]' flags.json | sort) \
         <(rg -o 'isEnabled\("([^"]+)"' -r '$1' src/ | sort -u)

Isso desmorona rápido. Chaves de flag construídas a partir de concatenação de strings não aparecem em grep. Flags passadas através de funções helper escondem seus call sites. Monorepos e setups multi-repo multiplicam o problema, porque a mesma chave pode ser lida por três serviços.

É aí que ler o código com tooling ajuda. Ferramentas de busca de código que entendem seu repo podem responder "onde new-checkout é avaliado, e o que depende do resultado?" em uma query em vez de uma tarde de grep. Cursor e GitHub Copilot lidam com isso razoavelmente dentro de um repo. Entre vários repos, você precisa de um index que abranja todos eles, que é o caso que construímos codebasechat para.

Como é um processo sane de limpeza de flags?

Trate a remoção como parte do trabalho, não como uma tarefa para depois.

Análise estática ajuda aqui também. Uma ferramenta como CodeScene pode mostrar quais arquivos carregam a lógica condicional mais emaranhada, que é geralmente onde flags antigas se aglomeram. SonarQube marca código inalcançável e morto depois que você remove um check.

Como você testa código que fica atrás de uma flag?

Testes são a parte que ninguém orça. Com dois caminhos por flag, sua suite de testes precisa cobrir ambos, pelo menos para as flags que guardam comportamento arriscado.

Mantenha prático. Teste os estados on e off de cada release flag em testes unitários, injetando o valor da flag em vez de ler um serviço ao vivo. Execute uma suite end-to-end contra a configuração de produção padrão, porque é isso que os usuários recebem hoje. Depois execute um segundo passe com a flag on para a feature que você está prestes a lançar.

Não tente testar cada combinação. Com dez flags você não consegue. Em vez disso, mantenha flags independentes: uma flag que muda comportamento apenas quando outra flag também está on é um design smell, e é a primeira coisa a desemaranhar.

O que novos engenheiros entendem mal sobre flags?

Juniores tendem a cometer os mesmos três erros, e cada um é barato de prevenir em review.

As duas primeiras semanas de um novo hire são exatamente quando eles encontram flags antigas sem owner. Um registro de flags curto, com um tipo, um owner e uma data de remoção para cada entrada, economiza várias horas de perguntar por aí.

Quando você deve pular feature flags?

Flags não são gratuitas, então pule-as quando:

E use-as sem hesitação quando uma mudança é arriscada, voltada para o usuário e difícil de reverter redeploy. Fluxos de pagamento, mudanças de auth e qualquer coisa com uma migração de dados atrás dela se qualificam.

O que realmente faríamos em um time de dez

Comece com um booleano em config e uma função wrapper única, para que cada leitura de flag passe por um lugar. Esse ponto único de estrangulamento torna a lista de flags greppable, auditável e fácil de migrar para um serviço hospedado depois.

Rotule cada flag por tipo, dê a ela um owner e abra o ticket de remoção no dia um. Revise a lista mensalmente por dez minutos. Delete as flags que estão em 100% por duas semanas.

Uma feature flag é um empréstimo. Pegue-a quando economizar um release arriscado, e a pague antes que os juros apareçam no seu próximo refactor.

Perguntas frequentes

O que é feature flag e qual é seu propósito principal?
Uma feature flag é um condicional no código que controla qual comportamento ativar em tempo de execução, sem necessidade de novo deploy. Seu propósito é separar o momento de deploy do momento de release, permitindo controle fino sobre qual funcionalidade vai ao ar.
Qual é a diferença entre os quatro tipos de feature flags?
Release flags vivem dias a semanas e são para esconder features inacabadas. Experiment flags vivem semanas para A/B testes. Ops flags vivem horas a sempre para kill switches. Permission flags vivem meses a anos para controle de acesso. A duração e frequência de mudança definem o tipo.
Por que feature flags se tornam dívida técnica?
Cada flag adiciona um caminho de código único. Dez flags criam 1.024 combinações possíveis de testes. Pesquisas mostram que 75% das flags nunca são removidas após 49 semanas. Sem processo rigoroso de cleanup, elas acumulam e aumentam a complexidade e riscos de refactor.
Como encontrar todas as feature flags em um repositório existente?
Comece com grep ou ripgrep para encontrar chamadas literais `isEnabled()`. Isso quebra com concatenação de strings ou passagem por funções. Ferramentas avançadas de busca de código, como Cursor ou GitHub Copilot em um repo, conseguem mapear todas as leituras de flag com mais precisão.
Qual é o processo recomendado para fazer rollout seguro de uma flag?
O processo é: 1) Deploy com flag off e verifique; 2) Ative para seu time em produção por um dia; 3) Ative para 1-5% dos usuários (sticky por user ID); 4) Monitore taxa de erro, latência e métricas de negócio; 5) Escale para 100% e remova a flag após período definido.
Quando devo usar feature flags e quando devo evitá-las?
Use flags para mudanças arriscadas, user-facing ou difíceis de reverter (pagamentos, auth, migrações de dados). Evite-as para mudanças pequenas, reversíveis em um deploy, ou que toquem schema de banco de dados de forma que a flag não consiga esconder. Se seu time não tem processo de removal, corrija isso primeiro.
Como garantir que features flags não se tornem uma bagunça no código?
Label cada flag por tipo. Defina um owner e data de expiração no dia um. Revise a lista mensalmente. Delete flags em 100% após duas semanas. Use testes com time bombs para flags de release. Mantenha um cap no número total de flags ativas. Leia flags por um único wrapper para que sejam greppable.
Qual é o papel de testes em código protegido por feature flags?
Com dois caminhos de código por flag, sua suite de testes precisa cobrir ambos, especialmente para flags com comportamento arriscado. Teste on/off em testes unitários injetando o valor da flag. Execute end-to-end contra produção padrão (hoje). Execute novo passe com flag on (amanhã). Mantenha flags independentes.