Como usar feature flags em APIs para rollout gradual de mudanças

1. Conceitos Fundamentais de Feature Flags em APIs

Feature flags (ou toggles) são mecanismos de configuração que permitem ativar ou desativar funcionalidades em tempo de execução, sem necessidade de deploy. No contexto de APIs REST, elas funcionam como interruptores controlados externamente que determinam qual versão de um recurso será executada.

Existem dois tipos principais de flags:
- Release toggles: controlam a liberação de novas funcionalidades para produção, permitindo ativação gradual.
- Experimentação toggles: usadas para testes A/B, onde diferentes grupos de usuários veem comportamentos distintos.

O ciclo de vida de uma feature flag segue quatro estágios:
1. Criação: define-se a flag, seu comportamento padrão e os alvos iniciais
2. Ativação gradual: libera-se para um percentual pequeno de tráfego
3. Estabilização: monitora-se métricas e expande-se a liberação
4. Remoção: quando a funcionalidade está estável, remove-se o código legado e a flag

2. Arquitetura de Integração de Feature Flags em APIs

A avaliação de flags pode seguir duas abordagens arquiteturais:

Abordagem centralizada: um serviço dedicado gerencia todas as flags. As APIs consultam esse serviço via SDK ou chamada HTTP. Vantagem: gerenciamento unificado. Desvantagem: latência adicional.

Abordagem descentralizada: cada serviço possui seu próprio mecanismo de flags, geralmente baseado em arquivos de configuração ou variáveis de ambiente. Vantagem: simplicidade e baixa latência. Desvantagem: difícil gerenciamento em escala.

Para minimizar impacto na latência, implementa-se cache local com TTL configurável:

// Exemplo de configuração de cache para feature flags
feature_flags:
  cache:
    strategy: local_in_memory
    ttl_seconds: 30
    max_size: 1000
  fallback:
    default_value: false
    log_on_fallback: true

Integrações comuns incluem provedores como LaunchDarkly, Unleash e Split, que oferecem SDKs otimizados. Para implementação própria, utiliza-se um banco de dados chave-valor com cache Redis.

3. Estratégias de Rollout Gradual com Feature Flags

Lançamento por percentual de usuários: utiliza-se hash consistente do identificador do usuário para determinar se a flag deve ser ativada. Exemplo:

// Função de hash para distribuição uniforme
function shouldActivateFlag(userId: string, percentage: number): boolean {
  const hash = hashCode(userId) % 100;
  return hash < percentage;
}

Lançamento por segmentos: flags podem ser ativadas com base em atributos como:
- Localização geográfica (país, região)
- Plano de assinatura (free, premium, enterprise)
- Versão do cliente (app mobile 2.3+, web)

Canary releases com flags: combina-se o deploy de uma nova versão do serviço com uma flag que controla o tráfego para essa versão. Exemplo de configuração:

# Configuração de canary release
canary:
  new_version: v2.1.0
  traffic_percentage: 5
  flag_name: api.search.new-algorithm.enabled
  metrics_monitor:
    error_rate_threshold: 0.01
    latency_p99_threshold_ms: 500

4. Implementação Técnica: Exemplos Práticos em APIs REST

Middleware de feature flags para validação em endpoints:

// Middleware para avaliação de feature flag
import { FeatureFlagService } from './services/feature-flags';

export function featureFlagMiddleware(flagName: string) {
  return async (req, res, next) => {
    try {
      const userId = req.headers['x-user-id'];
      const isEnabled = await FeatureFlagService.evaluate(flagName, {
        userId,
        region: req.headers['x-region'],
        plan: req.headers['x-plan']
      });

      if (!isEnabled) {
        return res.status(404).json({
          error: 'Resource not available',
          code: 'FEATURE_NOT_AVAILABLE'
        });
      }

      next();
    } catch (error) {
      // Fallback seguro: se o serviço de flags falhar, permite acesso
      console.error('Feature flag evaluation failed:', error);
      next();
    }
  };
}

Controle de versionamento interno sem alterar contrato público:

// Endpoint de busca com flag para novo algoritmo
app.get('/api/search', async (req, res) => {
  const query = req.query.q;
  const userId = req.headers['x-user-id'];

  const useNewAlgorithm = await flags.evaluate(
    'search.new-algorithm.enabled',
    { userId }
  );

  if (useNewAlgorithm) {
    const results = await searchService.newAlgorithm(query);
    log.info('New search algorithm used', { userId, query });
    return res.json(results);
  }

  const results = await searchService.legacyAlgorithm(query);
  return res.json(results);
});

