Teste A/B em C++: como não quebrar a produção e encontrar a verdade

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

Por que o teste A/B não é só sobre web e marketing

\n

Quando ouvimos "teste A/B", vêm à mente landing pages, botões "Comprar" e conversões no Google Analytics. Mas e se o seu produto não for um site, mas um servidor de alta carga em C++, um sistema embarcado ou um motor de jogo? É possível aplicar testes A/B aqui? Com certeza! E isso não é apenas um termo da moda, mas uma ferramenta real para tomar decisões baseadas em dados, e não na intuição.

\n

Em C++, o teste A/B ajuda a responder perguntas como: "O novo algoritmo de ordenação é realmente mais rápido?", "Vale a pena introduzir um pool de threads?", "O novo formato de serialização melhorará o tempo de resposta da API?". Sem um teste A/B, você corre o risco de implementar uma "otimização" que, na prática, se revela uma regressão. Neste artigo, vamos analisar como incorporar corretamente um mecanismo de experimentos no código C++, sem transformá-lo em um emaranhado de if-else.

\n\n

Arquitetura de teste A/B em C++: sem dor e sofrimento

\n

O principal perigo do teste A/B em C++ é poluir o código de produção com operadores condicionais. Para evitar isso, é necessário separar a lógica do experimento da lógica de negócios. Usamos o padrão Feature Flag (flag de funcionalidade) e a estratégia. Vamos considerar a arquitetura básica.

\n\n

Passo 1: Configuração centralizada

\n

Todas as flags e parâmetros dos experimentos são armazenados em um único local. Pode ser um arquivo JSON, um banco de dados ou um servidor de configuração remoto. Para simplificar, usaremos JSON.

\n
// experiment_config.json\n{\n  "experiments": {\n    "sort_algorithm_v2": {\n      "enabled": true,\n      "variants": {\n        "control": 50,\n        "treatment": 50\n      }\n    },\n    "memory_pool_size": {\n      "enabled": true,\n      "variants": {\n        "default": 33,\n        "pool_256": 33,\n        "pool_512": 34\n      }\n    }\n  }\n}
\n

A divisão de tráfego (traffic split) é definida em porcentagens. Em C++, carregamos esta configuração uma vez na inicialização da aplicação.

\n\n

Passo 2: Classe gerenciadora de experimentos

\n

Vamos criar uma classe thread-safe ExperimentManager, que, com base em uma chave (por exemplo, ID do usuário ou transação), determina em qual grupo o objeto se enquadra.

\n
#include <string>\n#include <unordered_map>\n#include <random>\n#include <shared_mutex>\n#include <nlohmann/json.hpp> // para parsing de JSON\n\nclass ExperimentManager {\npublic:\n    explicit ExperimentManager(const std::string& config_path);\n    \n    // Retorna o nome da variante para o experimento e chave fornecidos\n    std::string getVariant(const std::string& experiment_name, \n                           const std::string& user_id) const;\n\nprivate:\n    struct Experiment {\n        bool enabled;\n        std::vector<std::pair<std::string, int>> variants; // nome, peso\n        int total_weight;\n    };\n    \n    std::unordered_map<std::string, Experiment> experiments_;\n    mutable std::shared_mutex mutex_;\n    \n    // Hashing determinístico para distribuição estável\n    size_t hashAndMod(const std::string& key, int mod) const;\n};\n\n// Implementação (simplificada)\nstd::string ExperimentManager::getVariant(const std::string& exp_name,\n                                          const std::string& user_id) const {\n    std::shared_lock lock(mutex_);\n    auto it = experiments_.find(exp_name);\n    if (it == experiments_.end() || !it->second.enabled) {\n        return "control"; // fallback\n    }\n    const auto& exp = it->second;\n    size_t bucket = hashAndMod(user_id, exp.total_weight);\n    int cumulative = 0;\n    for (const auto& [name, weight] : exp.variants) {\n        cumulative += weight;\n        if (bucket < cumulative) {\n            return name;\n        }\n    }\n    return "control";\n}
\n

Importante: Usamos hashing determinístico — o mesmo usuário sempre cai no mesmo grupo. Isso é crítico para a correção do teste.

\n\n

Exemplo prático: Teste A/B de um novo algoritmo

Blogs

Book Recommendations