Los límites de las FSM planas
Una máquina de estados plana modela bien un problema de una sola dimensión, pero se derrumba cuando el sistema tiene varias dimensiones independientes: cada una multiplica el número de estados, y una máquina con tres ejes de dos valores ya necesita ocho estados y una maraña de transiciones. Es la misma explosión combinatoria que las banderas booleanas, resucitada un nivel más arriba. Esta lección diagnostica la explosión, explica por qué la FSM plana no puede evitarla, e introduce los statecharts de Harel —jerarquía y ortogonalidad— como el álgebra que la disuelve.
La máquina de estados plana es una herramienta magnífica hasta que deja de serlo, y deja de serlo de golpe. Mientras tu problema tenga una sola dimensión —una petición que pasa por fases, un torniquete que se abre y se cierra— la FSM plana es insuperable. Pero en cuanto el sistema combina varios ejes que evolucionan por su cuenta, el número de estados no crece: estalla. Y estalla con la misma ley exponencial que creíamos haber derrotado con las uniones discriminadas dos lecciones atrás. Entender por qué la FSM plana no puede escapar de esa explosión es entender exactamente qué problema vinieron a resolver los statecharts, y por qué el resto de este nivel gira en torno a ellos.
- Diagnosticar la explosión de estados que provoca cada dimensión independiente en una FSM plana.
- Ver que es la misma ley exponencial
k^nde las banderas booleanas, un nivel más arriba. - Entender la jerarquía —estados OR— como factor común que domestica las transiciones.
- Entender la ortogonalidad —estados AND— como la suma que sustituye al producto y disuelve la explosión.
La explosión combinatoria de estados
Modela un formulario con máquina plana. Tiene una fase de trabajo —editando o enviando—, una validez —valido o invalido— y una marca de modificación —pristino o modificado—. Son tres ejes de dos valores, y como una FSM plana solo admite un estado activo a la vez, cada estado tiene que codificar una combinación completa de los tres: editando_valido_pristino, editando_valido_modificado, editando_invalido_pristino, y así hasta ocho. Tres ejes binarios, 2 × 2 × 2 = 8 estados.
Añade un cuarto eje —autenticado o anónimo— y saltas a dieciséis. La ley es implacable: k^n estados, donde n es el número de dimensiones y k los valores de cada una. Es, letra por letra, el mismo 2^n de las banderas booleanas del nivel 9.2. No lo derrotamos: lo empujamos del nivel de los datos al nivel de los estados, donde reaparece intacto.
Pon números a las transiciones para calibrar la gravedad. Con ocho estados y cuatro eventos, la tabla del nivel anterior tiene treinta y dos casillas que revisar; con dieciséis estados, sesenta y cuatro. Y como muchos eventos afectan a una sola dimensión, buena parte de esas casillas son copias de la misma regla escritas una y otra vez con las otras dimensiones arrastradas sin cambio. No es solo que haya demasiados estados: es que la información se duplica, y la duplicación es deuda que hay que mantener sincronizada a mano.
El recuento de estados es solo la mitad del desastre. Un evento que afecta a una sola dimensión —por ejemplo CHANGE, que pasa de pristino a modificado— hay que declararlo por separado en CADA estado compuesto donde esa dimensión sea relevante. Con ocho estados, CHANGE aparece en cuatro transiciones idénticas salvo por las otras dos dimensiones que arrastra sin tocar. Añade estados y ese mismo evento se replica por todos ellos. La máquina se llena de transiciones que dicen lo mismo escrito muchas veces, y mantenerlas sincronizadas a mano es una fuente inagotable de bugs: cambias la regla en tres sitios y olvidas el cuarto.
El fenómeno no es de juguete. Un reproductor de vídeo real combina reproducción —parado, reproduciendo, pausado, buffering—, pantalla —normal, completa—, subtítulos —on, off— y sonido —normal, silenciado—. Cuatro dimensiones que en máquina plana son 4 × 2 × 2 × 2 = 32 estados, cada uno con un nombre compuesto ilegible del tipo reproduciendo_completa_on_silenciado. Nadie mantiene treinta y dos estados y sus transiciones a mano sin equivocarse. La explosión no es una curiosidad académica: es exactamente por lo que las interfaces con varias dimensiones de estado se vuelven inmantenibles cuando se aplastan en una sola máquina plana.
Harel 1987: jerarquía y ortogonalidad
La solución no vino del mundo de la interfaz sino de la ingeniería de sistemas reactivos complejos. En 1987 David Harel publicó “Statecharts: A Visual Formalism for Complex Systems”, extendiendo la FSM con dos operadores de composición que atacan cada mitad del problema. Un statechart no reemplaza la máquina de estados: la dota de un álgebra.
Que la idea naciera en los sistemas empotrados —aviónica, protocolos, controladores— no es casual: son dominios donde una combinación de estados no contemplada no es un pixel mal puesto sino un fallo con consecuencias físicas, y donde por tanto la exhaustividad nunca fue opcional. Que la misma herramienta gobierne hoy el asistente de un formulario en el navegador dice mucho de lo general que es el problema: en cuanto un sistema acumula varias dimensiones de estado, la explosión aparece, dé igual el dominio.
El primer operador es la jerarquía, o estados OR. Un superestado agrupa subestados de los que exactamente uno está activo —de ahí el “OR”—, y las transiciones comunes se declaran una sola vez en el superestado en lugar de repetirse en cada hijo. Si desde cualquier subestado de editando un evento RESET vuelve a pristino, lo declaras en el padre editando y vale para todos los hijos. La jerarquía factoriza el común: mata la replicación de transiciones que veíamos explotar.
stateDiagram-v2
[*] --> Formulario
state Formulario {
[*] --> Editando
Editando --> Enviando : SUBMIT
Enviando --> Editando : ERROR
Enviando --> Enviado : OK
--
[*] --> Pristino
Pristino --> Modificado : CHANGE
Modificado --> Pristino : RESET
--
[*] --> Invalido
Invalido --> Valido : VALIDA
Valido --> Invalido : INVALIDA
}El segundo operador es la ortogonalidad, o estados AND, y es el que disuelve la explosión de raíz. Un estado puede dividirse en varias regiones paralelas separadas por la línea --, y el sistema está simultáneamente en un estado de CADA región —de ahí el “AND”—. En el diagrama, el formulario está a la vez en una fase, en una marca de modificación y en una validez, cada una evolucionando por su cuenta. Las tres dimensiones que la máquina plana multiplicaba en ocho estados aquí conviven como tres regiones independientes: 2 + 2 + 2 componentes activos en lugar de 2 × 2 × 2 estados compuestos. La suma sustituyó al producto.
Fíjate en la elección de las palabras OR y AND, que no es decorativa. Un estado OR expresa exclusión —estás en uno de sus hijos, nunca en dos— y por eso modela dimensiones excluyentes como una fase. Un estado AND expresa concurrencia —estás en todos sus hijos a la vez— y por eso modela dimensiones independientes que coexisten. Elegir OR o AND para agrupar dos estados es, exactamente, decidir si son alternativas o si son ejes ortogonales, la misma pregunta que en el nivel 9.2 separaba una unión de un producto de campos. La correspondencia es literal: OR es la suma de tipos, AND es el producto.
Jerarquía: estados OR
Un superestado con subestados, uno activo. Las transiciones comunes se declaran una vez arriba y valen para todos los hijos.
Ortogonalidad: estados AND
Regiones paralelas activas a la vez. Cada dimensión vive en su región: los estados se suman en lugar de multiplicarse.
Y más: historia y tiempo
Estados de historia que recuerdan dónde estabas, transiciones diferidas por tiempo y guardas. El statechart trae todo el paquete.
Hay un poder de los statecharts que la FSM plana emula fatal: la historia. Un estado de historia recuerda cuál era el subestado activo la última vez que saliste de un superestado, para restaurarlo al volver. Piensa en un reproductor que, al salir de un menú de ajustes, debe regresar exactamente a pausado o reproduciendo según dónde estaba. Sin historia tendrías que duplicar el superestado una vez por cada subestado de retorno posible —otra explosión—; con un solo nodo de historia el statechart lo resuelve con una marca. Es un ejemplo más de que la composición no añade lujo: elimina duplicación.
Del autómata al statechart: qué gana la interfaz
La ortogonalidad no es un truco de dibujo: es un cambio de complejidad. Donde la máquina plana crecía como k^n, el statechart crece como k · n —una región por dimensión, cada una con sus pocos estados—. Un problema con seis dimensiones binarias son sesenta y cuatro estados planos ingobernables, o seis regiones limpias de dos estados cada una. La diferencia entre lo inmantenible y lo trivial es exactamente el paso del producto a la suma.
Vuelve al reproductor de las treinta y dos casillas. Como statechart son cuatro regiones ortogonales —reproducción, pantalla, subtítulos, sonido— con dos a cuatro estados cada una, 4 + 2 + 2 + 2 = 10 estados repartidos en cajas independientes que evolucionan sin pisarse. Silenciar el sonido ya no toca la fase de reproducción, porque viven en regiones distintas; antes, cada una de las treinta y dos combinaciones tenía que declarar por separado qué le ocurría al silenciar. Diez estados legibles contra treinta y dos nombres compuestos: el mismo comportamiento con un orden de magnitud menos de superficie que mantener.
Esta formalización tiene un estándar y una implementación de referencia. SCXML es la especificación del W3C que define la semántica de los statecharts de forma rigurosa, y XState v5 es su encarnación viva en JavaScript en 2026: estados anidados, regiones paralelas, historia, transiciones diferidas y actores, todo con tipado estricto vía setup. La FSM plana que aprendiste a escribir a mano en la lección anterior no desaparece: se convierte en el átomo semántico del que los statecharts son las moléculas. Cada región ortogonal y cada subestado sigue siendo, por dentro, una máquina finita determinista con su tabla de transiciones.
Así se ve el formulario ortogonal como configuración ejecutable en el estilo de XState v5: un estado padre marcado como paralelo y tres regiones que conviven.
const formulario = {
initial: "activo",
states: {
activo: {
type: "parallel", // regiones ortogonales: composicion AND
states: {
fase: { initial: "editando", states: { editando: {}, enviando: {} } },
modificacion: { initial: "pristino", states: { pristino: {}, modificado: {} } },
validez: { initial: "invalido", states: { invalido: {}, valido: {} } },
},
},
},
}
El valor de estado activo ya no es una cadena sino un objeto que nombra un subestado por región: fase en editando, modificacion en pristino y validez en invalido, las tres a la vez. Esa es, letra por letra, la suma sustituyendo al producto —tres coordenadas independientes en lugar de un único identificador compuesto que las multiplicaba—.
Y hay más munición en el arsenal de Harel que la máquina plana no tiene. Las guardas condicionan una transición a un predicado —pasar de enviando a enviado solo si la respuesta trae éxito—; las transiciones diferidas disparan un cambio tras un tiempo sin eventos —volver de guardado a editando a los tres segundos—; y las acciones de entrada y salida ejecutan efectos al cruzar los límites de un estado. Cada una de estas piezas, emulada a mano sobre una FSM plana, exige más estados o más código imperativo disperso; en un statechart es una declaración de una línea. La potencia no está en hacer cosas nuevas, está en declararlas en vez de programarlas.
Un matiz teórico redondea la idea: todo statechart tiene un autómata plano equivalente, obtenido al expandir la jerarquía y multiplicar las regiones ortogonales —el mismo producto cartesiano que tanto temíamos—. Es decir, el statechart no añade poder expresivo sobre la FSM; añade poder de expresión. La máquina plana equivalente existe siempre, pero es justo la maraña ingobernable que el statechart nos ahorra escribir y mantener. Igual que un lenguaje de alto nivel compila a ensamblador sin ser más potente que él, un statechart se reduce a una FSM plana sin ser más potente que ella: solo incomparablemente más humano de leer y de cambiar.
Más allá del statechart: componer máquinas
La ortogonalidad resuelve las dimensiones fijas y conocidas de antemano, pero existe una explosión que ni siquiera ella cubre: la de las entidades dinámicas. Imagina una lista donde el usuario sube archivos y cada subida tiene su propia máquina —en cola, subiendo, hecho, fallido—. No sabes cuántas subidas habrá, así que no puedes declarar una región por archivo: la ortogonalidad exige regiones fijas. La respuesta es subir un peldaño más en la escalera de la composición, hacia el modelo de actores.
Cada subida pasa a ser una máquina independiente —un actor— que se crea al vuelo, lleva su propio estado aislado y se comunica con las demás por mensajes en lugar de por estado compartido. XState v5 integra la idea con invoke y spawn: una máquina padre engendra máquinas hijas, les envía eventos y recibe los suyos, y cada actor vive y muere por su cuenta. Es la misma progresión de siempre —del número al álgebra, de la FSM al statechart, del statechart al sistema de actores— y cada escalón aparece justo cuando el anterior empieza a multiplicarse sin control. Los niveles que siguen recorren esa escalera entera: statecharts a fondo, el modelo de actores, y luego las arquitecturas que imponen disciplina sobre cómo todas esas máquinas mutan su estado.
El reflejo inverso también existe y es igual de dañino: envolver en un statechart un problema que era una FSM plana perfecta. Si tu sistema tiene una sola dimensión —una fase que avanza en línea— la jerarquía y el paralelismo solo añaden ceremonia sin eliminar nada, porque no hay explosión que disolver. La señal para subir de plano es concreta y medible: cuentas las dimensiones independientes de tu problema y hay más de una. Con una sola, la máquina plana del nivel 9.4 es la respuesta correcta, y alcanzar el statechart es sobreingeniería. La herramienta se elige por la forma del problema, jamás por su prestigio.
Cuando una máquina de estados empieza a explotar, la lectura ingenua es que la FSM se quedó pequeña y hay que abandonarla por algo más potente. La lectura correcta es la opuesta: la explosión es información valiosísima sobre tu problema. Que necesites k^n estados es la prueba matemática de que tu sistema tiene n dimensiones genuinamente independientes, y esa independencia es un hecho del dominio que estabas ignorando al aplastarlo en una sola máquina plana. Los statecharts no son una tecnología más grande que sustituye a la FSM; son la FSM más un álgebra de composición, y esa álgebra es sorprendentemente simple: la jerarquía es composición OR —uno de varios— y la ortogonalidad es composición AND —todos a la vez—. Harel no reinventó la máquina de estados, le dio operadores para construir máquinas a partir de máquinas, igual que el álgebra construye expresiones a partir de números. La suma que reemplaza al producto no es una optimización: es reconocer que dos dimensiones ortogonales no deben mezclarse en un solo eje, del mismo modo que en el nivel 9.2 aprendiste que dos datos incompatibles no deben convivir en un solo tipo. El principio es idéntico en las dos escalas: modela lo independiente como independiente y lo excluyente como excluyente, y la complejidad deja de multiplicarse. Quien ve la explosión y huye pierde el diagnóstico; quien la ve y la lee, descubre la estructura oculta de su problema y construye el statechart que la refleja. Ese es el puente hacia todo lo que viene: las máquinas dejan de ser planas y empiezan a componerse.
- Toma el formulario de tres ejes y escribe los ocho estados compuestos de la versión plana; cuenta cuántas veces aparece replicado el evento
CHANGE. - Reescríbelo como statechart con tres regiones ortogonales y comprueba que
CHANGEse declara una sola vez. - Añade una cuarta dimensión y calcula el recuento de estados en la versión plana
2^4frente a la ortogonal2+2+2+2. Anota la brecha. - Identifica en tu formulario una transición común a varios subestados y factorízala con jerarquía —un estado OR— para no repetirla.
- Coge una pantalla real de tu trabajo cuya máquina de estados se te haya ido de las manos y cuenta sus dimensiones independientes: esa cuenta te dice cuántas regiones ortogonales necesitas.
- Abre el editor visual de Stately o el inspector de XState v5 y reconstruye tu statechart para verlo ejecutarse: confirma que estás en un estado de cada región a la vez.