Pruebas A/B en Rust: cómo no romper la producción y encontrar la verdad

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué Rust es ideal para pruebas A/B?

\n

Cuando hablamos de pruebas A/B, normalmente pensamos en Python, R o JavaScript. Pero Rust es un caballo oscuro que, en condiciones reales de combate, demuestra ser más rápido, más seguro y más barato. Si tu backend está escrito en Rust, puedes integrar experimentos directamente en el núcleo del sistema sin la sobrecarga de microservicios y Redis.

\n

Imagina: cambias el algoritmo de recomendaciones, el color del botón "Comprar" o la lógica de análisis. Un error en el código de una prueba A/B en Python puede tumbar el servidor. En Rust, simplemente no compilarás si te saltas una rama de condición. El compilador es tu mejor QA.

\n

En este artículo, analizaremos las pruebas A/B en Rust desde la arquitectura hasta la producción. Aprenderás cómo hacer pruebas divididas sin dolor, calcular estadísticas y no perder usuarios.

\n\n

Arquitectura de una prueba A/B en Rust: tres pilares

\n

Cualquier experimento en una aplicación Rust se sostiene sobre tres componentes:

\n
    \n
  • Aleatorizador — ¿quién va al grupo A y quién al B?
  • \n
  • Almacén de asignaciones — para que el usuario no salte entre grupos en cada solicitud.
  • \n
  • Registro y estadísticas — calculamos la conversión y el valor p.
  • \n
\n

Escribamos un módulo simple pero funcional. Sin frameworks, solo Rust y su biblioteca estándar.

\n\n

Paso 1. Aleatorización basada en hash

\n

La aleatorización determinista es la clave de la estabilidad. Si usas rand::random(), con cada reinicio del servidor el usuario podría saltar a otro grupo. Usamos un hash ponderado del ID del usuario y el nombre del experimento.

\n\n
use std::collections::hash_map::DefaultHasher;\nuse std::hash::{Hash, Hasher};\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum Group {\n    Control,\n    Treatment,\n}\n\npub fn assign_group(user_id: &str, experiment: &str, treatment_weight: f64) -> Group {\n    let mut hasher = DefaultHasher::new();\n    user_id.hash(&mut hasher);\n    experiment.hash(&mut hasher);\n    let hash = hasher.finish();\n\n    let normalized = (hash as f64) / (u64::MAX as f64);\n    if normalized < treatment_weight {\n        Group::Treatment\n    } else {\n        Group::Control\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_deterministic() {\n        let g1 = assign_group("user_42", "button_color_v2", 0.5);\n        let g2 = assign_group("user_42", "button_color_v2", 0.5);\n        assert_eq!(g1, g2);\n    }\n}\n
\n\n

Este código garantiza que el mismo usuario siempre estará en el mismo grupo para un experimento específico. El peso treatment_weight es la proporción de tráfico que va al grupo de prueba (por ejemplo, 0.1 = 10%).

\n\n

Paso 2. Almacén: ¿caché o base de datos?

\n

Para proyectos de alta carga, almacenar las asignaciones en memoria es un riesgo. Si el servidor se cae, el usuario pierde el grupo. Es mejor usar Redis o SQLite. Pero para simplificar, mostraremos un caché en memoria con TTL.

\n\n
use std::collections::HashMap;\nuse std::time::{Duration, Instant};\n\nstruct AssignmentCache {\n    store: HashMap<String, (Group, Instant)>,\n    ttl: Duration,\n}\n\nimpl AssignmentCache {\n    fn new(ttl_secs: u64) -> Self {\n        Self {\n            store: HashMap::new(),\n            ttl: Duration::from_secs(ttl_secs),\n        }\n    }\n\n    fn get(&mut self, key: &str) -> Option<Group> {\n        if let Some((group, time)) = self.store.get(key) {\n            if time.elapsed() < self.ttl {\n                return Some(*group);\n            }\n        }\n        None\n    }\n\n    fn set(&mut self, key: String, group: Group) {\n        self.store.insert(key, (group, Instant::now()));\n    }\n}\n
\n\n

En un proyecto real, reemplace HashMap por dashmap o Redis; esto proporcionará seguridad en hilos y persistencia.

\n\n

Integración con

Blogs

Book Recommendations