Módulo 8: Project Validate Mercados Recommendations
Paso 5: audita la muestra y pesa la evidencia sin sesgo
Descripción
Tienes, después de la lección 5, un resultado positivo pero con un margen ajustado. Antes de dejar que ese número —solo, sin compañía— decida el destino de recommendations, este paso lo somete al mismo chequeo que el módulo 6 diseñó: ¿la muestra que generó este resultado está sesgada? ¿y cómo se compara, en fuerza, contra el resto de la evidencia que el equipo recogió durante la semana? Esta lección reutiliza sampleBias({ segments }) y rankByStrength(evidences), exactamente como quedaron en el módulo 6, sobre la muestra y la evidencia reales de esta ronda de discovery.
Conexión con el módulo. No hay ningún modelo nuevo aquí: los dos modelos son exactamente los del módulo 6. Lo que este paso aporta al pipeline completo es la evidencia ya auditada y ordenada que la lección 7 va a usar, pieza por pieza, para mover el confidence de la apuesta — sin ese orden explícito por strength, updateConfidence() no sabría cuánto peso darle a cada pieza.
Una analogía: la auditoría antes de la junta directiva
Antes de que un equipo de finanzas presente sus números en una junta directiva, alguien los audita: revisa si las fuentes son confiables, si algún número se infló sin respaldo. La auditoría no cambia los hechos del negocio — cambia si esos números merecen la confianza que se les va a dar en la decisión que sigue. Esta lección es esa auditoría, aplicada a la evidencia recogida durante la semana de recommendations, antes de que llegue a la lección 7 para convertirse en una decisión.
Ejemplo trabajado: sampleBias() sobre la muestra y rankByStrength() sobre la evidencia recogida
El equipo, aprendiendo del error que el proyecto del módulo 6 encontró en la primera ronda —una muestra dominada al 70% por power_user, con new_user completamente ausente—, reclutó esta vez con un criterio explícito de balance entre segmentos:
// L6 (M8): sampleBias() y rankByStrength(), exactamente como quedaron en el
// modulo 6, sobre la muestra y la evidencia reales de esta ronda de "recommendations".
function sampleBias({ segments }) {
const total = segments.reduce((sum, s) => sum + s.count, 0);
const withShare = segments.map((s) => ({ ...s, share: +((s.count / total) * 100).toFixed(1) }));
const dominant = withShare.reduce((max, s) => (s.share > max.share ? s : max));
const missing = withShare.filter((s) => s.count === 0);
const biased = dominant.share >= 70 || missing.length > 0;
return { total, segments: withShare, dominant, missing, biased };
}
const STRENGTH_BY_TYPE = { observed_behavior: 4, reported_behavior: 3, stated_opinion: 2, hypothetical: 1 };
function rankByStrength(evidences) {
return evidences.map((e) => ({ ...e, strength: STRENGTH_BY_TYPE[e.type] })).sort((a, b) => b.strength - a.strength);
}
console.log('=== sampleBias() sobre la muestra reclutada para esta ronda ===\n');
const sample = { segments: [
{ segment: 'power_user', count: 3 },
{ segment: 'occasional_buyer', count: 4 },
{ segment: 'new_user', count: 3 },
] };
const sampleCheck = sampleBias(sample);
console.log('Muestra: ' + sampleCheck.total + ' personas.');
console.log('Segmento dominante: ' + sampleCheck.dominant.segment + ' (' + sampleCheck.dominant.share + '%)');
console.log('Segmentos ausentes: ' + (sampleCheck.missing.length === 0 ? 'ninguno' : sampleCheck.missing.map((s) => s.segment).join(', ')));
console.log('sampleBias.biased: ' + sampleCheck.biased);
console.log('\n=== rankByStrength() sobre las cinco piezas de evidencia recogidas ===\n');
const evidence = [
{ claim: 'Hizo clic en "recomendado para ti" en el fake door del checkout', type: 'observed_behavior' },
{ claim: 'Ignoro la seccion de recomendados, cero clics en dos visitas', type: 'observed_behavior' },
{ claim: 'Agrego al carrito un producto recomendado en el fake door', type: 'observed_behavior' },
{ claim: 'Conto que la ultima vez que compro en otra tienda, siguio una recomendacion', type: 'reported_behavior' },
{ claim: 'Dijo que le encantaria ver recomendaciones personalizadas', type: 'hypothetical' },
];
const ranked = rankByStrength(evidence);
ranked.forEach((e) => console.log(' strength=' + e.strength + ' [' + e.type.padEnd(18) + '] ' + e.claim));
const observedFavorable = ranked.filter((e) => e.type === 'observed_behavior' && !e.claim.toLowerCase().includes('ignoro')).length;
const observedUnfavorable = ranked.filter((e) => e.type === 'observed_behavior' && e.claim.toLowerCase().includes('ignoro')).length;
console.log('\nDe las evidencias observed_behavior (strength=4): ' + observedFavorable + ' favorable(s), ' + observedUnfavorable + ' desfavorable(s).');
Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:
=== sampleBias() sobre la muestra reclutada para esta ronda ===
Muestra: 10 personas.
Segmento dominante: occasional_buyer (40%)
Segmentos ausentes: ninguno
sampleBias.biased: false
=== rankByStrength() sobre las cinco piezas de evidencia recogidas ===
strength=4 [observed_behavior ] Hizo clic en "recomendado para ti" en el fake door del checkout
strength=4 [observed_behavior ] Ignoro la seccion de recomendados, cero clics en dos visitas
strength=4 [observed_behavior ] Agrego al carrito un producto recomendado en el fake door
strength=3 [reported_behavior ] Conto que la ultima vez que compro en otra tienda, siguio una recomendacion
strength=1 [hypothetical ] Dijo que le encantaria ver recomendaciones personalizadas
De las evidencias observed_behavior (strength=4): 2 favorable(s), 1 desfavorable(s).
La muestra pasa el chequeo: 10 personas repartidas entre tres segmentos, ninguno por encima del 70%, ninguno ausente — sampleBias.biased: false. Este resultado no es casualidad: el equipo reclutó, esta vez, con la corrección exacta que el proyecto del módulo 6 dejó lista, en vez de repetir el patrón de reclutar "a quien tenga más a mano".
La evidencia ordenada por strength deja algo importante a la vista: tres piezas de observed_behavior (la fuerza máxima), pero no las tres apuntan en la misma dirección. Dos son favorables —un clic real, un producto agregado al carrito—, y una es desfavorable —alguien ignoró por completo la sección, en dos visitas distintas—. rankByStrength() no tiene ninguna opinión sobre si una evidencia es buena o mala noticia: solo mide cuánto pesa, según su tipo. Un equipo tentado a quedarse solo con las dos favorables, ignorando la tercera, estaría cometiendo exactamente el sesgo de confirmación que el módulo 6 nombró en detalle — y esta lección, al correr rankByStrength() sobre las cinco piezas juntas, no permite ese descarte silencioso: la evidencia desfavorable queda al mismo nivel de fuerza que las dos favorables, no relegada a una nota al pie.
Por qué la señal negativa no cancela la señal positiva
Vale la pena resistir dos lecturas igualmente equivocadas de este resultado. La primera: ignorar la evidencia desfavorable porque "dos contra una, gana la mayoría" — eso confunde volumen con fuerza, exactamente el error que el módulo 6 señaló al advertir que contar votos no es lo mismo que pesar evidencia. La segunda: tratar la única señal desfavorable como si cancelara por completo a las dos favorables, dejando la evidencia en tablas — eso ignora que las tres son observaciones reales, cada una legítima, de comportamientos distintos frente a la misma funcionalidad. Ninguna de las dos lecturas es correcta. Lo que corresponde es llevar las tres, con su peso exacto, a la lección 7 — donde updateConfidence() las va a procesar una por una, dejando que cada una mueva la aguja en su dirección, sin que ninguna cancele a la otra por decreto.
TIPO DE EVIDENCIA STRENGTH CUENTA A FAVOR CUENTA EN CONTRA
──────────────────────── ──────── ─────────────── ─────────────────
observed_behavior 4 2 (clic, carrito) 1 (ignoro)
reported_behavior 3 1 (conto que...) 0
hypothetical 1 1 (le encantaria) 0
──────────────────────── ──────── ─────────────── ─────────────────
Errores comunes
Reportar solo las evidencias favorables al presentar el resultado. Qué pasa: al resumir la semana para el resto del equipo, alguien menciona el clic y el producto agregado al carrito, pero omite por completo la evidencia de la persona que ignoró la sección en dos visitas — no por mala fe, sino porque el resultado favorable se siente más relevante de contar. Por qué pasa: una historia con final feliz es más fácil de comunicar, y omitir un dato incómodo no siempre se siente como ocultar información, sino como "simplificar el reporte". Cómo detectarlo: pregunta directamente si hubo alguna evidencia desfavorable en la semana — si la respuesta tarda en llegar o se minimiza rápido, probablemente se omitió del resumen inicial. Cómo corregirlo: reporta siempre las cinco piezas de evidencia juntas, ordenadas por rankByStrength(), sin filtrar por si confirman o refutan — la auditoría de esta lección existe exactamente para hacer visible lo que un resumen selectivo escondería.
Confundir sampleBias.biased: false con "la evidencia es suficiente". Qué pasa: al ver que la muestra pasó el chequeo de sesgo, alguien concluye que la evidencia recogida ya es sólida y suficiente para decidir, sin considerar que solo son 10 personas en total —una muestra pequeña, aunque bien balanceada—. Por qué pasa: pasar un chequeo explícito ("no está sesgada") se siente como una aprobación general de calidad, más allá de lo que el chequeo realmente mide. Cómo detectarlo: si la conclusión sobre el tamaño de la evidencia se basa únicamente en sampleBias.biased: false, sin ninguna mención de cuántas personas participaron en total. Cómo corregirlo: sampleBias() responde una sola pregunta —¿algún segmento domina de forma desproporcionada, o falta por completo?—, no si la muestra es lo bastante grande para tener rigor estadístico. Esa segunda pregunta, sobre tamaño de muestra y significancia, es territorio de product-metrics-and-experimentation-guide — esta lección solo confirma que, dentro de las 10 personas que sí se entrevistaron, ningún segmento se llevó una porción injusta de la conversación.
Ejercicios
Ejercicio 1 — Agrega una sexta evidencia y recalcula. Al conjunto evidence de este ejemplo, agrega { claim: 'Compro un producto recomendado dos semanas despues, sin que nadie se lo recordara', type: 'observed_behavior' }. ¿Cuántas piezas de observed_behavior habría en total, y cambia la fuerza máxima reportada?
Ver solución
Habría 4 piezas de observed_behavior en vez de 3 (las tres originales más esta nueva, favorable). La fuerza máxima seguiría siendo 4 — ya era el máximo posible con las tres observaciones originales, así que una cuarta no lo cambia, aunque sí mejora la proporción general de evidencia de comportamiento real (ahora 4 de 6 piezas, en vez de 3 de 5). rankByStrength() reporta la fuerza de cada pieza individual, no un acumulado — agregar más evidencia del mismo nivel más fuerte no cambia el strength máximo, pero sí cambia cuántas piezas de ese nivel llegan a la síntesis de la lección 7.
Ejercicio 2 — Recalcula sampleBias() con una muestra distinta. Si la muestra hubiera sido { power_user: 6, occasional_buyer: 3, new_user: 1 } (10 personas en total), ¿pasaría el chequeo de sampleBias()? Calcula el share de cada segmento a mano antes de responder.
Ver solución
power_user: 60%, occasional_buyer: 30%, new_user: 10%. Ningún segmento llega al 70% de dominancia, y ninguno está en cero — así que sampleBias.biased: false, la muestra pasaría el chequeo, aunque power_user sea el segmento claramente mayoritario. Este ejercicio muestra un límite real de sampleBias(): el umbral de 70% detecta dominancia extrema, no cualquier desbalance — una muestra 60/30/10 sigue siendo menos representativa que la 30/40/30 de este ejemplo, aunque el modelo no la marque como biased. El chequeo es un mínimo necesario, no una garantía de balance perfecto.
Ejercicio 3 — Conecta con el estado de la apuesta. Después de esta lección, ¿en qué cambió exactamente el objeto recommendationsBet (risk: 0.6, impact: 5, tested: false, confidenceCeiling: 0.3)? Sé preciso sobre qué campos cambiaron y cuáles siguen exactamente igual.
Ver solución
Ningún campo cambió. risk, impact, tested y confidenceCeiling siguen exactamente iguales — tested sigue en false. Lo que existe ahora, y no existía antes de esta lección, es una auditoría verificada de la evidencia disponible: una muestra confirmada sin sesgo y cinco piezas de evidencia ordenadas por fuerza real, con una señal desfavorable incluida honestamente. El objeto de la apuesta —y su estado tested: false— solo cambia cuando esa evidencia auditada se sintetiza y el confidence se actualiza, el trabajo exacto de la lección 7.
Resumen y siguiente paso
En esta lección auditaste, sin cambiar ningún modelo del módulo 6, la muestra y la evidencia recogidas durante la semana de recommendations: 10 personas repartidas sin sesgo entre tres segmentos, y cinco piezas de evidencia ordenadas por fuerza real —tres de comportamiento observado (dos favorables, una desfavorable), una de comportamiento reportado, una hipotética. Viste, con la evidencia mixta a la vista, por qué ni ignorar la señal negativa ni dejar que cancele a las positivas es la lectura correcta — las tres son observaciones legítimas que merecen su propio peso.
Antes de avanzar deberías poder: correr sampleBias() y rankByStrength() sobre cualquier conjunto nuevo de evidencia; y explicar por qué una muestra sin sesgo no garantiza, por sí sola, que la evidencia sea suficiente para decidir.
La lección 7 toma esta evidencia ya auditada —junto con las notas de entrevista de la lección 4— y la sintetiza de verdad: cuenta las señales reales, mueve el confidence de recommendations pieza por pieza, y llega, por primera vez en toda la guía, a una decisión.
Recursos
- Teresa Torres, "Assumption Testing: Everything You Need to Know to Get Started" — producttalk.org/assumption-testing. Sobre por qué auditar la evidencia antes de sintetizarla es una disciplina, no un paso opcional. En inglés.
- Rob Fitzpatrick, The Mom Test — momtestbook.com. El recordatorio de por qué el compromiso real —un clic, una compra— pesa más que un cumplido, sea cual sea el proyecto que estés validando. En inglés.
- Daniel Kahneman, Thinking, Fast and Slow — us.macmillan.com/books/9780374533557/thinkingfastandslow. Sobre por qué el cerebro humano tiende a descartar, sin darse cuenta, la evidencia que contradice lo que ya quería creer. En inglés.