Módulo 6: Avoiding Bias

Mantente honesto: el checklist final

Descripción

Hasta aquí construiste dos herramientas ejecutables por separado: sampleBias() (lección 4) verifica si la muestra que recolectaste está sesgada hacia un segmento o le falta uno relevante; rankByStrength() (lección 6) pesa cada pieza de evidencia por su tipo, sin importar cuán convincente se sintió al escucharla. Es tentador pensar que, con las dos herramientas en la mano, ya estás protegido del sesgo de confirmación. Pero el ejercicio 3 de la lección anterior ya insinuó el problema: una evidencia puede tener el strength máximo posible y seguir siendo poco confiable, si viene de una muestra sesgada — y una muestra perfectamente representativa puede producir evidencia débil, si nadie observó comportamiento real. Ninguna de las dos herramientas, por sí sola, cuenta la historia completa.

Esta lección no agrega ningún modelo nuevo: junta los dos que ya tienes en un checklist de honestidad de dos preguntas, y lo corre sobre dos versiones de la misma ronda de discovery de recommendations — la ronda apurada, típica de un equipo sin las herramientas de este módulo, y la ronda corregida, después de aplicar lo aprendido en las lecciones 2 a 6. El resultado no es solo un ejercicio académico: es, literalmente, el checklist que vas a correr en el mini-proyecto de la lección 8 antes de entregar cualquier hallazgo al módulo 7 para sintetizarlo.

Conexión con el módulo. Esta es la lección bisagra del módulo: reutiliza sampleBias() (lección 4) y rankByStrength() (lección 6) exactamente como quedaron, sin ningún cambio, y los corre juntos dentro de honestyChecklist(), un pequeño modelo que combina ambos resultados en un solo veredicto. Lo que sale de este checklist es exactamente el insumo que el módulo 7 (synthesize(), updateConfidence()) necesita recibir: evidencia ya verificada como no sesgada en su origen y suficientemente fuerte en su tipo, no evidencia cruda sin auditar.

Una analogía: la doble revisión antes de firmar

Un contrato importante no se firma después de que una sola persona lo lea una vez y diga "se ve bien". Pasa, típicamente, por dos revisiones distintas: alguien revisa que los términos sean justos (el contenido), y alguien más revisa que las firmas y las partes involucradas sean las correctas (el proceso). Un contrato con términos perfectos, firmado por la persona equivocada, es tan inválido como uno con la firma correcta pero términos abusivos — ninguna de las dos revisiones, por separado, es suficiente.

El checklist de esta lección hace exactamente ese doble chequeo con la evidencia de discovery: sampleBias() revisa "¿la persona correcta firmó esto?" —¿la muestra representa a quien debería representar?— y rankByStrength() revisa "¿los términos son sólidos?" —¿la evidencia es del tipo que de verdad predice comportamiento futuro?—. Pasar solo una de las dos revisiones no es suficiente para confiar en la conclusión.

Ejemplo trabajado: honestyChecklist() sobre dos rondas de discovery

Comparamos la ronda apurada de recommendations —la muestra sesgada de la lección 4, con evidencia mayormente de tipo hypothetical y stated_opinion— contra una ronda corregida, con una muestra más balanceada y evidencia de comportamiento real.

// L7: sampleBias() (L4) y rankByStrength() (L6) reusados sin cambios, combinados en un
// checklist de honestidad de dos preguntas, aplicado a dos rondas de discovery de
// "recommendations": la ronda apurada de antes de este modulo, y la ronda corregida
// despues de aplicar lo aprendido en L2-L6.
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, 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);
}

function honestyChecklist(round) {
  const sample = sampleBias(round.sample);
  const ranked = rankByStrength(round.evidence);
  const avgStrength = +(ranked.reduce((sum, e) => sum + e.strength, 0) / ranked.length).toFixed(2);
  const strongEnough = avgStrength >= 3;
  return {
    label: round.label,
    sampleBiased: sample.biased,
    dominantSegment: sample.dominant.segment + ' (' + sample.dominant.share + '%)',
    avgStrength,
    strongEnough,
    passes: !sample.biased && strongEnough,
  };
}

