wandres.dev
VISUALIZAR · Stately Studio

Por qué visualizar: el diagrama como formalismo, no como adorno

El nivel 3 enseñó las herramientas; este nivel las convierte en método, y esta lección funda la práctica. Se distingue el dibujo decorativo del modelo informal y del formalismo visual de Harel, se explica por qué el round-trip de las herramientas CASE de UML fracasó mientras el de los statecharts funciona, y por qué todo diagrama mantenido a mano se degrada hasta tener valor negativo. La tesis del nivel entero queda enunciada aquí: la máquina no tiene dos representaciones que sincronizar, sino una sola fuente con varias proyecciones, y esa diferencia estructural es lo que impide que el diagrama mienta.

⏱ 17 min

Casi todo el mundo ha visto un diagrama de cajas y flechas pegado en un documento de arquitectura y ha aprendido, con el tiempo, a no fiarse de él. La desconfianza está justificada: ese diagrama se dibujó una vez, describía el sistema de entonces y nadie lo ha vuelto a tocar desde que el sistema cambió. La lección que sigue no defiende que dibujar sea bueno —eso sería trivial y falso—, sino algo mucho más estrecho y mucho más fuerte: que un statechart pertenece a una categoría distinta de la del dibujo, la categoría que David Harel llamó formalismo visual, y que esa pertenencia cambia la economía entera de la documentación. Un formalismo visual no ilustra el comportamiento: lo define. Y lo que define el comportamiento no puede desincronizarse de él, porque no hay dos cosas que sincronizar.

🎯 Al terminar esta lección sabrás
  • Distinguir el dibujo decorativo, el modelo informal y el formalismo visual con semántica ejecutable.
  • Explicar por qué la deriva entre código y diagrama es inevitable cuando son dos artefactos y no uno.
  • Reconocer la definición como fuente única, y el diagrama, la simulación y la traza como proyecciones suyas.
  • Diagnosticar cuándo un diagrama tiene valor negativo y conviene borrarlo en lugar de actualizarlo.

Dibujo, modelo, formalismo

Tres artefactos que se parecen en la pizarra son radicalmente distintos en su régimen de verdad. El primero es el dibujo: cajas y flechas sin gramática, donde una flecha puede significar llamada, dependencia, flujo de datos o simple cercanía conceptual, según quién la mirase el día que se pintó. El segundo es el modelo informal: hay convenciones, quizá heredadas de UML, pero la semántica vive en la cabeza del equipo y nada impide dibujar una configuración que el sistema real jamás alcanzaría. El tercero es el formalismo visual: una notación gráfica con semántica operacional definida, en la que el diagrama es una sintaxis alternativa de un objeto matemático preciso, y por tanto se puede ejecutar, simular, verificar y traducir sin pérdida.

La prueba de pertenencia a la tercera categoría es sencilla de aplicar y despiadada: si no puedes ejecutar el diagrama, no es un formalismo visual, por muy formal que parezca. Un diagrama de secuencia de UML dibujado con esmero sigue siendo un modelo informal si ninguna herramienta puede recorrerlo; y un statechart garabateado a mano en una servilleta ya pertenece a la tercera categoría en cuanto se transcribe, porque su semántica no depende de la calidad del trazo sino de la notación. La categoría no la determina el aspecto del artefacto, sino si existe una función definida que lleve cada símbolo a un significado computable.

Harel introdujo los statecharts en 1987 con exactamente esa ambición, y el subtítulo de su artículo —una formalidad visual para sistemas complejos— no era retórica. Lo que propuso no fue un estilo de dibujo sino una extensión de los autómatas finitos con jerarquía, ortogonalidad y difusión de eventos, junto con una semántica que dice sin ambigüedad qué ocurre cuando llega un evento en una configuración dada. Que se dibuje bien es una consecuencia agradable de esa formalidad, no su propósito. La confusión de las décadas siguientes —tratar los statecharts como una técnica de diagramación— invirtió causa y efecto, y explica gran parte del escepticismo actual hacia los diagramas.

🖍️

Dibujo

Sin gramática ni semántica. Comunica una intuición y envejece a la velocidad del código. Su verdad depende de la memoria de quien lo pintó.

📐

Modelo informal

Tiene convenciones pero no ejecución. Admite configuraciones imposibles y decisiones no resueltas, y nadie puede comprobarlo mecánicamente.

🧮

Formalismo visual

Notación gráfica con semántica operacional. Se simula, se verifica y se traduce a código sin pérdida porque es el mismo objeto.

🔁

Proyección

No es un artefacto más, sino una vista derivada de la fuente. Cambiar la fuente cambia la vista de inmediato y sin intervención humana.

