Testes A/B e Experimentação
Testes A/B medem se uma mudança melhora resultados - conversão, latência, receita - com disciplina estatística. Serviços Rust precisam de atribuição no lado do servidor, métricas de proteção e tags de variante em tracing para que experimentos não se tornem um espaguete de flags indebugável.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Variant { Control, Treatment }
pub fn assign(experiment: &str, unit_id: &str, pct_treatment: u8) -> Variant {
let mut h = DefaultHasher::new();
experiment.hash(&mut h);
unit_id.hash(&mut h);
let bucket = (h.finish() % 100) as u8;
if bucket < pct_treatment { Variant::Treatment } else { Variant::Control }
}Quando usar isso:
- Produto pergunta "o novo fluxo de checkout converte melhor?"
- Ajuste de algoritmo com métrica de negócio mensurável
- Mudança de UX com latência de proteção
- Experimentos de precificação ou ranqueamento
Exemplo de Trabalho
use axum::{Json, Router, routing::post};
use serde::{Deserialize, Serialize};
use tracing::instrument;
#[derive(Deserialize)]
struct CheckoutRequest { tenant_id: String, order_id: String }
#[derive(Serialize)]
struct CheckoutResponse {
variant: &'static str,
order_id: String,
}
#[instrument(skip(req), fields(experiment = "checkout_v2", variant))]
async fn checkout(Json(req): Json<CheckoutRequest>) -> Json<CheckoutResponse> {
let variant = match assign("checkout_v2", &req.tenant_id, 10) {
Variant::Control => "control",
Variant::Treatment => "treatment",
};
tracing::Span::current().record("variant", variant);
// lógica do branch...
Json(CheckoutResponse { variant, order_id: req.order_id })
}# Proteção: aborte o experimento se a latência p95 > baseline + 20% por 15m
# Conecte ao controlador de experimento ou decisão manual do ICO que isso demonstra:
- Atribuição fixa determinística por
tenant_id - Variante registrada no span para correlação de logs/métricas
- Porcentagem de tratamento explícita (rampa de 10%)
Mergulho Profundo
Ciclo de Vida do Experimento
- Hipótese - "O tratamento reduz o p95 do checkout em 10ms sem aumentar erros."
- Pré-registro - Métrica primária, proteções, duração, amostra mínima.
- Rampa - 5% -> 10% -> 50% com verificações de proteção.
- Análise - Teste estatístico ou IC Bayesiano; evite "espiar" sem correção.
- Envie ou reverta - Merge do vencedor; delete o caminho de código do experimento.
Proteções
- Taxa de erro, p95 latência, receita por minuto - pisos/teto.
- Desabilite automaticamente o tratamento via flag quando a proteção queimar.
- Ética: sem dark patterns; documente na revisão do produto.
Observabilidade em Rust
Exporte os rótulos experiment e variant em métricas Prometheus. Use logs estruturados para análise de funil e exportação para o data warehouse.
Armadilhas
- Atribuição não fixa - O mesmo usuário vê ambas as variantes. Correção: Hash de um ID de unidade estável.
- Espiar e parar cedo - Falsos positivos. Correção: Defina o tamanho da amostra previamente ou use método de teste sequencial.
- Paradoxo de Simpson - Agregado vence, segmentos perdem. Correção: Divida por região/plano.
- Experimento sem caminho de desativação de flag - Não é possível abortar rapidamente. Correção: Tratamento atrás de uma flag.
- Variante apenas no corpo da resposta - Análises perdem falhas do lado do servidor. Correção: Log de evento do lado do servidor.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Bandit Multi-braço | Otimização contínua | Teste regulatório de período fixo |
| Tráfego Sombra | Compare a saída sem impacto no usuário | Mudanças de UX que os usuários devem ver |
| Replay Offline | Dados históricos disponíveis | Comportamento muda com tráfego ao vivo |
| Entrevista com Usuário | Porquê qualitativo | Decisão estatística de envio |
FAQs
Duração mínima do experimento?
Geralmente 1-2 semanas para ciclo dia útil/fim de semana; análise de potência define a amostra.
Unidade de atribuição?
Usuário, tenant ou sessão - escolha um e documente; tenant é comum em APIs Rust B2B.
Experimentos em workers?
Mesma função de atribuição; propague a variante na carga útil do job.
A/B via CLI?
Canal de opt-in ou atualização do manifesto da coorte, não porcentagem HTTP.
GDPR e experimentos?
Documente a base legal; evite segmentação por atributos sensíveis sem revisão.
Múltiplos experimentos concorrentes?
Hashing em camadas ou exclusão mútua; observe os efeitos de interação.
Quando não fazer A/B?
Correções de segurança, conformidade legal, correções de bugs claras - envie uniformemente.
Ferramentas?
Customizado + data warehouse, Optimizely server-side, ou plataforma de feature flag com módulo de experimentos.
Experimentos em batch com Polars?
Análise de coorte offline; separado da infraestrutura de experimentos HTTP ao vivo.
Valor de experimento falho?
Documente o resultado nulo; evita reexecutar a mesma hipótese cegamente.
Relacionados
- Feature Flags & Progressive Delivery - rampa e desativação
- DORA Metrics - cadência de entrega vs. experimento
- Tracing - variante em spans
- Comunicação com Stakeholders - relatar resultados
- Melhores Práticas de Colaboração de Produto - alinhamento de produto
Versões do Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.