Feature flags em Java: como ativar funcionalidades sem reiniciar a aplicação
\n\nImagine: 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\nE 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\nTudo 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\nO que é um feature flag e por que você precisa dele?
\n\nFeature 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\nParece 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\nPara 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
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\nA maneira mais simples: Enum + Properties
\n\nVamos começar com a abordagem básica. Criamos uma enumeração de flags e as lemos do application.properties.
\n\npublic enum FeatureFlag {\n NEW_CHECKOUT_FLOW,\n AI_RECOMMENDATIONS,\n DARK_MODE\n}\n\nCarregamos 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\nUso no código:
\n\nif (featureFlagService.isEnabled(FeatureFlag.NEW_CHECKOUT_FLOW)) {\n // novo fluxo de checkout\n} else {\n // fluxo antigo\n}\n\nPró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\nFlags dinâmicas: banco de dados ou Redis
\n\nSe você quiser alterar flags em tempo real — armazene-as em um banco de dados ou Redis. Vamos criar uma tabela:
\n\nCREATE TABLE feature_flags (\n name VARCHAR(100) PRIMARY KEY,\n enabled BOOLEAN NOT NULL DEFAULT FALSE,\n updated_at TIMESTAMP\n);\n\nServiço com cache (para não sobrecarregar o banco a cada requisição):
\n\n@Service\npublic class DynamicFeatureFlagService {\n\n private final JdbcTem