Arquitetura de software · Confiabilidade e segurança · 6 min de leitura
Feature flags: separar deploy de lançamento
Um interruptor no código permite publicar uma funcionalidade desligada, ligar para 1% das pessoas, comparar resultados e desligar em segundos se der problema.
Arraste para o lado para ver o diagrama inteiro.
Os quatro tipos
- De lançamento: código incompleto vai para produção desligado (permite trunk-based development). Vida curta.
- De experimento: teste A/B — metade vê a versão nova e medimos o resultado.
- Operacionais: kill switch para desligar algo pesado durante um pico. Podem durar anos.
- De permissão: funcionalidade só para plano pago, beta ou equipe interna.
Lançamento gradual bem feito
- A fatia é determinística: um hash de
flag + id_do_usuariodecide, então a mesma pessoa sempre vê a mesma versão. - Comece pela equipe interna, depois 1%, 10%, 50%, 100% — olhando erros e métricas de negócio a cada passo.
- Avalie a flag no servidor sempre que possível; no app, guarde a última configuração para funcionar offline e com um valor padrão seguro.
- Padrões abertos como o OpenFeature evitam prender o código a um fornecedor.
A dívida das flags
Cada flag dobra os caminhos possíveis do código. Flags esquecidas viram armadilhas — a Knight Capital perdeu US$ 440 milhões em 2012 em parte por reaproveitar uma flag antiga que reativou código morto. Regras práticas: dono e data de validade para cada flag de lançamento, remover o código antigo assim que chegar a 100%, testes para os dois estados das flags ativas e um painel listando as flags velhas.
No código
/** Mesmo usuário, mesma resposta: o hash de flag + id cai sempre no mesmo balde de 0 a 99. */
fun bucket(flag: String, userId: String): Int {
val digest = MessageDigest.getInstance("SHA-256").digest("$flag:$userId".toByteArray())
val n = ((digest[0].toInt() and 0xff) shl 8) or (digest[1].toInt() and 0xff)
return n % 100
}
data class FlagRule(val enabled: Boolean, val percent: Int, val allowList: Set<String> = emptySet())
class Flags(private val rules: () -> Map<String, FlagRule>) {
fun isOn(flag: String, userId: String): Boolean {
val rule = rules()[flag] ?: return false // desconhecida = desligada (padrão seguro)
if (!rule.enabled) return false // kill switch
if (userId in rule.allowList) return true // equipe interna primeiro
return bucket(flag, userId) < rule.percent
}
}
// Remover este if (e o checkout antigo) quando a flag chegar a 100%.
if (flags.isOn("novo-checkout", user.id)) NewCheckout() else OldCheckout() A fatia não depende de sorteio a cada abertura: subir de 10% para 20% mantém os primeiros 10% e acrescenta outros. Flag desconhecida fica desligada.
Em resumo
- Deploy ≠ lançamento.
- Tipos: lançamento, experimento, operacional, permissão.
- Fatia determinística por hash do usuário.
- Kill switch desliga sem novo deploy.
- Remova a flag quando chegar a 100%.
Vantagens e custos
- Lançamentos graduais e reversíveis em segundos.
- Testes A/B com o mesmo código.
- Integração contínua sem ramos longos.
- Caminhos de código multiplicados.
- Flags esquecidas viram dívida e risco.
- Mais uma dependência em tempo de execução.
Perguntar à IA
Monte um prompt pronto sobre Feature flags: separar deploy de lançamento na sua linguagem e no seu caso, e abra num chat gratuito.
O prompt vai para o site que você escolher. Não cole senhas, tokens nem dados reais de clientes. Para Gemini e outros, copie e cole.