const rushedRound = {
  label: 'Ronda apurada (antes de este modulo)',
  sample: { segments: [
    { segment: 'power_user', count: 6 },
    { segment: 'friend_of_team', count: 1 },
    { segment: 'occasional_buyer', count: 1 },
    { segment: 'new_user', count: 0 },
  ]},
  evidence: [
    { claim: 'Dijo que le encantaria ver recomendaciones', type: 'hypothetical' },
    { claim: 'Dijo que probablemente compraria mas', type: 'hypothetical' },
    { claim: 'Le parecio util el mockup', type: 'stated_opinion' },
  ],
};

const correctedRound = {
  label: 'Ronda corregida (despues de L2-L6)',
  sample: { segments: [
    { segment: 'power_user', count: 3 },
    { segment: 'occasional_buyer', count: 3 },
    { segment: 'new_user', count: 2 },
  ]},
  evidence: [
    { claim: 'Hizo clic en recomendado para ti en el fake door', type: 'observed_behavior' },
    { claim: 'Ignoro la seccion por completo, cero clics', type: 'observed_behavior' },
    { claim: 'Conto una compra pasada influida por una recomendacion', type: 'reported_behavior' },
  ],
};

console.log('=== honestyChecklist() sobre dos rondas de discovery de "recommendations" ===\n');
[rushedRound, correctedRound].forEach((round) => {
  const result = honestyChecklist(round);
  console.log(result.label + ':');
  console.log('  muestra sesgada: ' + result.sampleBiased + ' (domina ' + result.dominantSegment + ')');
  console.log('  fuerza promedio de la evidencia: ' + result.avgStrength + ' (>=3 requerido)');
  console.log('  pasa el checklist: ' + result.passes);
  console.log('');
});

Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:

=== honestyChecklist() sobre dos rondas de discovery de "recommendations" ===

Ronda apurada (antes de este modulo):
  muestra sesgada: true (domina power_user (75%))
  fuerza promedio de la evidencia: 1.33 (>=3 requerido)
  pasa el checklist: false

Ronda corregida (despues de L2-L6):
  muestra sesgada: false (domina power_user (37.5%))
  fuerza promedio de la evidencia: 3.67 (>=3 requerido)
  pasa el checklist: true

La ronda apurada falla en las dos preguntas del checklist, no solo en una: la muestra está dominada por power_user al 75% (exactamente el resultado que ya viste en la lección 4), y la evidencia recogida —dos piezas hypothetical y una stated_opinion— tiene una fuerza promedio de apenas 1.33, muy por debajo del umbral de 3 que el checklist exige. Cualquier conclusión que se hubiera sacado de esta ronda estaría construida sobre una base doblemente débil: gente que ya estaba predispuesta a decir que sí, diciendo cosas que ni siquiera son comportamiento real.

La ronda corregida pasa las dos preguntas: la muestra está balanceada entre tres segmentos, sin ninguno por encima del umbral de dominancia, y la evidencia —dos piezas de observed_behavior y una de reported_behavior— alcanza una fuerza promedio de 3.67. Fíjate en que la ronda corregida incluye, deliberadamente, una evidencia desfavorable (el usuario que ignoró la sección por completo) — el checklist no exige que la evidencia sea toda positiva, exige que sea representativa y del tipo correcto. Una ronda "corregida" que solo cambiara la muestra pero mantuviera evidencia puramente favorable seguiría siendo sospechosa, aunque pasara la primera pregunta.

El checklist completo, en dos preguntas

  1. ¿La muestra está sesgada?sampleBias(), lección 4. Si algún segmento domina con 70% o más, o si falta un segmento relevante, la evidencia recogida —sin importar cuán fuerte sea individualmente— representa solo a una parte del público real.
  2. ¿La evidencia es lo bastante fuerte?rankByStrength(), lección 6. Si la mayoría de las piezas son hypothetical o stated_opinion, incluso una muestra perfectamente representativa está prediciendo comportamiento futuro a partir de opiniones, no de acciones.

