El criterio de elección: peso, dominio, herramientas y equipo
El nivel cierra con la decisión que las cuatro lecciones anteriores prepararon: dado que tu problema ya es una máquina, qué motor la ejecuta. El criterio se apoya en cuatro ejes —peso real medido en su contexto, capacidades discriminantes del dominio, necesidad de que el grafo sea legible por herramientas y personas, y economía del segundo lector— con una regla de desempate que casi nadie aplica: diseñar la frontera del motor antes de elegirlo, para que equivocarse cueste una tarde y no un trimestre.
Conviene decir con precisión qué decide esta lección y qué no, porque el track ya contiene un criterio parecido y confundirlos arruina los dos. Aquel del nivel ocho respondía a una pregunta anterior: si tu problema tiene forma de máquina o si te la estabas imaginando. Este da esa respuesta por hecha y se ocupa de la siguiente, que es puramente instrumental y sin embargo se discute con una carga ideológica desproporcionada: qué ejecuta tu máquina. La tesis es que la elección se decide con cuatro medidas honestas y una decisión estructural que hace reversible el error, y que quien diseña esa reversibilidad puede permitirse equivocarse, mientras que quien la ignora tendrá razón o no durante los próximos tres años sin posibilidad de revisión barata.
- Medir el peso de un motor en su contexto real y no con cifras publicadas fuera de contexto.
- Identificar las capacidades discriminantes que solo un motor completo aporta al dominio.
- Valorar la introspección del grafo según quién necesite leerlo además del intérprete.
- Diseñar la frontera del motor para que la decisión sea reversible con coste de una tarde.
Los cuatro ejes, medidos y no opinados
Cada eje se puede convertir en una medida concreta. Si no puedes producir el número o la lista que cada uno pide, todavía no estás decidiendo: estás prefiriendo.
El peso, en su contexto. La cifra publicada de un paquete es irrelevante por sí sola; lo que importa es su fracción del presupuesto de la ruta que la carga y si esa ruta está en el camino crítico del primer pintado. Un motor completo en una aplicación interna que ya descarga varios megabytes de tablas y gráficos es ruido estadístico. El mismo motor en un formulario incrustado en la página de un tercero puede ser inaceptable. Y hay un factor multiplicativo que casi nadie contabiliza: el coste marginal de la segunda máquina en un motor ya cargado es cero, así que la comparación correcta nunca es una máquina contra un motor, sino todas tus máquinas presentes y previsibles contra ese motor.
Las capacidades discriminantes. Solo cinco cosas separan de verdad a un motor completo de todo lo demás, y ninguna es opinable: jerarquía de estados anidados, regiones paralelas ortogonales, estados de historia, transiciones diferidas por tiempo declaradas y actores invocados con ciclo de vida gestionado. Haz la lista de cuáles usa tu problema hoy. Si son cero, cualquier alternativa ligera basta. Si son dos o más, escribirlas a mano es reimplementar un motor peor.
La legibilidad del grafo. Esta es la que más decisiones voltea cuando se plantea bien. Pregunta quién necesita leer la máquina además del intérprete: una persona de producto que valida el flujo, un generador de pruebas basadas en modelo, alguien que audita un proceso regulado, tú mismo dentro de un año. Solo las definiciones que son dato pueden ser leídas por herramientas; las compuestas con funciones solo pueden ejecutarse.
El segundo lector. El eje humano no es el blando de los cuatro, es el más caro. Un vocabulario conocido traslada el coste de aprendizaje a documentación pública y a conocimiento previo; un intérprete casero lo concentra en la única persona que lo escribió. La pregunta operativa es cuántas personas van a modificar esta máquina en dos años y cuánto tarda cada una en estar en condiciones de hacerlo sin romper nada.
Empieza siempre por las capacidades discriminantes, porque es el único eje que puede cerrar la decisión por sí solo: dos capacidades presentes obligan al motor completo y los otros tres ejes pasan a ser irrelevantes. Sigue por la legibilidad, que descarta de golpe toda la escuela funcional si la respuesta incluye a alguien que no ejecuta código. Deja el peso para el final, cuando ya solo queden opciones válidas, y úsalo como desempate y no como filtro. Decidir en el orden inverso —empezar por los kilobytes— es la causa número uno de máquinas mal modeladas por razones de descarga que nadie llegó a medir.
Existe además un quinto eje que se cuela sin invitación en casi todas estas conversaciones y que conviene desactivar por escrito: la popularidad. El número de estrellas de un repositorio no es una capacidad, no reduce el peso y no hace más legible tu grafo; a lo sumo es un indicador indirecto y ruidoso del eje del segundo lector, porque una herramienta extendida es más fácil de contratar y de documentar. Úsalo solo así, como aproximación imperfecta de un eje que ya tienes medido mejor, y nunca como argumento independiente. La versión honesta de la frase la librería más usada es simplemente hay más gente que ya sabe leerla, y eso ya lo estabas contando.
La tabla de decisión
| Eje | Tabla a mano | Reducer como máquina | Librería ligera | Motor completo |
|---|---|---|---|---|
| Coste añadido, descarga | aprendizaje | nulo | bajo | nulo | bajo | mínimo | medio | notable | alto |
| Transiciones ilegales prohibidas | sí | por convención | sí | sí |
| Jerarquía, paralelismo, historia | no | no | no | sí |
| Actores y efectos con ciclo de vida | manual | manual | básicos | nativos |
| Definición legible por herramientas | sí | no | no | sí |
| Vocabulario compartido y documentado | no | parcial | parcial | sí |
| Reversibilidad de la decisión | total | total | alta | media |
Peso relativo, nunca absoluto
Divide el tamaño del motor entre el presupuesto real de la ruta que lo carga y multiplícalo por las máquinas que amortizará.
Cinco capacidades y ninguna más
Jerarquía, paralelismo, historia, tiempo declarado y actores. Cuéntalas en tu problema de hoy, no en el imaginado.
Quién lee el grafo
Si alguien que no ejecuta código necesita ver el flujo, la definición tiene que ser dato. Ese requisito manda sobre los otros.
El coste del segundo lector
Multiplica el tiempo de puesta en marcha por las personas que tocarán la máquina en dos años. Suele superar a todo lo demás.
flowchart TD A[ya sabes que tu problema es una maquina] --> B[cuantas capacidades discriminantes usa?] B -->|dos o mas| C[motor completo] B -->|una| D[motor completo o revisar el modelo] B -->|ninguna| E[alguien fuera del codigo debe leer el grafo?] E -->|si| F[definicion como dato: tabla propia o motor completo] E -->|no| G[el presupuesto de descarga es critico?] G -->|si| H[tabla a mano o libreria ligera] G -->|no| I[reducer como maquina o almacen tipado] style C fill:#cba6f7,color:#11111b style H fill:#a6e3a1,color:#11111b style I fill:#89dceb,color:#11111b
La hoja que más gente sorprende es la de una sola capacidad discriminante, porque no manda al motor sin más: manda a revisar el modelo. Una única región paralela suele ser dos máquinas independientes que alguien juntó sin necesidad, y un único estado de historia suele ser un campo de contexto que recuerda la última pestaña. Antes de adoptar un motor por una sola capacidad, comprueba si esa capacidad es del dominio o es un artefacto de haber modelado junto lo que estaba separado.
La frontera que hace reversible el error
Todo lo anterior sirve de poco sin la decisión estructural que convierte la elección en algo revisable. Consiste en no dejar que el motor se filtre por la aplicación: la máquina y su intérprete viven detrás de un módulo que expone un contrato mínimo —el estado actual, el envío de eventos y la consulta de qué es legal ahora— y nadie fuera de ese módulo importa nada del motor.
export type EventoPedido = { type: 'PEDIR' } | { type: 'CANCELAR' } | { type: 'REINTENTAR' }
export type FasePedido = 'inactivo' | 'cargando' | 'listo' | 'fallo'
export type Flujo = {
fase: FasePedido
enviar: (e: EventoPedido) => void
puede: (t: EventoPedido['type']) => boolean // legalidad, sin exponer el motor
}
export function useFlujoPedido(): Flujo {
const [snapshot, send] = useActor(maquinaPedido) // unica linea acoplada al motor
return {
fase: snapshot.value as FasePedido,
enviar: send,
puede: (t) => snapshot.can({ type: t } as EventoPedido),
}
}
La prueba de que la frontera funciona es poder implementar el mismo contrato con la opción vecina sin que nada de fuera se entere. Escribirlo una vez, aunque no lo uses, es la manera más barata de verificar que el acoplamiento está donde crees.
// mismo contrato, cero dependencias: la frontera es la unidad de reversibilidad
export function useFlujoPedidoSinMotor(): Flujo {
const [fase, despachar] = useReducer(transicion, 'inactivo' as FasePedido)
return {
fase,
enviar: (e) => despachar(e),
puede: (t) => transicion(fase, { type: t } as EventoPedido) !== fase,
}
}
Fíjate en cómo puede se deriva aquí de la propia función de transición: si aplicar el evento no mueve la fase, el evento no era legal. Esa equivalencia solo se sostiene mientras no existan transiciones internas que cambien contexto sin cambiar de estado, y detectar esa limitación al escribir la implementación alternativa es exactamente el tipo de hallazgo que la frontera está para producir antes de que sea urgente.
El valor de esa frontera es medible: si mañana decides que el motor sobraba y quieres bajar a una tabla propia, o que la tabla se quedó corta y quieres subir al motor, cambias un archivo y las pruebas del contrato te dicen si acertaste. Sin frontera, el motor aparece en cada componente que consulta un estado, y la migración deja de ser una tarde para convertirse en un proyecto que nadie aprueba. Fíjate además en puede: exponer la legalidad como consulta en vez de que cada componente reconstruya sus propias condiciones es lo que impide que la lógica de la máquina se filtre en forma de condicionales dispersos.
Escribe las pruebas contra el contrato de la frontera y no contra la API del motor. Un test que envía eventos y comprueba fases y legalidad sigue siendo válido después de cambiar de librería; un test que inspecciona la estructura interna del intérprete hay que tirarlo entero. Esa disciplina convierte tu batería de pruebas en la especificación ejecutable del comportamiento, que es justamente lo único de esta decisión que no debería depender de la herramienta elegida.
Equivocarse hacia abajo se descubre pronto y duele poco: el día que necesitas jerarquía lo sabes con certeza y migras con la frontera puesta. Equivocarse hacia arriba no se descubre nunca, porque un motor sobredimensionado funciona perfectamente y su coste se paga en cuotas invisibles de ceremonia y de curva por cada persona nueva. Como el error caro es el silencioso, el criterio debe estar sesgado hacia la opción menor y ceder solo ante capacidades nombradas. Y desconfía especialmente del argumento de que la librería es nueva y elegante: en esta franja la novedad rara vez aporta capacidades y casi siempre aporta un vocabulario más que mantener.
Vuelve a pasar los cuatro ejes cuando aparezca una capacidad discriminante nueva, cuando el equipo que mantiene la máquina cambie de tamaño o cuando el flujo se estabilice hasta el punto de que el motor ya no amortice su ceremonia. No la revises porque salga una librería nueva: si no aporta ninguna de las cinco capacidades ni mejora la legibilidad del grafo, la novedad no ha movido ningún eje y la migración solo compraría vocabulario. Una decisión revisable no es una decisión que se rehace continuamente, es una que se puede rehacer barata el día que un eje se mueve de verdad.
Con esto se cierra el nivel de las alternativas, y su recorrido tiene una forma deliberada: empezó midiendo el suelo a mano para tener una referencia honesta, pasó por el motor que ya venía instalado en el framework, recorrió la franja ligera para entender qué se paga con los kilobytes, y estudió en Zag el caso donde la máquina es el activo duradero de toda una industria. Las cinco lecciones responden a la misma pregunta desde ángulos distintos, y por eso el criterio de esta última no es un apéndice: es la única forma de que las cuatro anteriores no se conviertan en preferencias.
La razón de que estas discusiones se envenenen es que se confunden dos cosas de vidas radicalmente distintas. El modelo —qué estados existen, qué transiciones son legales, qué efectos pertenecen a qué estado— es un hallazgo sobre tu dominio y dura lo que dure el negocio; el motor es una convención de escritura y dura lo que dure la moda, que en este ecosistema es notoriamente menos. Cuando alguien defiende una librería como si defendiera una posición, está tratando lo efímero como si fuera lo permanente, y esa inversión tiene un coste práctico enorme: hace que la aplicación se acople a la herramienta en vez de a su propio modelo, y entonces cambiar de herramienta obliga a redescubrir el modelo, que era la parte cara. La estrategia correcta es exactamente la contraria y se resume en una frase: haz que tu modelo sea explícito y que tu motor sea anónimo. Un módulo que expone fases, eventos y legalidad, con pruebas escritas contra ese contrato, te deja empezar con veinte líneas cuando el problema es plano, subir a un motor completo el día que aparezcan la jerarquía o los actores, y bajar de nuevo si el flujo se simplifica, sin que ninguna de esas mudanzas toque una sola pantalla. Quien ha organizado así su código no necesita tener razón en esta elección, y esa es la única forma de tenerla siempre. Lo demás —los kilobytes, la ergonomía de la API, la elegancia de la composición— son detalles reales pero de segundo orden, y merecen exactamente la energía que sobra después de haber protegido lo que sí es irreemplazable.
- Mide el peso real del motor candidato en tu empaquetador, con tu configuración, y exprésalo como porcentaje del presupuesto de la ruta que lo carga.
- Cuenta cuántas máquinas presentes y previsibles amortizarán ese peso. Si son más de tres, recalcula el coste marginal por máquina.
- Enumera cuáles de las cinco capacidades discriminantes usa tu problema hoy. Si sale exactamente una, dedica media hora a comprobar si es del dominio o un artefacto del modelado.
- Escribe la lista nominal de quién necesita leer el grafo además del intérprete. Si incluye a alguien que no ejecuta código, descarta las definiciones no inspeccionables.
- Estima el coste del segundo lector: personas que tocarán la máquina en dos años por horas de puesta en marcha de cada una, para cada opción candidata.
- Extrae una máquina existente detrás de una frontera con fase, envío y legalidad, mueve las pruebas al contrato y cronometra cuánto tardarías en cambiar de motor. Ese número es tu verdadero margen de error.