en
· 4 min de leitura

Feature flags como arquitetura

Uma flag é um branch do seu sistema que vive em produção. Trate como tal, ou ela vira o bug que ninguém consegue reproduzir.

fullstackengineering

Uma feature flag é um condicional que vive em produção e pode mudar sem deploy. É uma coisa poderosa de ter, e todo time com o qual trabalhei ficou feliz de ter adicionado flags. Também é um segundo fluxo de controle, invisível no código, que decide o que o seu sistema faz. A maioria dos times trata como conveniência. É arquitetura.

A diferença aparece na auditoria. Uma base de código com quarenta flags, metade delas permanentemente ligadas, três referenciadas em código que não existe mais, e uma que desliga retries de pagamento e está ligada desde um incidente dois anos atrás. Ninguém sabe qual combinação de flags a produção está rodando. O bug que só acontece para alguns usuários não é uma condição de corrida. É uma flag.

Flags têm tipos

A primeira disciplina é admitir que nem toda flag é a mesma coisa. Uma flag de release esconde trabalho inacabado e deve viver semanas. Uma flag de experimento divide tráfego e deve viver até o experimento concluir. Uma flag operacional é um kill switch para uma dependência e deve viver para sempre. Uma flag de permissão libera uma feature por cliente e é na verdade um direito de produto, não uma flag.

Misturar é onde começa o problema. Uma flag de release que nunca é removida vira uma flag operacional acidental. Um direito de produto modelado como flag booleana vira algo impossível de cobrar. Eu nomeio flags com o tipo como prefixo e dou a cada tipo uma regra de ciclo de vida diferente.

Avalie uma vez, na borda da requisição

O erro de implementação mais comum é consultar o serviço de flags em todo lugar. No fundo de uma função de domínio, em um componente React, em um worker de fila. Cada consulta é uma chamada de rede ou uma leitura de cache, e cada uma pode ver um valor diferente se a flag mudar no meio da requisição. Metade de um checkout roda com o preço novo e metade com o antigo.

A regra que imponho: flags são avaliadas uma vez, no ponto de entrada, em um objeto simples de booleanos e variantes que desce junto com a requisição. No Next.js isso significa ler as flags no layout ou na Server Action, uma vez, e passar o resultado como argumento ou por contexto. O código de domínio recebe uma decisão, não um cliente de flags. Fica testável sem mockar serviço, e a requisição é consistente do início ao fim.

Para Server Components, esse objeto também é o que você serializa para as partes de cliente que precisam dele. O cliente nunca chama o serviço de flags. Ele recebe as decisões que o servidor já tomou, o que também significa que o bundle do cliente não contém um SDK de flags e seus segredos.

Flags mudam a chave do cache

Uma página renderizada com a flag A ligada e cacheada é uma página diferente da mesma rota com a flag A desligada. Se a chave do cache não inclui o estado da flag, um usuário no experimento vê a página de controle vinda do cache, e os resultados do experimento são ruído.

Essa é a parte que todo mundo esquece. Toda flag que muda o que uma rota renderiza faz parte da identidade de cache daquela rota, no cache do framework e no CDN. Ou o estado da flag está na chave, ou a rota não é cacheada. Não existe terceira opção, e já vi experimentos rodarem por um mês com dados sem sentido por causa disso.

Remoção faz parte da feature

Uma flag não está pronta quando a feature é lançada. Está pronta quando a flag é deletada e o branch perdedor sumiu. Coloco o ticket de remoção no mesmo pull request que adiciona a flag, com data, e faço o CI falhar em qualquer flag de release com mais de noventa dias. Parece duro. É o que mantém a contagem em dez em vez de quarenta.

Os kill switches ficam, mas são testados. Uma vez por trimestre, vire cada flag operacional em staging e confirme que o sistema degrada do jeito que o runbook diz. Um kill switch que ninguém tentou desde o incidente que o criou é uma hipótese, não um controle.

Flags valem a pena. Também são um segundo programa rodando ao lado do primeiro, e ele merece um design.