La distinción no es académica: determina qué preguntas puedes hacerle al artefacto. A un dibujo solo puedes preguntarle qué recuerda su autor. A un modelo informal puedes preguntarle qué estados existen, pero no si una secuencia concreta de eventos es alcanzable. A un formalismo visual puedes preguntarle si el estado expirado es alcanzable desde anonimo sin pasar por autenticado, y obtener una respuesta calculada, no opinada. Esa capacidad de interrogar mecánicamente el modelo es lo que convierte el diagrama en una herramienta de ingeniería y no en un elemento de presentación.

Conviene añadir una precisión que suele omitirse al contar esta historia. Lo que hizo dibujable un autómata de tamaño realista no fue la notación gráfica sino la jerarquía y la ortogonalidad que Harel le añadió: un autómata plano con veinte modos independientes tiene un número de estados que ninguna hoja puede contener, mientras que el mismo comportamiento expresado con estados compuestos y regiones paralelas cabe holgadamente en una pantalla. La visualización es un beneficio de la estructura, no al revés. Quien intenta dibujar una máquina plana grande concluye que los diagramas no escalan, y tiene razón; lo que no escala es su modelo, y el diagrama se limita a decírselo antes que el código.

📝
Harel y Rumpe: la semántica de la semántica

En un artículo de 2004 sobre modelado con significado, Harel y Rumpe insisten en que una notación de modelado necesita tres cosas: sintaxis abstracta, dominio semántico y una función que asigne a cada elemento sintáctico su significado en ese dominio. La mayoría de las notaciones populares tienen la primera y presumen de la tercera sin haber definido nunca la segunda. Los statecharts, y con ellos XState, la tienen definida: el dominio semántico son las configuraciones de estados activos y su evolución ante una secuencia de eventos. Por eso un statechart admite simulación y un diagrama de arquitectura no.

La deriva y el valor negativo

Cuando el código y el diagrama son dos artefactos distintos, la sincronización entre ellos es un trabajo humano, recurrente y sin recompensa inmediata. La teoría de la evolución del software de Lehman lo formuló hace medio siglo: un sistema en uso cambia sin descanso y su complejidad crece salvo que se invierta explícitamente en reducirla. La documentación separada es la primera víctima de esa dinámica, porque la penalización por no actualizarla llega tarde, es difusa y recae sobre otra persona. La deriva no es un fallo de disciplina del equipo: es el resultado esperable de un sistema de incentivos donde el coste de actualizar es inmediato y el beneficio, diferido y ajeno.

flowchart LR
subgraph DOS_ARTEFACTOS
  A[Codigo de ayer] -->|cambia cada dia| B[Codigo de hoy]
  C[Diagrama dibujado a mano]
  B -.deriva silenciosa.-> C
end
subgraph UNA_FUENTE
  D[Definicion serializable] --> E[Diagrama]
  D --> F[Simulacion]
  D --> G[Traza en vivo]
  D --> H[Recorridos de prueba]
end

Hay un matiz que la formulación habitual del problema pasa por alto y que agrava el diagnóstico: un diagrama mantenido a mano no es solo una copia que puede desviarse, es un segundo sistema con su propio trabajo pendiente. Tiene convenciones que alguien decidió, un formato que alguien eligió, una herramienta que hay que instalar y una persona que sabe usarla. Cuando esa persona cambia de equipo, el diagrama deja de actualizarse aunque nadie lo anuncie, y su fecha de defunción no queda registrada en ninguna parte. Los artefactos con un solo cuidador no se abandonan mediante una decisión: simplemente dejan de recibir cuidados.

De ahí se sigue una conclusión incómoda: un diagrama desactualizado no vale menos que ningún diagrama, vale menos que cero. Quien no tiene diagrama sabe que no lo tiene y va al código; quien tiene uno obsoleto razona sobre un sistema que no existe, toma decisiones basadas en transiciones que se eliminaron hace tres meses y descubre el engaño en producción. El coste de la mentira es proporcional a la confianza que inspira, y un diagrama bonito inspira mucha. Por eso la regla práctica es dura: si un diagrama no se genera automáticamente y nadie va a mantenerlo, la decisión correcta no es actualizarlo una vez más, es borrarlo.

La regla admite una formulación operativa que conviene adoptar como política de equipo, porque zanja discusiones estériles. Todo diagrama del repositorio debe declarar en su encabezado a cuál de tres categorías pertenece: derivado, y entonces indica el comando que lo regenera; congelado con fecha, y entonces se lee como una fotografía histórica y nadie debe confiar en él para decidir; o candidato a borrado. No existe la cuarta categoría, la de mantenido por buena voluntad, porque es la que produce todos los diagramas mentirosos que cualquiera puede encontrar hoy en su propio proyecto sin buscar mucho.

