Módulo 7: Synthesizing And Deciding

Agrupar los hallazgos por problema: `clusterByProblem`

Descripción

Tienes, en este punto, las notas de ocho entrevistas de comportamiento —corridas con el guion que auditaste en el módulo 2— más el resultado del fake door que corriste en el módulo 5. Es mucha información suelta, cada pieza anotada por separado, en el orden en que fue llegando. Antes de poder decir nada útil sobre esa evidencia, hace falta el primer paso, el más simple pero el que más se salta: agruparla por el problema que describe, no por el orden en que la recolectaste, ni por qué comprador la dijo.

Conexión con el módulo. Esta lección construye la primera pieza de synthesize(), la función que vas a completar en la lección 4 y vas a correr sin ningún cambio en el proyecto de la lección 8. clusterByProblem() no cuenta señales todavía —eso es la lección 3—, ni clasifica nada como señal o ruido —eso es la lección 4—. Solo hace una cosa, bien hecha: juntar lo que habla de lo mismo, para que las lecciones siguientes tengan algo ordenado sobre lo cual trabajar.

Una analogía cotidiana: la ropa recién lavada

Después de lavar varias cargas de ropa —tal vez de días distintos, tal vez mezclada por apuro— tienes un montón sobre la cama: camisetas, medias, toallas, todo revuelto. Nadie guarda la ropa en ese estado. Lo primero que haces, antes de doblar nada, es separar por tipo: las camisetas en una pila, las medias en otra, las toallas en otra. No decides todavía cuáles camisetas conservar y cuáles donar —eso viene después, con más criterio—; solo agrupas lo que es del mismo tipo, para poder pensar en cada grupo por separado.

Las notas de las ocho entrevistas de Mercado están, al llegar a este módulo, exactamente como ese montón sobre la cama: una nota del comprador 3 sobre vendedores nuevos, seguida de una nota del comprador 5 sobre el carrito olvidado, seguida de otra nota del comprador 1 —de vuelta— sobre productos que no encuentra. clusterByProblem() es el primer paso de doblar la ropa: nada de juicio todavía, solo juntar lo que es del mismo tipo.

Ejemplo trabajado: clusterByProblem() sobre las notas de las 8 entrevistas de Mercado

Estas son las notas reales, tal como quedaron después de correr las ocho entrevistas con el guion auditado del módulo 2. Cada nota registra qué comprador (user) mencionó qué problema (problem), y si el equipo se lo sugirió antes o el comprador lo trajo por su cuenta (unprompted) — este último campo todavía no se usa en esta lección; vas a necesitarlo recién en la lección 3.

// L2: clusterByProblem() -- agrupa los hallazgos de las entrevistas de
// Mercado por problema, sin todavia distinguir señal de ruido (eso es L3-L4).
function clusterByProblem(findings) {
  const clusters = {};
  findings.forEach((f) => {
    if (!clusters[f.problem]) clusters[f.problem] = [];
    clusters[f.problem].push(f);
  });
  return Object.keys(clusters).map((problem) => ({
    problem,
    mentions: clusters[problem].length,
  })).sort((a, b) => b.mentions - a.mentions);
}

// Las notas reales de las 8 entrevistas de comportamiento (guion del modulo 2),
// ya corridas con compradores de Mercado. Cada nota anota el problema que
// menciono el comprador y si el equipo se lo sugirio antes (unprompted).
const findings = [
  { user: 'buyer_01', problem: 'no descubro productos que me gustarian sin buscarlos por nombre exacto', unprompted: true },
  { user: 'buyer_01', problem: 'no descubro productos que me gustarian sin buscarlos por nombre exacto', unprompted: true },
  { user: 'buyer_02', problem: 'no descubro productos que me gustarian sin buscarlos por nombre exacto', unprompted: true },
  { user: 'buyer_02', problem: 'se me olvida el carrito y no vuelvo a completarlo', unprompted: true },
  { user: 'buyer_03', problem: 'no confio en vendedores nuevos sin reseñas', unprompted: true },
  { user: 'buyer_04', problem: 'no descubro productos que me gustarian sin buscarlos por nombre exacto', unprompted: false },
  { user: 'buyer_05', problem: 'se me olvida el carrito y no vuelvo a completarlo', unprompted: true },
  { user: 'buyer_06', problem: 'no descubro productos que me gustarian sin buscarlos por nombre exacto', unprompted: true },
  { user: 'buyer_07', problem: 'se me olvida el carrito y no vuelvo a completarlo', unprompted: true },
  { user: 'buyer_08', problem: 'la app se cierra sola cuando el celular tiene poca memoria', unprompted: true },
];

