Feature Flags: Como Separar Deploy de Release e Entregar Sem Medo
Feature flags desacoplam colocar código em produção de ativá-lo para o usuário. Entenda os tipos de flag, rollout progressivo, kill switch e como não afundar em dívida técnica de flags.
Neste artigo
Existe uma diferença que muda a forma como um time entrega software, e que a maioria descobre tarde demais: deploy e release não são a mesma coisa. Deploy é colocar o código em produção. Release é torná-lo visível e ativo para o usuário. Quando esses dois eventos acontecem juntos — o código sobe e no mesmo instante está no ar para todo mundo — cada deploy é uma aposta de tudo ou nada. É aí que entram as feature flags.
Uma feature flag (ou feature toggle) é um interruptor no código que decide, em tempo de execução, se um trecho de funcionalidade está ativo ou não, sem exigir um novo deploy para mudar. Com ela, você faz o deploy do código desligado, e depois liga a funcionalidade quando quiser, para quem quiser — e desliga na mesma velocidade se algo der errado. Vamos entender os tipos de flag, os padrões de uso e as armadilhas que fazem times bem-intencionados afundarem em dívida técnica.
O conceito central: o interruptor em runtime#
No código, uma flag é uma condição:
``python if flags.is_enabled("novo_checkout", user=usuario): return renderizar_novo_checkout(usuario) return renderizar_checkout_antigo(usuario) ``
O que muda tudo é de onde vem o valor de is_enabled. Ele não é uma constante compilada; é buscado em runtime de um serviço de configuração de flags. Isso significa que você pode alterar o comportamento da aplicação — para todos, para 10% dos usuários, para um cliente específico — sem tocar no código e sem reimplantar. O mesmo artefato em produção passa a se comportar de forma diferente porque a configuração da flag mudou.
Essa separação entre deploy e release é o que destrava várias práticas modernas: integração contínua de verdade (você mescla código incompleto, protegido por flag), rollout progressivo, testes A/B, e a capacidade de desligar uma funcionalidade problemática em segundos.
Os tipos de flag (e por que a distinção importa)#
Nem toda flag é igual. Uma classificação clássica separa quatro tipos por longevidade e dinamismo, e confundi-los é a origem de boa parte da bagunça.
Release toggles#
São flags temporárias que escondem funcionalidade em desenvolvimento. Permitem mesclar código incompleto na branch principal sem expô-lo. Vivem dias ou semanas — o suficiente para a funcionalidade ficar pronta e ser ativada. Devem ser removidas assim que a feature está 100% no ar. São a fonte número um de dívida quando esquecidas.
Experiment toggles#
Dividem usuários em grupos para testes A/B. Um grupo vê a variante A, outro a B, e você mede qual converte melhor. São dinâmicas por usuário e vivem o tempo do experimento. A decisão de qual variante cada usuário vê precisa ser consistente (o mesmo usuário sempre cai no mesmo grupo) para o experimento ter validade.
Ops toggles#
Controlam aspectos operacionais em produção: ligar/desligar uma funcionalidade pesada sob carga, ativar um caminho de degradação graciosa. O kill switch é o exemplo mais importante — um interruptor para desativar rapidamente algo que está causando problema. Alguns ops toggles são de vida longa, permanecendo como válvulas de controle permanentes.
Permission toggles#
Controlam quais funcionalidades cada usuário ou plano vê — recursos premium, acesso antecipado para beta testers. São de vida longa por natureza, porque fazem parte da lógica de produto, não de entrega. Aqui a "flag" na prática se confunde com autorização e deve seguir as regras de authz (decisão no servidor, nunca confiável no cliente).
A confusão perigosa é tratar um release toggle como se fosse permanente (e nunca removê-lo) ou um permission toggle como temporário. Nomeie e documente o tipo de cada flag desde o nascimento.
Rollout progressivo: ligando aos poucos#
O maior ganho operacional das flags é poder ativar uma funcionalidade gradualmente, observando o impacto antes de expor todo mundo. Em vez de um big-bang, você abre a torneira devagar:
``text Dia 1: novo_checkout = 1% dos usuários -> observa erros, latência Dia 2: novo_checkout = 10% -> métricas saudáveis, segue Dia 3: novo_checkout = 50% Dia 4: novo_checkout = 100% ``
Se, em qualquer etapa, as métricas degradarem — taxa de erro sobe, conversão cai, latência dispara — você volta a flag para o percentual anterior instantaneamente. Isso é um rollback sem deploy: o código problemático continua em produção, mas desligado. Compare com o rollback tradicional, que exige reimplantar a versão anterior e leva minutos ou mais.
O direcionamento pode ir além do percentual: ativar primeiro para usuários internos (dogfooding), depois para um beta, depois por região, por plano, por qualquer atributo. A flag vira um bisturi para controlar exatamente quem é exposto ao novo.
O kill switch: sua rede de segurança#
Toda funcionalidade nova de risco deveria nascer atrás de um kill switch — uma flag cujo único propósito é desligar aquilo rápido. Quando um alerta dispara às três da manhã por causa da feature que subiu ontem, a diferença entre "desligo a flag em dez segundos" e "preciso preparar, revisar e implantar um rollback" é a diferença entre um soluço e um incidente.
``python # Caminho protegido por kill switch com degradação graciosa. if flags.is_enabled("recomendacoes_ml"): try: return servico_ml.recomendar(usuario) except Exception as e: logger.error("recomendacao_ml_falhou", trace_id=trace_id, erro=str(e)) return recomendacoes_padrao() # degrada, não quebra return recomendacoes_padrao() ``
Repare que o kill switch e a degradação graciosa andam juntos: quando a funcionalidade está desligada (ou falha), o sistema cai num comportamento padrão seguro, em vez de quebrar. É a mesma filosofia da independência de serviços — a ausência de um componente degrada a experiência, não derruba o sistema.
A dívida técnica das flags#
Aqui está o lado sombrio. Cada flag adiciona um if no código, e cada if dobra os caminhos possíveis. Um punhado de flags convive bem; dezenas de flags esquecidas transformam o código em um labirinto onde ninguém sabe mais quais combinações são realmente testadas ou usadas. Flags acumuladas são dívida técnica que rende juros compostos.
Os problemas concretos:
- Explosão combinatória: N flags criam até 2^N combinações de estado. Testar
todas é impossível; na prática, você testa só as que imagina, e as outras viram bugs latentes.
- Código morto disfarçado: uma flag que já está em 100% há meses ainda tem o
ramo else com o código antigo, que ninguém remove e que confunde quem lê.
- Flags zumbi: ninguém lembra para que servem, ninguém tem coragem de
removê-las, e elas ficam ali para sempre.
A disciplina que evita isso:
- Toda release toggle nasce com data de validade e um responsável. Se passou
do prazo, vira tarefa de remoção prioritária.
- Remover a flag faz parte de terminar a feature. A funcionalidade só está
"pronta" quando está em 100%, estável, e a flag foi removida junto com o código do caminho antigo.
- Inventário de flags visível. Um painel que lista todas as flags ativas, seu
tipo, idade e estado. Flags velhas demais aparecem em vermelho.
- Limpeza periódica. Reserve tempo de manutenção para caçar e remover flags
mortas, como quem tira o lixo.
Distinguir o tipo ajuda aqui: release toggles têm de morrer; permission e alguns ops toggles são permanentes por design. O problema é quase sempre release toggle que virou permanente por inércia.
Flags e testes: a combinação que precisa ser testada#
Um risco pouco discutido é que feature flags multiplicam os estados que o sistema pode assumir, e isso tem consequência direta para os testes. Se uma funcionalidade está atrás de uma flag, tanto o caminho com a flag ligada quanto o desligado precisam funcionar — e ambos precisam de cobertura. Testar só o estado que está em produção hoje deixa o outro caminho como um campo minado: no dia em que você liga a flag, descobre que aquele ramo nunca foi exercitado.
A prática saudável é testar os dois estados relevantes de cada flag ativa, e resistir à tentação de deixar flags acumularem justamente porque cada uma dobra o espaço de teste. Quando uma release toggle chega a 100% e é removida, o número de combinações diminui e o sistema volta a ser testável de forma exaustiva. É mais um argumento para a disciplina de remoção: flags não são só dívida de leitura de código, são dívida de testabilidade.
Onde as flags são avaliadas, e a questão da confiança#
Uma decisão de arquitetura importante: a avaliação da flag acontece no servidor ou no cliente? Para qualquer coisa que envolva segurança ou autorização (os permission toggles), a resposta é categórica: no servidor. Uma flag que esconde um botão de admin no frontend é só experiência de usuário — se a rota do servidor não reverifica a permissão, um usuário mal-intencionado chama a API direto e contorna o botão escondido. Flag de UI não é controle de acesso.
Para flags de release e experimento sem implicação de segurança, avaliar no cliente é aceitável e às vezes preferível (evita uma ida ao servidor). Mas o valor que decide precisa vir de uma fonte confiável e ser resolvido de forma consistente por usuário.
Começando com o pé direito#
Feature flags são uma dessas ferramentas que resolvem problemas reais e criam outros se usadas sem disciplina. Um caminho pragmático para adotá-las bem:
- Comece com um mecanismo simples de flags — pode ser configuração em runtime
antes de um serviço dedicado — e proteja a próxima funcionalidade de risco atrás de uma.
- Pratique o rollout progressivo já na primeira: 1%, 10%, 50%, 100%, observando
métricas em cada degrau.
- Trate o kill switch como item obrigatório de qualquer feature que possa quebrar
produção.
- Nomeie, documente e datar cada flag desde o commit que a cria.
- Faça da remoção parte da definição de pronto.
O objetivo final não é ter muitas flags — é entregar com segurança. A flag é o meio pelo qual você separa a coragem de implantar da prudência de expor, ligando o novo devagar e podendo voltar atrás num piscar de olhos. Usada com disciplina, ela transforma o deploy de um evento tenso em um não-evento, e é isso que permite a um time entregar muitas vezes ao dia sem prender a respiração a cada push.