Merece la pena contrastar esta situación con la propuesta de la programación literaria de Knuth, que intentó resolver el mismo problema por el camino contrario: en lugar de derivar la descripción del programa, entrelazar ambos en un único documento del que se extraen el código y la prosa. La idea era correcta en su diagnóstico —el problema es la separación— y frágil en su remedio, porque la prosa entrelazada sigue siendo prosa y nada impide que describa algo que el código adyacente ya no hace. Derivar es estrictamente más fuerte que entrelazar: entrelazar acerca los dos artefactos, derivar elimina uno de los dos.

Esto explica el fracaso histórico del round-trip engineering en las herramientas CASE de UML durante los años dos mil. La promesa era exactamente la nuestra —edita el diagrama y obtén el código, edita el código y obtén el diagrama— y no se cumplió por una razón estructural, no por falta de ingeniería. El código fuente contiene mucho más de lo que el diagrama puede expresar, así que la traducción de vuelta era lossy; las herramientas insertaban marcas de generación en los archivos, los desarrolladores las tocaban, y cada ciclo introducía conflictos que solo un humano podía resolver. Round-trip entre dos representaciones de expresividad desigual es, en el mejor de los casos, una fusión perpetua.

⚠️
La asimetría de expresividad es el enemigo

Ninguna herramienta arregla un round-trip entre un lenguaje rico y una notación pobre. La única salida es estructural: dividir el artefacto en una parte estrictamente serializable —estados, transiciones, nombres de acciones y de guardas— y otra parte que es código y que la notación nunca intenta representar. Esa división es literalmente lo que hace la API setup de XState v5, y es la razón técnica de que el ciclo visual funcione hoy donde falló entonces. La lección siguiente lo desarrolla.

Una fuente, varias proyecciones

La máquina de XState es un valor: una estructura de datos anidada, sin funciones en su núcleo si se respeta la disciplina de nombrar las implementaciones en lugar de incrustarlas. Leerla no la ejecuta. Esa propiedad, que el nivel 3 estableció y que aquí se cobra por completo, es la que permite tratar el diagrama como una proyección y no como una copia. La analogía exacta viene de las bases de datos: una vista no es una segunda tabla que haya que mantener en sincronía con la primera, es una consulta sobre la tabla que se evalúa cuando se mira. El diagrama de un statechart es una vista sobre la definición.

import { createMachine } from 'xstate'

export const pedido = createMachine({
  id: 'pedido',
  initial: 'borrador',
  states: {
    borrador: { on: { ENVIAR: 'revision' } },
    revision: {
      on: { APROBAR: 'listo', DEVOLVER: 'borrador' },
    },
    listo: { type: 'final' },
  },
})

// Nada de esto arranca la maquina: solo la interroga como dato.
const nombres = Object.keys(pedido.config.states ?? {})
const finales = nombres.filter((n) => pedido.config.states?.[n]?.type === 'final')

Las proyecciones útiles son al menos cuatro y las cuatro salen de la misma fuente sin intervención manual. Cada una responde a una pregunta distinta y tiene un lector distinto, lo cual explica por qué ninguna sustituye a las demás y por qué el nivel dedica una lección a cada familia.

🗺️

Diagrama estático

Responde a qué estados hay y cómo se llega de uno a otro. Lo consumen la revisión de cambios y la documentación del repositorio.

🎮

Simulación

Responde a qué ocurriría ante esta secuencia de eventos. Valida el comportamiento antes de que exista una sola línea de interfaz.

📡

Traza en vivo

Responde a qué está pasando ahora mismo en esta sesión concreta. Es la depuración y, grabada, el informe de error reproducible.

🧪

Recorridos generados

Responden a qué caminos hay que probar para cubrir el comportamiento. Son el material del nivel anterior sobre pruebas basadas en modelo.

Ninguna de las cuatro es un documento: las cuatro son funciones de la definición, y esa es la razón de que no aparezcan en la lista de tareas pendientes de nadie. Una consecuencia menos obvia es que las proyecciones que no usas también las estás pagando, solo que a mano y peor: si dibujas el flujo en una pizarra para explicárselo a alguien, estás calculando la primera proyección con tu cerebro; si razonas en voz alta sobre qué pasaría con cierta secuencia, estás simulando; si buscas en los registros qué hizo el usuario, estás reconstruyendo una traza. El trabajo se hace igual; la única variable es si lo hace la máquina o tú.