Las dos preguntas son independientes, y las dos tienen que pasar. Una muestra representativa con evidencia débil, o una evidencia fuerte de una muestra sesgada, producen el mismo resultado final: una conclusión que no merece la confianza que se le está dando.

Errores comunes

Aplicar el checklist después de haber tomado la decisión, para justificarla. Qué pasa: el equipo ya decidió, informalmente, que recommendations "se ve bien" — y corre honestyChecklist() no para decidir si confiar en la evidencia, sino para tener un documento que respalde una decisión que ya estaba tomada, seleccionando qué evidencia incluir en el análisis a partir del resultado que quieren obtener. Por qué pasa: un checklist con casillas verdes se siente como validación objetiva, y es tentador construir el input del checklist —qué evidencia entra, cómo se define la muestra— para que produzca el resultado deseado, en vez de dejar que el checklist evalúe honestamente lo que ya se recolectó. Cómo detectarlo: si el orden de los eventos fue "decidimos, después corrimos el checklist" en vez de "corrimos el checklist, después decidimos", el checklist está siendo usado como teatro, no como herramienta. Cómo corregirlo: corre honestyChecklist() sobre toda la evidencia y toda la muestra recolectada, antes de que nadie en el equipo exprese una opinión sobre qué conclusión prefiere — el orden importa tanto como el resultado.

Confundir "pasó el checklist" con "la hipótesis está validada". Qué pasa: la ronda corregida del ejemplo de hoy pasa el checklist (passes: true), y alguien concluye que eso significa que recommendations ya está validado y listo para construirse. Por qué pasa: un checklist con un veredicto claro (true/false) se siente como una respuesta final, cuando en realidad solo certifica que la evidencia es confiable para ser sintetizada — no dice todavía qué conclusión arroja esa síntesis. Cómo detectarlo: pregunta qué porcentaje de la evidencia de comportamiento fue favorable versus desfavorable — en el ejemplo de hoy, de las tres piezas de la ronda corregida, una de las dos observaciones de comportamiento fue negativa (ignoró la sección por completo); el checklist pasa, pero la conclusión sobre recommendations sigue sin decidirse. Cómo corregirlo: honestyChecklist() certifica la calidad de la evidencia de entrada, no la decisión de salida — contar las señales, decidir si son suficientes y actualizar el confidence es trabajo explícito del módulo 7, que empieza justo donde termina este checklist.

Ejercicios

Ejercicio 1 — Corre el checklist sobre una muestra mixta. Con esta ronda, calcula manualmente si pasaría honestyChecklist():

const mixedRound = {
  sample: { segments: [
    { segment: 'power_user', count: 4 },
    { segment: 'new_user', count: 4 },
  ]},
  evidence: [
    { claim: 'Hizo clic en el fake door', type: 'observed_behavior' },
    { claim: 'Dijo que le gustaria verlo', type: 'hypothetical' },
  ],
};
Ver solución

Muestra: total 8, power_user 50%, new_user 50% — ningún segmento domina (ninguno ≥70%), ninguno está en cero. sampleBias.biased = false, la primera pregunta pasa. Evidencia: observed_behavior (strength 4) y hypothetical (strength 1), promedio = (4+1)/2 = 2.5, por debajo del umbral de 3. strongEnough = false, la segunda pregunta falla. passes: false — a pesar de tener una muestra perfectamente balanceada, la evidencia recogida es, en promedio, demasiado débil para pasar el checklist completo. Este caso muestra exactamente el punto del ejercicio 3 de la lección 6: una muestra sana no garantiza que la evidencia sea suficientemente fuerte.

