Feature flags no código: desacoplando deploy de release

1. O Problema: Quando Deploy e Release São a Mesma Coisa

1.1. Riscos de deploys monolíticos: rollbacks forçados e downtime

Em times que não utilizam feature flags, cada deploy é também uma release. Isso significa que, ao fazer o merge de uma branch com código novo para main, a funcionalidade vai automaticamente para produção. Se algo der errado, o rollback exige reverter commits inteiros, o que pode causar downtime prolongado.

# Cenário sem feature flags: deploy = release
git checkout -b feature/nova-busca
# ... desenvolve e commita código incompleto ...
git checkout main
git merge feature/nova-busca
git push origin main
# Agora o código incompleto está em produção!

1.2. Dependência entre branches e ciclos de release

Sem flags, as equipes ficam reféns de ciclos de release rígidos. Uma funcionalidade que depende de outra precisa esperar ambas estarem prontas. Isso gera branches longas, conflitos de merge e retrabalho.

1.3. Conflitos entre equipes: código incompleto vs. necessidade de deploy

Enquanto o time de frontend precisa fazer deploy de uma correção urgente, o time de backend ainda está desenvolvendo uma nova API. Sem flags, ou o deploy é adiado ou o código incompleto vai para produção.

2. Conceito de Feature Flags no Contexto do Git

2.1. O que é uma feature flag: variável de controle no código

Uma feature flag é uma variável que controla se um trecho de código deve ser executado ou não. No Git, isso significa que você pode commitar código novo sem ativá-lo imediatamente.

2.2. Diferença entre flag de release e flag experimental

  • Flag de release: controla quando uma funcionalidade pronta vai para produção.
  • Flag experimental: ativa funcionalidades em desenvolvimento para testes internos.

2.3. Exemplo mínimo: if feature_enabled("nova-busca") no código

# config/flags.yaml
features:
  nova-busca: false
  dark-mode: true
# app/busca.py
from config import flags

def realizar_busca(termo):
    if flags.feature_enabled("nova-busca"):
        return nova_busca(termo)
    else:
        return busca_legado(termo)

3. Estratégias de Branch com Feature Flags

3.1. Trabalhando em main com flags desligadas até o fim

Com feature flags, você pode trabalhar diretamente em main ou em branches curtas. O código novo fica atrás de uma flag desligada.

git checkout main
git pull origin main
# Adiciona código da nova funcionalidade com flag desligada
git add app/nova_busca.py config/flags.yaml
git commit -m "feat: implementa nova busca (desligada por padrão)"
git push origin main

3.2. Merge contínuo sem branches de feature longas

Em vez de manter uma branch feature/nova-busca por semanas, você faz merges frequentes para main com a flag desligada. Isso reduz conflitos e facilita revisões.

# Dia 1: estrutura básica
git commit -m "feat: adiciona estrutura da nova busca (flag off)"

# Dia 2: lógica principal
git commit -m "feat: implementa algoritmo de busca (flag off)"

# Dia 3: testes e ajustes
git commit -m "fix: corrige edge case na nova busca (flag off)"

3.3. Removendo flags após estabilização: como e quando no Git

Quando a funcionalidade está estável, remova a flag e o código legado.

# 1. Ativa a flag em produção
git commit -m "feat: ativa nova busca para 50% dos usuários"

# 2. Após validação, remove a flag e código antigo
git rm app/busca_legado.py
# Remove a condição if do código
git commit -m "feat: remove flag nova-busca e código legado"

4. Gerenciamento de Flags no Repositório Git

4.1. Armazenando configuração de flags: arquivos YAML/JSON versionados

Mantenha as flags em arquivos versionados para rastrear mudanças no Git.

# config/flags.yaml
version: 2.3.0
flags:
  nova-busca:
    enabled: false
    description: "Nova engine de busca"
    owner: "@time-busca"
  dark-mode:
    enabled: true
    description: "Modo escuro"
    owner: "@time-ui"

4.2. Commits de ativação/desativação de flags

Cada mudança de estado de flag deve ser um commit claro.

git commit -m "feat: enable pagination v2 for 10% users"
git commit -m "fix: disable pagination v2 due to timeout issue"
git commit -m "chore: remove flag pagination-v2 after stabilization"

4.3. Tags de release com estado de flags

Use tags para marcar releases com o estado das flags.

git tag -a v2.3.0-flags-off -m "Release v2.3.0 com flags desligadas"
git tag -a v2.4.0-flags-on -m "Release v2.4.0 com flags ativadas"

5. Deploy Seguro com Rollback via Flags

5.1. Usando git revert em uma flag (alteração de configuração) vs. código inteiro

Para desativar uma flag, basta reverter o commit de configuração, não o código inteiro.

# Commit que ativou a flag
git log --oneline
# a1b2c3d feat: enable pagination v2

# Reverte apenas a ativação
git revert a1b2c3d
git commit -m "fix: disable pagination v2 (rollback via flag)"

5.2. Hotfix com flag: desligar recurso sem reverter commit

Em vez de reverter commits de funcionalidades, apenas desligue a flag.

# Em produção, a flag está ativa
# Para desligar rapidamente:
echo "features:\n  pagination-v2: false" > config/flags.yaml
git add config/flags.yaml
git commit -m "hotfix: disable pagination v2 due to performance issue"

5.3. Exemplo prático: deploy de duas versões, uma com flag ativa

# Versão A: flag desligada (deploy padrão)
git tag v2.3.0-stable

# Versão B: flag ativada (deploy para teste)
git checkout v2.3.0-stable
echo "features:\n  pagination-v2: true" > config/flags.yaml
git add config/flags.yaml
git commit -m "feat: enable pagination v2 for testing"
git tag v2.3.0-test

6. Integração com Git Bisect e Automação

6.1. Identificando regressões em flags: git bisect sobre commits de configuração

git bisect start
git bisect bad HEAD
git bisect good v2.2.0

# O bisect vai testar commits. Se o bug só aparece com flag ativa:
# O commit ruim será o que ativou a flag

6.2. Scripts de teste que respeitam estado de flags

#!/bin/bash
# test_with_flags.sh
FLAGS_FILE="config/flags.yaml"

# Testa com flags desligadas
cp $FLAGS_FILE ${FLAGS_FILE}.backup
echo "features:\n  nova-busca: false" > $FLAGS_FILE
pytest tests/

# Testa com flags ativadas
echo "features:\n  nova-busca: true" > $FLAGS_FILE
pytest tests/

# Restaura configuração original
mv ${FLAGS_FILE}.backup $FLAGS_FILE

6.3. Pipeline CI/CD com variáveis de ambiente para flags

# .gitlab-ci.yml
stages:
  - test
  - deploy

variables:
  FEATURE_NOVA_BUSCA: "false"

test:
  script:
    - echo "features:\n  nova-busca: $FEATURE_NOVA_BUSCA" > config/flags.yaml
    - pytest tests/

deploy:
  script:
    - ansible-playbook deploy.yml -e "flag_nova_busca=$FEATURE_NOVA_BUSCA"

7. Boas Práticas e Armadilhas Comuns

7.1. Evitar flags permanentes: técnica de "flag debt" e remoção agendada

Crie um arquivo FLAGS_DEBT.md no repositório para rastrear flags que precisam ser removidas.

# FLAGS_DEBT.md
| Flag | Data de Criação | Data Limite | Responsável |
|------|----------------|-------------|-------------|
| nova-busca | 2024-01-15 | 2024-03-15 | @joao |
| dark-mode | 2024-02-01 | 2024-04-01 | @maria |

7.2. Cuidado com conflitos de merge em arquivos de configuração de flags

Use arquivos separados por funcionalidade para evitar conflitos.

config/flags/
├── busca.yaml
├── ui.yaml
└── pagamento.yaml

7.3. Documentação no README do repositório: mapa de flags ativas

# README.md

## Feature Flags Ativas

| Flag | Estado | Descrição | Como Desativar |
|------|--------|-----------|----------------|
| nova-busca | Ativa (50%) | Nova engine de busca | `git revert <commit-hash>` |
| dark-mode | Ativa (100%) | Modo escuro | Remover em 2024-04-01 |

Referências