Avaliação com fallback seguro e logs estruturados:

// Serviço de feature flags com fallback e logging
class FeatureFlagService {
  async evaluate(flagName: string, context: any): Promise<boolean> {
    try {
      const result = await this.provider.evaluate(flagName, context);

      log.info('Feature flag evaluated', {
        flag: flagName,
        userId: context.userId,
        result,
        timestamp: new Date().toISOString()
      });

      return result;
    } catch (error) {
      log.error('Feature flag evaluation failed', {
        flag: flagName,
        error: error.message
      });

      // Fallback: retorna false (funcionalidade desativada)
      return false;
    }
  }
}

5. Boas Práticas de Nomenclatura e Organização de Flags

Adote um padrão de nomenclatura consistente: {serviço}.{recurso}.{comportamento}

# Exemplos de nomenclatura padronizada
payment.new-gateway.enabled
payment.new-gateway.timeout_ms
search.vector-index.active
auth.biometric-login.rollout_percentage
notifications.push-v2.target_platforms

Versionamento semântico de flags: inclua informações de deprecação no nome:

# Nomenclatura com data de deprecação
payment.legacy-gateway.deprecated_2024-12-31
search.old-algorithm.removal_scheduled_2025-03-01

Documentação automatizada: gere um manifesto de flags ativas:

# Manifesto de feature flags (gerado automaticamente)
flags:
  - name: search.new-algorithm.enabled
    status: active
    rollout_percentage: 25
    created: 2024-01-15
    deprecated: null
    impact: Alters search result ordering
  - name: payment.legacy-gateway.enabled
    status: deprecated
    rollout_percentage: 100
    created: 2023-06-01
    deprecated: 2024-12-31
    impact: Affects payment processing flow

6. Monitoramento, Observabilidade e Remoção de Flags

Métricas essenciais para cada flag:

# Métricas de observabilidade para feature flags
metrics:
  - name: feature_flag.error_rate
    type: counter
    labels: [flag_name, group]
  - name: feature_flag.latency_ms
    type: histogram
    labels: [flag_name, group]
  - name: feature_flag.evaluation_count
    type: counter
    labels: [flag_name, result]

Alertas para comportamento inesperado:

# Regras de alerta para feature flags
alerts:
  - name: high_error_rate_on_new_feature
    condition: error_rate > 0.05
    for: 5m
    severity: critical
    action: rollback_flag
  - name: latency_degradation
    condition: latency_p99 > 2x baseline
    for: 10m
    severity: warning
    action: notify_team

Processo de limpeza de flags obsoletas:

# Script de identificação de flags obsoletas
SELECT flag_name, last_evaluation, rollout_percentage
FROM feature_flags
WHERE last_evaluation < NOW() - INTERVAL '90 days'
   OR rollout_percentage = 100
   AND created < NOW() - INTERVAL '180 days';

7. Casos de Uso Avançados e Armadilhas Comuns

A/B testing em endpoints de busca: utilize flags para expor diferentes algoritmos:

# Configuração de A/B test para busca
ab_test:
  flag: search.ranking-algorithm.experiment
  variants:
    - name: control
      percentage: 50
      algorithm: tf-idf
    - name: treatment
      percentage: 50
      algorithm: neural-ranking
  metrics:
    - click_through_rate
    - session_duration

Gerenciamento de dependências entre flags (flag entanglement):

# Matriz de dependências entre flags
dependencies:
  - flag: payment.new-gateway.enabled
    depends_on:
      - flag: infrastructure.new-payment-service.active
        required: true
      - flag: compliance.audit-v2.enabled
        required: false
  - flag: search.vector-index.active
    depends_on:
      - flag: infrastructure.vector-db.deployed
        required: true

Problemas comuns e como evitá-los:

  1. Poluição de código: estabeleça um limite máximo de flags ativas por serviço (ex: 10 flags simultâneas)
  2. Testes insuficientes: implemente testes unitários que cubram ambos os estados de cada flag
  3. Riscos de segurança: nunca use flags para controlar autenticação ou autorização; sempre valide permissões no backend

Referências