Módulo 7: Synthesizing And Deciding
Seguir, pivotar o matar: `decide`
Descripción
Con synthesize() sabes qué es señal y qué es ruido (lecciones 2 a 4), y con reachedSaturationAt() sabes que ya recolectaste suficiente evidencia como para confiar en esa síntesis (lección 5). Pero todavía falta el paso que le da sentido a todo lo anterior: decidir qué hace el equipo con esa evidencia. Esta lección construye decide(), un modelo con tres salidas posibles —no dos— que refleja las opciones reales que tiene un equipo de producto frente a una apuesta: seguir invirtiendo tal como está, cambiar de enfoque sin abandonar el problema, o parar por completo.
Conexión con el módulo. decide() recibe un número —signalStrength— que resume qué tan fuerte es la evidencia acumulada, y lo compara contra umbrales acordados de antemano. En esta lección esos números son escenarios ilustrativos, elegidos para mostrar las tres salidas posibles; en la lección 7 vas a ver de dónde sale, con un modelo real, el signalStrength que corresponde a recommendations — y en el proyecto de la lección 8 vas a correr decide() con ese número real, no con un escenario de ejemplo.
Una analogía cotidiana: la receta que no sale como esperabas
Estás cocinando una receta nueva y algo no sale como esperabas. Tienes, en realidad, tres opciones — no solo dos. Puedes seguir la receta tal como está, si lo que salió mal fue un detalle menor y el resto va bien encaminado. Puedes cambiar un ingrediente — la receta seguía siendo una buena idea en general, pero una parte específica no funcionaba con lo que tenías disponible, así que ajustas esa parte y sigues cocinando el mismo plato con una variación. O puedes cerrar la cocina por hoy — si lo que salió mal revela que la receta entera no era una buena idea desde el principio, seguir intentando arreglarla plato tras plato solo desperdicia más ingredientes.
Esas tres opciones —seguir, cambiar un ingrediente, cerrar la cocina— son exactamente persevere, pivot y kill. La tentación más común, frente a una apuesta de producto, es pensar que solo hay dos opciones: seguir o abandonar. decide() te obliga a considerar la tercera, la que casi siempre se olvida: la evidencia puede confirmar que el problema es real y vale la pena resolverlo, sin confirmar que la solución específica que estabas construyendo sea la correcta.
Ejemplo trabajado: decide() sobre tres escenarios de evidencia
decide() recibe un signalStrength —un número entre 0 y 1 que resume la fuerza de la evidencia acumulada— y un objeto threshold con dos cortes, persevere y pivot, acordados antes de ver ningún resultado. Si el signalStrength supera el corte de persevere, la apuesta sigue tal como está; si no llega a eso pero sí supera el corte de pivot, hay evidencia real de que el problema importa, pero no de que esta solución específica sea la correcta; si no llega ni a eso, la apuesta se abandona.
// L6: decide() -- dado un puntaje de fuerza de evidencia acumulada
// (signalStrength, 0-1) y los cortes acordados DE ANTEMANO (threshold),
// decide si la apuesta debe seguir (persevere), cambiar de enfoque dentro de
// la misma oportunidad (pivot), o abandonarse (kill).
function decide(signalStrength, threshold) {
if (signalStrength >= threshold.persevere) {
return {
signalStrength,
action: 'persevere',
reason: 'la evidencia acumulada supera el umbral para seguir invirtiendo tal como esta',
};
}
if (signalStrength >= threshold.pivot) {
return {
signalStrength,
action: 'pivot',
reason: 'hay señal real pero no alcanza para seguir sin cambios -- cambiar de solucion dentro de la misma oportunidad',
};
}
return {
signalStrength,
action: 'kill',
reason: 'la evidencia acumulada no alcanza ni para pivotar -- abandonar esta apuesta',
};
}
// Los cortes se acuerdan ANTES de ver ningun resultado, igual que el umbral
// del fake door en el modulo 5.
const THRESHOLD = { persevere: 0.6, pivot: 0.3 };
console.log('=== decide() sobre tres escenarios de evidencia acumulada ===\n');
console.log('umbrales acordados: persevere >= ' + THRESHOLD.persevere + ' | pivot >= ' + THRESHOLD.pivot + '\n');
const scenarios = [
{ label: 'evidencia fuerte (varias señales + fake door positivo)', signalStrength: 0.75 },
{ label: 'evidencia mixta (una señal clara, el resto ambiguo)', signalStrength: 0.45 },
{ label: 'evidencia debil (solo ruido, o el test refuto la hipotesis)', signalStrength: 0.15 },
];
scenarios.forEach((s) => {
const d = decide(s.signalStrength, THRESHOLD);
console.log(s.label + ':');
console.log(' signalStrength=' + d.signalStrength + ' -> ' + d.action.toUpperCase());
console.log(' ' + d.reason + '\n');
});
Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:
=== decide() sobre tres escenarios de evidencia acumulada ===
umbrales acordados: persevere >= 0.6 | pivot >= 0.3
evidencia fuerte (varias señales + fake door positivo):
signalStrength=0.75 -> PERSEVERE
la evidencia acumulada supera el umbral para seguir invirtiendo tal como esta
evidencia mixta (una señal clara, el resto ambiguo):
signalStrength=0.45 -> PIVOT
hay señal real pero no alcanza para seguir sin cambios -- cambiar de solucion dentro de la misma oportunidad
evidencia debil (solo ruido, o el test refuto la hipotesis):
signalStrength=0.15 -> KILL
la evidencia acumulada no alcanza ni para pivotar -- abandonar esta apuesta
Tres escenarios, tres salidas distintas, sin ambigüedad — cada uno cae limpio en uno de los tres rangos definidos por THRESHOLD. Fíjate en el escenario intermedio: signalStrength: 0.45 no alcanza el corte de 0.6 para persevere, pero sí supera el corte de 0.3 para pivot — así que decide() no lo trata como "fracaso", lo trata como "cambia de enfoque". Esa es, exactamente, la opción que un modelo de solo dos salidas —seguir o abandonar— habría forzado a caer en un lado equivocado: con evidencia mixta, matar la apuesta completa desperdiciaría una oportunidad real ya confirmada por el problema, y seguir sin ningún cambio ignoraría que la solución actual, tal como está planteada, no tiene la evidencia suficiente detrás.
Qué significa pivot en el contexto de una apuesta como recommendations
pivot, en el sentido de Eric Ries y The Lean Startup, no significa "abandonar todo y empezar de cero con una idea completamente distinta" — significa cambiar de dirección manteniendo un pie en lo que ya aprendiste. Para recommendations, un resultado de pivot no descartaría la oportunidad completa —"los compradores no encuentran lo que les gustaría"—, que el árbol del módulo 3 ya confirmó como real y de alto impacto. Descartaría, en cambio, la solución específica —recomendaciones personalizadas automáticas— a favor de otra solución que ataca la misma oportunidad, como la alternativa que ya estaba en el árbol desde el módulo 3: categorías curadas a mano por tendencia. El problema del usuario sigue siendo el mismo; lo que cambia es la apuesta sobre cómo resolverlo.
Errores comunes
Tratar cualquier resultado que no sea "matar" como si fuera "seguir sin cambios". Qué pasa: el equipo corre decide(), ve que el resultado no es kill, y lo comunica en la reunión de planeación simplemente como "seguimos con recommendations" — sin distinguir si el resultado real fue persevere (seguir tal como está) o pivot (cambiar de solución). Por qué pasa: kill es la única opción que se siente claramente negativa, así que cualquier otra cosa se agrupa mentalmente como "luz verde", perdiendo la distinción importante entre las otras dos. Cómo detectarlo: si le preguntas al equipo "¿seguimos construyendo exactamente lo que teníamos planeado, o cambia algo?", y la respuesta es "seguimos, nomás", sin poder decir si el resultado fue persevere o pivot, la comunicación perdió información crítica. Cómo corregirlo: comunica siempre las tres etiquetas por su nombre exacto — persevere, pivot o kill — nunca las colapses en un binario de "seguimos" o "no seguimos"; la diferencia entre las dos primeras cambia por completo qué construye el equipo la semana que viene.
Definir los umbrales de persevere y pivot después de calcular el signalStrength real. Qué pasa: el equipo corre toda la síntesis, obtiene un signalStrength de, digamos, 0.55, y recién ahí alguien propone "pongamos el umbral de persevere en 0.5" — casualmente, justo por debajo del número que ya tienen. Por qué pasa: es exactamente el mismo error que ya viste con el umbral del fake door en el módulo 5, y con signalMinUsers en la lección 4 de este módulo — fijar un criterio después de ver el resultado permite, sin que nadie lo note conscientemente, ajustarlo para que confirme lo que el equipo ya quería hacer. Cómo detectarlo: revisa la fecha en que se acordaron los umbrales de THRESHOLD contra la fecha en que se calculó el signalStrength real — si el umbral se decidió después, o el mismo día que el resultado, hay una sospecha razonable de manipulación, intencional o no. Cómo corregirlo: los umbrales de THRESHOLD se acuerdan al mismo tiempo que se diseña el test (módulo 4) o, a más tardar, antes de empezar a sintetizar la evidencia recolectada — nunca después de calcular el número que se va a comparar contra ellos.
Ejercicios
Ejercicio 1 — Calcula el resultado en el límite exacto. Sin ejecutar Node, ¿qué action devuelve decide() si signalStrength es exactamente 0.6, con el mismo THRESHOLD del ejemplo trabajado (persevere: 0.6, pivot: 0.3)? Explica por qué, mirando el operador usado en el código.
Ver solución
persevere. El primer if de decide() usa >= (mayor o igual), no > (estrictamente mayor) — así que signalStrength: 0.6 sí satisface signalStrength >= threshold.persevere (0.6 >= 0.6 es true), y la función devuelve persevere en el primer chequeo, sin llegar siquiera a evaluar el segundo if. Este es un detalle que vale la pena revisar en cualquier función de umbral: si el operador fuera > en vez de >=, un resultado exactamente igual al umbral caería, por el borde, en la categoría de abajo (pivot) — una diferencia pequeña en el código con consecuencias grandes para una apuesta real.
Ejercicio 2 — Diseña un cuarto escenario. Sin ejecutar Node, propón un valor de signalStrength que resultaría en kill, distinto al 0.15 del ejemplo trabajado, y que esté lo más cerca posible del límite con pivot sin cruzarlo.
Ver solución
Cualquier valor menor que 0.3 (el umbral de pivot) da kill; el más cercano al límite sin cruzarlo, dentro de la precisión habitual de estos ejemplos, sería 0.29 — apenas por debajo del corte. Vale la pena notar que un resultado tan cerca del límite (0.29 contra un umbral de 0.3) merece la misma cautela que ya viste con el margen ajustado del fake door en el módulo 5: un signalStrength justo en el borde probablemente amerita conseguir un poco más de evidencia antes de tomar una decisión tan definitiva como kill, en vez de confiar ciegamente en que el número cayó, por muy poco, del lado equivocado.
Ejercicio 3 — Explica por qué pivot no es un tercer nombre para "no sé". En 2-3 frases, explica la diferencia entre un resultado pivot de decide() y una situación real de indecisión del equipo ("no tenemos suficiente información para decidir nada").
Ver solución
Un resultado pivot es una decisión — específica y accionable: la evidencia confirma que el problema del usuario es real (por eso el signalStrength supera el umbral mínimo de pivot), pero no confirma que la solución actual sea la correcta, así que la acción concreta es cambiar de solución dentro de la misma oportunidad, no quedarse parado. Una situación real de indecisión sería, en cambio, un signalStrength que ni siquiera se pudo calcular con confianza — por ejemplo, porque la síntesis de la lección 5 mostró que todavía no se alcanzó saturación—; en ese caso, la respuesta correcta no es correr decide() con un número poco confiable, sino volver a conseguir más evidencia antes de decidir nada.
Resumen y siguiente paso
En esta lección construiste decide(signalStrength, threshold): compara la fuerza de la evidencia acumulada contra dos cortes acordados de antemano, y devuelve una de tres acciones —persevere, pivot o kill—, nunca solo un binario de seguir o parar. Sobre tres escenarios ilustrativos —evidencia fuerte, mixta y débil— la función distinguió con claridad los tres casos, incluyendo el intermedio que un modelo más simple hubiera forzado hacia un extremo equivocado.
Antes de avanzar deberías poder: explicar la diferencia real entre persevere y pivot, con el ejemplo de recommendations frente a la alternativa de categorías curadas; y calcular a mano, para un signalStrength dado, cuál de las tres acciones devolvería decide().
Queda una pregunta pendiente que esta lección dejó como un número de ejemplo: ¿de dónde sale, con un modelo real, el signalStrength de recommendations? La lección 7 responde eso, conectando la síntesis de este módulo con el confidence de RICE que quedó topado en 0.3 desde product-thinking-for-engineers.
Recursos
- Eric Ries, "Pivot, don't jump to a new vision" — startuplessonslearned.com/2009/06/pivot-dont-jump-to-new-vision.html. El post original donde Ries define el pivote como mantener un pie en lo aprendido mientras se cambia de dirección — la fuente directa del framework de esta lección. En inglés.
- Marty Cagan (Silicon Valley Product Group), "The Four Big Risks" — svpg.com/four-big-risks. Un recordatorio útil antes de decidir:
recommendationses, específicamente, un riesgo de valor — ypivot, en este caso, significaría cambiar la solución sin cambiar la apuesta sobre qué problema del usuario vale la pena resolver. En inglés. - Annie Duke, Thinking in Bets — annieduke.com/annie-duke-thinking-in-bets. Ya citado en
product-thinking-for-engineers: su idea de que una buena decisión no se juzga por si el resultado salió bien, sino por si se tomó con el proceso correcto, es exactamente el espíritu de fijarTHRESHOLDde antemano en esta lección. En inglés.