Feature flags em Go: quando cada linha de código é um risco
\n\nVocê já acordou às 3 da manhã porque um novo lançamento "matou" a produção? Ou passou horas revertendo mudanças porque uma nova funcionalidade quebrou a lógica da antiga? Familiar? Então feature flags (flags de funcionalidades) é o que vai mudar radicalmente sua vida como desenvolvedor Go.
\n\nFeature flags não são apenas "if-else" no código. É uma filosofia de lançamentos seguros. É a capacidade de ativar e desativar funcionalidades sem deploy, realizar testes A/B e fazer canary releases. No mundo Go, onde velocidade e confiabilidade são as principais prioridades, feature flags se tornam uma ferramenta must-have.
\n\nO que são feature flags e por que são necessárias em Go?
\n\nFeature flag (também conhecido como toggle, flag de funcionalidade) é um mecanismo que permite ativar ou desativar uma funcionalidade específica do aplicativo sem alterar o código e sem um novo deploy. Em palavras simples: você escreve o código, envolve-o em uma condição, e se a flag estiver ativa — o código é executado, se não — é ignorado.
\n\nEm Go, onde a compilação em binário estático e a arquitetura minimalista são o padrão, feature flags resolvem vários problemas de uma vez:
\n\n- \n
- Redução do risco de lançamentos — lançou uma funcionalidade, mas algo deu errado? Basta desativar a flag. Sem reversão, sem novo deploy. \n
- Teste A/B — mostramos a nova versão para apenas 10% dos usuários, comparamos métricas. \n
- Canary releases — ativação gradual da funcionalidade para diferentes grupos de usuários. \n
- Dark launches — o código já está em produção, mas oculto. Permite testar integrações sem impacto nos usuários. \n
- Gerenciamento de dependências — ativamos a funcionalidade somente quando a infraestrutura backend estiver pronta. \n
Como implementar feature flags em Go: do simples ao complexo
\n\n1. A maneira mais simples: constantes e variáveis de ambiente
\n\nVamos começar pelo básico. A opção mais primitiva, mas funcional, é usar constantes globais ou variáveis de ambiente. Adequado para MVP ou quando não é necessária dinamicidade.
\n\npackage main\n\nimport (\n "fmt"\n "os"\n)\n\nconst NewCheckoutFlowEnabled = false\n\nfunc main() {\n envFlag := os.Getenv("FEATURE_NEW_PAYMENT")\n isPaymentEnabled := envFlag == "true"\n\n if isPaymentEnabled {\n fmt.Println("Novo gateway de pagamento ativo")\n processNewPayment()\n } else {\n fmt.Println("Usando gateway de pagamento antigo")\n processOldPayment()\n }\n}\n\nfunc processNewPayment() {\n fmt.Println("Processando via Stripe API v3")\n}\n\nfunc processOldPayment() {\n fmt.Println("Processando via Stripe API v2")\n}\n\nAs desvantagens são óbvias: não há gerenciamento dinâmico, é necessário reiniciar o aplicativo. Mas para cenários simples, serve.
\n\n2. Abordagem estrutural: seu próprio gerenciador de flags
\n\nQuando o número de flags ultrapassa 5, é hora de extrair a lógica para um pacote separado. Vamos criar um gerenciador simples de feature flags com suporte a armazenamento in-memory.
\n\npackage feature\n\nimport (\n "sync"\n "time"\n)\n\ntype Flag struct {\n Name string\n Enabled bool\n UpdatedAt time.Time\n}\n\ntype Manager struct {\n mu sync.RWMutex\n flags map[string]Flag\n}\n\nfunc NewManager() *Manager {\n return &Manager{\n flags: make(map[string]Flag),\n }\n}\n\nfunc (m *Manager) IsEnabled(name string) bool {\n m.mu.RLock()\n defer m.mu.RUnlock()\n flag, exists := m.flags[name]\n return exists && flag.Enabled\n}\n\nfunc (m *Manager) Enable(name string) {\n m.mu.Lock()\n defer m.mu.Unlock()\n m.flags[name] = Flag{\n Name: name,\n Enabled: true,\n UpdatedAt: time.Now(),\n }\n}\n\nfunc (m *Manager) Disable(name string) {\n m.mu.Lock()\n defer m.mu.Unlock()\n m.flags[name] = Flag{\n Name: name,\n Enabled: false,\n UpdatedAt: