Feature flags em Java: como ativar funcionalidades sem reiniciar a aplicação

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Feature flags em Java: como ativar funcionalidades sem reiniciar a aplicação

\n\n

Imagine: você passou uma semana desenvolvendo uma nova funcionalidade — um formulário de contato com preenchimento automático mágico. Fez o merge, implantou em produção. E então — um bug. Os usuários não conseguem enviar mensagens. Você precisa reverter o commit às pressas, recompilar o JAR, reiniciar o servidor. Todos odeiam esse momento.

\n\n

E se você pudesse simplesmente apertar um botão "desligar" diretamente no painel de administração, sem tocar no código? Ou ativar a funcionalidade apenas para 10% dos usuários para testar a carga? Ou iniciar um teste A/B onde metade vê o novo design e a metade vê o antigo?

\n\n

Tudo isso é a mágica dos feature flags (também conhecidos como feature toggles). Neste artigo, vamos explorar como implementá-los em Java — desde um Enum primitivo até soluções industriais como Togglz e LaunchDarkly. Vamos nessa!

\n\n

O que é um feature flag e por que você precisa dele?

\n\n

Feature flag (ou feature toggle) é um interruptor condicional no código que permite ativar ou desativar funcionalidades sem alterar o código-fonte e sem reimplantar a aplicação.

\n\n

Parece um simples if? Mas o poder está no gerenciamento. Você armazena o estado das flags em um arquivo de configuração, banco de dados ou serviço externo. Altera o valor — a funcionalidade é ativada ou desativada instantaneamente.

\n\n

Para que isso serve em projetos Java?

\n\n
    \n
  • Trunk-based development — faça merge de funcionalidades inacabadas na main, escondendo-as atrás de uma flag. Colegas não verão bugs, e você termina tranquilamente.
  • \n
  • Canary releases — ative a funcionalidade para 5% dos usuários, veja as métricas, depois para 50%, depois para todos.
  • \n
  • Kill switch — se a funcionalidade falhar em produção, você simplesmente a desliga com um botão.
  • \n
  • Teste A/B — compare o comportamento dos usuários com e sem a funcionalidade.
  • \n
  • Personalização — ative funcionalidades premium apenas para clientes pagantes.
  • \n
\n\n

O principal risco é a dívida técnica. Se você não remover flags antigas, o código se transforma em uma teia de if-else. Também falaremos sobre isso.

\n\n

A maneira mais simples: Enum + Properties

\n\n

Vamos começar com a abordagem básica. Criamos uma enumeração de flags e as lemos do application.properties.

\n\n
public enum FeatureFlag {\n    NEW_CHECKOUT_FLOW,\n    AI_RECOMMENDATIONS,\n    DARK_MODE\n}
\n\n

Carregamos as flags em um serviço:

\n\n
@Component\npublic class FeatureFlagService {\n\n    private final Map<FeatureFlag, Boolean> flags = new HashMap<>();\n\n    public FeatureFlagService(@Value("${features.new-checkout-flow:false}") boolean newCheckout,\n                              @Value("${features.ai-recommendations:false}") boolean aiRec,\n                              @Value("${features.dark-mode:false}") boolean darkMode) {\n        flags.put(FeatureFlag.NEW_CHECKOUT_FLOW, newCheckout);\n        flags.put(FeatureFlag.AI_RECOMMENDATIONS, aiRec);\n        flags.put(FeatureFlag.DARK_MODE, darkMode);\n    }\n\n    public boolean isEnabled(FeatureFlag flag) {\n        return flags.getOrDefault(flag, false);\n    }\n}
\n\n

Uso no código:

\n\n
if (featureFlagService.isEnabled(FeatureFlag.NEW_CHECKOUT_FLOW)) {\n    // novo fluxo de checkout\n} else {\n    // fluxo antigo\n}
\n\n

Prós: simplicidade, sem dependências extras. Contras: para alterar o valor, é necessário reiniciar a aplicação. Não é exatamente "sem reiniciar", mas para projetos pequenos, é aceitável.

\n\n

Flags dinâmicas: banco de dados ou Redis

\n\n

Se você quiser alterar flags em tempo real — armazene-as em um banco de dados ou Redis. Vamos criar uma tabela:

\n\n
CREATE TABLE feature_flags (\n    name VARCHAR(100) PRIMARY KEY,\n    enabled BOOLEAN NOT NULL DEFAULT FALSE,\n    updated_at TIMESTAMP\n);
\n\n

Serviço com cache (para não sobrecarregar o banco a cada requisição):

\n\n
@Service\npublic class DynamicFeatureFlagService {\n\n    private final JdbcTem                

Blogs

Book Recommendations