Teste A/B em C#: como parar de adivinhar e começar a ganhar dinheiro

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 seu código C# está trabalhando em vão (e como corrigir isso)

\n\n

Você passou semanas refatorando o algoritmo de recomendações. A nova versão do RecommendationService parece impecável, o código mais limpo, as consultas LINQ mais rápidas. Você implanta a atualização em produção, e… a conversão cai 15%. Familiar? Se não — você teve sorte. Se sim — você acabou de perder dinheiro e tempo, porque confiou na intuição, e não nos dados.

\n\n

No mundo do desenvolvimento C#, existe uma regra de ouro: “Nunca confie na sua opinião, confie na estatística”. É para isso que existe o teste A/B (ou teste de divisão). Isso não é um truque de marketing — é uma abordagem de engenharia para a tomada de decisões. Neste artigo, vamos abordar como implementar um teste A/B completo na sua aplicação C#: desde a arquitetura até o cálculo da significância estatística. E sim, terá muito código.

\n\n

O que é teste A/B no contexto do C#?

\n\n

Imagine que você tem duas variantes da mesma lógica — o grupo de controle A (versão antiga) e o grupo experimental B (versão nova). Você distribui os usuários aleatoriamente entre eles, coleta métricas e, com a ajuda da matemática, determina qual variante é realmente melhor.

\n\n

Em C#, isso pode envolver qualquer coisa:

\n
    \n
  • Alteração do algoritmo de busca
  • \n
  • Novo design do formulário de pagamento
  • \n
  • Diferentes opções de ordenação de produtos
  • \n
  • Experimentos com tempo de cache
  • \n
  • Teste de modelos de ML
  • \n
\n\n

Ponto-chave: teste A/B em C# não é sobre “colocar uma flag no config”. É sobre arquitetura limpa, randomização e coleta de métricas sem bugs.

\n\n

Arquitetura de teste A/B: Layer Cake em C#

\n\n

Vamos direto à prática. Vou mostrar uma arquitetura mínima, mas funcional. Ela consiste em três camadas:

\n\n
    \n
  1. Assigner — decide em qual grupo o usuário caiu.
  2. \n
  3. Feature Decision — qual variante de código executar.
  4. \n
  5. Tracker — registra o evento e o resultado.
  6. \n
\n\n

Passo 1: Assigner — quem é você, usuário?

\n\n

O bug mais comum é a randomização incorreta. Usar Random é uma má ideia, porque ao reiniciar a aplicação, os grupos “flutuam”. A solução correta é a distribuição determinística baseada no UserId.

\n\n
public interface IExperimentAssigner\n{\n    string GetVariant(string userId, string experimentName);\n}\n\npublic class ConsistentExperimentAssigner : IExperimentAssigner\n{\n    public string GetVariant(string userId, string experimentName)\n    {\n        // Cria um hash estável a partir de userId + experimentName\n        var hash = HashCode.Combine(userId, experimentName);\n        // Pega o módulo de 100 — obtém a porcentagem\n        var bucket = Math.Abs(hash) % 100;\n        \n        // Supondo 50% em A, 50% em B\n        return bucket < 50 ? "A" : "B";\n    }\n}\n
\n\n

Agora, o mesmo usuário estará sempre no mesmo grupo para um experimento específico. Isso é crítico para a correção dos dados.

\n\n

Passo 2: Feature Decision — a batalha dos algoritmos

\n\n

Agora, precisamos que o código escolha qual lógica executar. Usamos o padrão Strategy com uma factory.

\n\n
public interface IPricingStrategy\n{\n    decimal CalculatePrice(decimal basePrice);\n}\n\npublic class OldPricingStrategy : IPricingStrategy\n{\n    public decimal CalculatePrice(decimal basePrice) \n        => basePrice; // Sem desconto\n}\n\npublic class NewPricingStrategy : IPricingStrategy\n{\n    public decimal CalculatePrice(decimal basePrice) \n        => basePrice * 0.9m; // Desconto de 10%\n}\n\npublic class PricingStrategyFactory\n{\n    private readonly IExperimentAssigner _assigner;\n\n    public PricingStrategyFactory(IExperimentAssigner assi                

Blogs

Book Recommendations