Ejercicio 2 — Diseña una ronda que falle solo por la muestra. Diseña un objeto round (con sample y evidence) donde rankByStrength() daría un promedio de strength de al menos 3, pero sampleBias() marcaría biased: true. No hace falta ejecutar Node — verifica manualmente los dos criterios por separado.

Ver solución

Una solución válida: sample: { segments: [{segment: 'power_user', count: 9}, {segment: 'new_user', count: 1}] } (90% power_user, claramente por encima del umbral de dominancia de 70% → biased: true) junto con evidence: [{claim: 'Hizo clic', type: 'observed_behavior'}, {claim: 'Agrego al carrito', type: 'observed_behavior'}] (ambas strength: 4, promedio = 4, por encima del umbral de 3strongEnough: true). El resultado: passes: false, porque aunque la evidencia sea del tipo más fuerte posible, viene casi exclusivamente de power_user — exactamente el caso que muestra por qué las dos preguntas del checklist son independientes y ambas necesarias.

Ejercicio 3 — Explica por qué el orden de las preguntas no importa. honestyChecklist() evalúa sampleBias() y rankByStrength() en el mismo paso, sin que uno dependa del resultado del otro. ¿Cambiaría el veredicto final (passes) si se evaluara primero la fuerza de la evidencia y después la muestra, en vez de al revés?

Ver solución

No cambiaría. passes se define como !sample.biased && strongEnough — una operación AND lógica entre dos condiciones independientes. El operador && en JavaScript evalúa de izquierda a derecha, pero el resultado final de una expresión AND es el mismo sin importar el orden de sus operandos: es true solo si ambas condiciones son true, y false si cualquiera de las dos es false. Esto reflaja un punto conceptual importante de esta lección: las dos preguntas del checklist —¿la muestra está bien? ¿la evidencia es fuerte?— son verificaciones independientes entre sí, no una secuencia donde una condiciona a la otra.

Resumen y siguiente paso

Esta lección no agregó ningún modelo nuevo: combinó sampleBias() (lección 4) y rankByStrength() (lección 6), sin cambios, en un checklist de dos preguntas independientes. Viste el contraste completo entre la ronda apurada de recommendations —que falla las dos preguntas, con una muestra dominada al 75% por power users y una fuerza de evidencia promedio de apenas 1.33— y la ronda corregida, que pasa las dos, con una muestra balanceada y evidencia mayormente de comportamiento observado, incluyendo honestamente una señal desfavorable.

Antes de avanzar deberías poder: recitar las dos preguntas del checklist de memoria; y explicar por qué "pasó el checklist" certifica la calidad de la evidencia de entrada, no la conclusión final sobre la hipótesis.

Con esto cierras las seis lecciones de contenido del módulo. La lección 8, el mini-proyecto, te pide auditar el plan de discovery completo de recommendations con estas mismas dos herramientas —sampleBias() y rankByStrength(), sin ningún cambio— y corregirlo, dejando la evidencia lista para que el módulo 7 la sintetice y actualice, por fin, el confidence que sigue topado en 0.3 desde el primer módulo de esta guía.

Recursos

  • Paul Saffo, entrevista "Betting on Strong Opinions, Weakly Held" — saffo.com/interviews. Sobre la disciplina de someter cualquier conclusión propia a una revisión honesta antes de confiar en ella — el espíritu detrás de un checklist que se corre antes de decidir, no después. En inglés.
  • Teresa Torres, "Assumption Testing: Everything You Need to Know to Get Started" — producttalk.org/assumption-testing. Sobre por qué probar suposiciones de forma disciplinada, con criterios explícitos, es de las prácticas de mayor valor que puede adoptar un equipo de producto. En inglés.
  • Nielsen Norman Group, "Confirmation Bias in UX" — nngroup.com/articles/confirmation-bias-ux. Revisítalo aquí como cierre: sus recomendaciones prácticas —investigar en vez de validar, mantener una mente abierta— son, en esencia, lo que este checklist de dos preguntas convierte en un chequeo verificable. En inglés.