console.log('=== clusterByProblem() sobre las 10 notas de las 8 entrevistas ===\n');
const clustered = clusterByProblem(findings);
clustered.forEach((c) => {
  console.log('  ' + String(c.mentions).padStart(2) + ' mencion(es) -- "' + c.problem + '"');
});

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

=== clusterByProblem() sobre las 10 notas de las 8 entrevistas ===

   5 mencion(es) -- "no descubro productos que me gustarian sin buscarlos por nombre exacto"
   3 mencion(es) -- "se me olvida el carrito y no vuelvo a completarlo"
   1 mencion(es) -- "no confio en vendedores nuevos sin reseñas"
   1 mencion(es) -- "la app se cierra sola cuando el celular tiene poca memoria"

Diez notas crudas se convierten en cuatro grupos claros, ordenados por cuántas menciones tiene cada uno. Nota algo importante, que la próxima lección va a corregir: el conteo de 5 mencion(es) para "no descubro productos..." no significa todavía que sea la señal más fuerte de las cuatro — solo significa que hay cinco notas sobre ese problema, sin importar si vinieron de un solo comprador muy hablador o de varios compradores distintos, ni si alguna de esas menciones fue sugerida por el propio equipo en la entrevista. Ese es, exactamente, el hueco que clusterByProblem() deja abierto a propósito: agrupa bien, pero no sabe todavía distinguir cinco menciones de un mismo comprador repitiéndose de cinco compradores distintos diciendo lo mismo sin ponerse de acuerdo.

Por qué agrupar por problem y no por otra cosa

Podrías agrupar las notas de muchas formas distintas: por comprador, por orden cronológico de la entrevista, por qué tan larga fue la respuesta. Ninguna de esas otras formas te acerca a una decisión sobre recommendations. Agrupar por problem es la única que responde la pregunta que de verdad importa en este módulo: ¿qué necesidades del usuario aparecen una y otra vez, sin que nadie se las sugiera? — la misma pregunta que el opportunity solution tree del módulo 3 ya empezó a mapear con las oportunidades problem: '...', y que este módulo retoma con evidencia real recolectada, no con hipótesis de planeación.

Errores comunes

Agrupar por comprador en vez de por problema. Qué pasa: alguien en el equipo arma un resumen de "lo que dijo cada comprador" —ocho párrafos, uno por entrevista— en vez de un resumen de "qué problemas aparecieron y cuántas veces". Por qué pasa: las notas llegan naturalmente organizadas por entrevista —cada sesión produce su propio documento—, y reorganizarlas por tema requiere un paso adicional que se siente opcional. Cómo detectarlo: si le pides al equipo "¿cuántos compradores distintos mencionaron el problema de no encontrar productos?", tienen que releer las ocho entrevistas completas para contestar, en vez de mirar un resumen ya agrupado. Cómo corregirlo: corre clusterByProblem() —o su equivalente hecho a mano en una pizarra— apenas termine la última entrevista, antes de que el equipo empiece a discutir qué significa la evidencia.

Crear un cluster nuevo por cada variación de redacción del mismo problema. Qué pasa: "no descubro productos que me gustarían" y "no encuentro cosas que me interesarían" terminan en dos clusters distintos, aunque describan exactamente la misma necesidad del comprador, solo porque las cadenas de texto no coinciden letra por letra. Por qué pasa: clusterByProblem(), tal como está escrita, agrupa por igualdad exacta de la cadena problem — no entiende sinónimos ni paráfrasis, así que dos notas que dicen lo mismo con otras palabras quedan separadas. Cómo detectarlo: la lista de clusters tiene más entradas de las que el equipo esperaba, y varias de ellas, leídas una al lado de la otra, describen claramente la misma idea. Cómo corregirlo: antes de correr clusterByProblem(), alguien del equipo —no el código— tiene que normalizar la redacción de cada nota a una frase común por problema, exactamente como se normalizó problem al mapear el opportunity solution tree en el módulo 3. El algoritmo agrupa bien lo que ya está bien escrito; no reemplaza el criterio humano de decidir qué cuenta como "el mismo problema".

Ejercicios

Ejercicio 1 — Agrega una novena entrevista. Sin ejecutar Node, si se agregara esta nota nueva a la lista de findings{ user: 'buyer_09', problem: 'se me olvida el carrito y no vuelvo a completarlo', unprompted: true } — ¿cómo cambiaría el resultado de clusterByProblem()? Sé específico sobre qué cluster cambia y cuál queda igual.

Ver solución

Solo el cluster de "se me olvida el carrito y no vuelvo a completarlo" cambia: pasa de 3 mencion(es) a 4 mencion(es), y con eso empataría en primer lugar con "no descubro productos..." si esa nota hubiera tenido cuatro en vez de cinco — pero como "no descubro..." sigue con 5, el orden general no cambia, solo el número del segundo cluster. Los otros dos clusters —"no confío en vendedores..." y "la app se cierra sola..."— quedan exactamente iguales, con 1 mencion(es) cada uno, porque la nota nueva no los toca.

Ejercicio 2 — Cuenta los clusters, no las menciones. Sin ejecutar Node, ¿cuántos clusters distintos produce clusterByProblem() sobre las diez notas del ejemplo trabajado? ¿Coincide ese número con la cantidad de entrevistas (8) o con la cantidad de notas (10)? Explica por qué no tiene que coincidir con ninguno de los dos.

Ver solución

Produce 4 clusters. No coincide con las 8 entrevistas (porque varios compradores mencionaron el mismo problema, así que varias entrevistas caen en el mismo cluster) ni con las 10 notas (porque una nota es una mención individual, y varias menciones —como las 5 de "no descubro productos..."— caen dentro de un solo cluster). El número de clusters depende únicamente de cuántos problemas distintos aparecieron en el conjunto completo de notas — puede ser menor o, en teoría, hasta igual al número de notas, si cada nota describiera un problema que ninguna otra nota mencionó.

Ejercicio 3 — Explica por qué el orden de mentions no basta para decidir. En 2-3 frases, explica por qué el cluster con más mentions ("no descubro productos...", con 5) no es automáticamente el que debería recibir más atención del equipo — adelantando, sin ejecutar nada todavía, el problema que resuelve la lección 3.

Ver solución

mentions cuenta notas, no personas — y una nota puede venir de un comprador que repitió la misma idea dos veces en la misma entrevista, o de una pregunta donde el propio entrevistador ya sugirió el problema antes de que el comprador lo dijera. Un cluster con mentions: 5 podría, en el caso extremo, venir de un solo comprador muy hablador, mientras que un cluster con mentions: 3 podría venir de tres compradores completamente distintos que nunca hablaron entre sí. La segunda situación es evidencia mucho más fuerte que la primera, aunque el número de menciones sea menor — exactamente el problema que countUnpromptedUsers(), en la lección 3, resuelve contando personas, no notas.

Resumen y siguiente paso

En esta lección construiste clusterByProblem(findings): agrupa las notas crudas de las ocho entrevistas de Mercado por el problema que describen, sin todavía juzgar cuáles de esos grupos son evidencia real. Sobre las diez notas del ejemplo trabajado, el resultado fue claro: cuatro problemas distintos, con "no descubro productos que me gustarían..." liderando con 5 menciones — el primer paso, ordenado, hacia una síntesis real.

Antes de avanzar deberías poder: explicar por qué se agrupa por problem y no por comprador ni por orden cronológico; y reconocer, con tus propias palabras, por qué el número de mentions de un cluster, por sí solo, todavía no te dice si esa evidencia es fuerte o débil.

La lección 3 toma exactamente ese hueco y lo cierra: en vez de contar notas, vas a contar personas distintas — el primer paso real hacia separar señal de ruido.

Recursos

  • Teresa Torres, "The Interview Snapshot: How to Synthesize and Share What You Learned from a Single Customer Interview" — producttalk.org/interview-snapshot. El artefacto de una página que un equipo de discovery continuo construye después de cada entrevista — la fuente de las notas que clusterByProblem() agrupa en esta lección. En inglés.
  • Steve Portigal, Interviewing Users (2ª edición) — rosenfeldmedia.com/books/interviewing-users-second-edition. Su capítulo sobre análisis y síntesis distingue exactamente estos dos pasos: separar las notas en piezas pequeñas primero (análisis), y después juntar esas piezas en patrones más grandes (síntesis) — el trabajo que arranca en esta lección. En inglés.
  • Teresa Torres, Continuous Discovery Habitsproducttalk.org/continuous-discovery-habits. El capítulo sobre mapear oportunidades usa el mismo criterio de agrupación por problema que ya viste en el opportunity solution tree del módulo 3 de esta guía — aquí se aplica a evidencia recolectada, no a hipótesis de planeación. En inglés.