La proyección más infravalorada de las cuatro es la primera, y no por lo que muestra sino por dónde se muestra. Un diagrama derivado que aparece en la petición de cambio convierte la revisión de una modificación de comportamiento en un ejercicio visual de treinta segundos —esta flecha ya no está, esta caja es nueva— para el que no hace falta conocer el archivo ni el lenguaje. La última lección del nivel construye esa tubería. Merece la pena adelantar aquí por qué importa tanto: es el único punto del proceso donde una decisión de comportamiento todavía se puede discutir con quien tiene autoridad para cambiarla y sin haber pagado el despliegue.

Nada de esto es gratis, y el nivel entero se entiende mal si se lee como una promesa sin contrapartida. La condición que sostiene las cuatro proyecciones es que el comportamiento esté modelado como dato, y modelar cuesta: obliga a nombrar estados que en un if anidado no tenían nombre, a declarar eventos que antes eran llamadas directas y a decidir por adelantado cosas que el código imperativo permite dejar implícitas hasta que fallan. La tesis honesta no es que visualizar sea gratis, sino que su coste ya está pagado por quien decidió modelar, y que no cobrarlo —tener la máquina y seguir manteniendo diagramas a mano— es la peor combinación posible de las dos.

Merece la pena nombrar la consecuencia epistemológica, porque es la que ordena el nivel entero. La crítica clásica al modelado, que el mapa no es el territorio, presupone que el mapa se dibuja mirando un territorio independiente y que por eso puede equivocarse. Aquí el territorio se compila desde el mapa: la definición que se dibuja es la que se ejecuta. El mapa no puede desviarse del territorio porque el territorio se deriva del mapa. Es la única configuración conocida en la que la crítica de Korzybski deja de aplicar, y no por virtuosismo del equipo sino por la forma del artefacto.

ℹ️
La disciplina que sostiene la propiedad

La equivalencia entre definición y diagrama se pierde en cuanto se incrustan funciones anónimas dentro de la configuración de estados, porque una función no es un dato dibujable ni serializable. Mantener el núcleo puro —transiciones que referencian acciones y guardas por nombre— no es purismo estético: es el precio exacto de conservar todas las proyecciones. Cada función incrustada en un on es un trozo de comportamiento que el diagrama ya no puede mostrar y que la simulación ya no puede recorrer.

La documentación viva no se mantiene: se deriva

Todo el discurso sobre documentación viva de los últimos veinte años ha girado alrededor de la disciplina —convenciones, revisiones, recordatorios, listas de comprobación antes de fusionar— y ha fracasado con una regularidad que debería habernos enseñado algo. Ha fracasado porque atacaba el síntoma. Mientras la documentación sea un artefacto separado que describe otro artefacto, su corrección depende de un acto voluntario y repetido que nadie tiene incentivo para ejecutar, y ninguna cantidad de proceso convierte un acto voluntario en una garantía. La lección profunda del formalismo visual no es que dibujar ayude a comunicar, sino que la corrección de una descripción debe ser estructural y no cultural. Cuando la descripción es una proyección de la fuente, la pregunta que consume las reuniones —está esto actualizado— deja de tener sentido, igual que no tiene sentido preguntar si el resultado de una suma está actualizado respecto a sus sumandos. Y esto reordena lo que significa modelar: no escribes código y luego lo documentas, escribes una descripción del comportamiento que resulta ser ejecutable, y todo lo demás —el dibujo, la simulación, la traza, los casos de prueba, la conversación con producto— cae como corolario. El diagrama deja de ser el resultado de un trabajo de documentación para convertirse en la forma en que ya mirabas tu propio sistema. Quien entiende esto deja de preguntar si merece la pena mantener diagramas y empieza a preguntar algo mucho mejor: qué partes de mi sistema tienen una forma que permita derivarlos, y qué me está costando que las demás no la tengan.

⚔️ Audita tus diagramas y su régimen de verdad
  1. Reúne todos los diagramas de comportamiento de tu proyecto y clasifícalos en dibujo, modelo informal o formalismo visual según los criterios de la primera sección.
  2. Para cada uno, fecha el último cambio del diagrama y el último cambio del código que describe. La distancia entre ambas fechas es la medida de tu deriva.
  3. Elige el diagrama con más deriva y calcula su valor negativo: enumera qué decisión errónea tomaría alguien que confiase hoy en él.
  4. Toma un flujo real de tu aplicación y escribe su definición mínima con createMachine, sin acciones ni contexto, solo estados y transiciones. Comprueba que la configuración sigue siendo serializable a JSON.
  5. Enumera las cuatro proyecciones de esa definición e indica cuáles usas ya y cuáles no. Las que no usas son documentación que estás pagando a mano teniéndola gratis.
  6. Decide, para cada diagrama de la lista inicial, si se puede derivar o hay que borrarlo. Prohíbete la tercera opción, que es prometer mantenerlo.