Qué aportó Flux al oficio: las ideas que ya son patrimonio común
Tercera lección del nivel de síntesis: separar la herencia viva de Flux de su envoltorio histórico. Aísla las seis ideas que hoy se dan por supuestas en ecosistemas que nunca pronunciaron la palabra Flux —el flujo unidireccional, el cambio como valor y no como llamada, la reducción pura, la inmutabilidad como técnica de detección, la depuración como derecho del programador y la separación entre lo derivado y lo almacenado—, muestra por qué la primera de todas, convertir un cambio en un dato, es la que hace posibles a las demás, y recorre después la parte de la herencia que conviene no heredar: el culto a la ceremonia, el store único como dogma, el estado del servidor tratado como propio y la creencia de que todo debe ser serializable. Termina rastreando la genealogía en herramientas actuales que jamás se declararon flux.
Flux, la implementación concreta que Facebook presentó en 2014, está muerta. No la usa nadie, su repositorio original es un museo y sus cuatro piezas con nombres de mayúsculas suenan hoy a otra época. Y sin embargo, un programador que empiece mañana con SolidJS, con Svelte, con Pinia o con las signals que trae cualquier framework de este año va a escribir código organizado según ideas que salieron de allí, sin haberlo sabido nunca. Esa es la definición operativa de que una idea ha entrado en el patrimonio común de un oficio: deja de tener nombre propio, deja de citarse y se convierte en lo que la gente considera sentido común. Esta lección hace el trabajo de arqueología inverso —desenterrar el origen de lo que ya damos por obvio— porque distinguir lo que es patrimonio de lo que era moda es lo que te permitirá no confundirlos la próxima vez.
- Aislar las seis ideas de Flux que hoy son patrimonio común del oficio, con independencia de la librería.
- Explicar por qué convertir un cambio en un valor es la idea de la que se derivan todas las demás.
- Identificar la parte de la herencia que conviene no heredar y el daño concreto que cada pieza produce.
- Rastrear la genealogía del patrón en herramientas actuales que nunca se declararon flux.
Seis ideas que ya no tienen dueño
Cuando una idea triunfa del todo, pierde el nombre de quien la trajo. Estas seis se enseñan hoy sin citar a nadie, y sin embargo entraron en el oficio por la misma puerta y a la vez, empujadas por un problema muy concreto de un chat que contaba mal.
El flujo tiene una dirección
El dato baja, el cambio sube. Que esto suene obvio es exactamente la medida del éxito: en 2013 no lo era, y los sistemas eran grafos sin orientación.
El cambio es un valor
Un hecho descrito como dato, no una llamada ejecutada. De aquí salen el registro, la reproducción, la persistencia y la depuración.
La transición es pura
Estado anterior más hecho igual a estado siguiente. La misma firma de Array.reduce, y hoy la de useReducer, incluido en React sin mencionar el origen.
No mutar es una técnica
La inmutabilidad dejó de ser un gusto funcional para ser la forma barata de saber que algo cambió comparando referencias.
Depurar es un derecho
Ver la secuencia de cambios y retroceder por ella se convirtió en una expectativa. Antes era un lujo que nadie pedía.
Derivar en vez de guardar
Guardar lo mínimo y calcular el resto. Selectores, getters, computados y memoizaciones son la misma idea con cuatro nombres.
Cada una de estas ideas se puede fechar y ninguna era evidente antes. La dirección única contradecía frontalmente el enlace bidireccional que era el argumento de venta de los frameworks dominantes de la época. La inmutabilidad como técnica de rendimiento sonaba a contradicción en un lenguaje donde copiar era caro. La depuración retrospectiva no existía como categoría: se depuraba con puntos de interrupción sobre el presente, nunca sobre la historia. Que hoy las cuatro suenen a obviedad no significa que fueran fáciles, sino que la industria ya se movió y olvidó que se había movido.
Ese olvido tiene un efecto secundario que conviene tener presente: cuando una idea deja de citarse, deja también de defenderse, y una idea que nadie defiende puede erosionarse sin que nadie lo note. Es lo que ocurre cada vez que una herramienta nueva ofrece comodidad a costa de una de las seis —permitiendo mutar en cualquier sitio, o haciendo el cambio invisible en lugar de nombrado— y la comunidad la adopta sin advertir qué garantía ha soltado por el camino, hasta que reaparece el bug que esa garantía prevenía.
Merece la pena notar que la pieza central del patrón no se inventó: se reconoció. La firma que combina un acumulador con un elemento para producir un acumulador nuevo lleva décadas en la programación funcional y estaba en el propio lenguaje desde 2009, en Array.prototype.reduce. Lo que hizo Flux fue advertir que un estado de aplicación es un acumulador y que un evento de usuario es un elemento, y que por tanto la historia entera de una sesión es una reducción sobre una secuencia de hechos. Esa lectura es lo que convierte el viaje en el tiempo en una consecuencia trivial en lugar de una funcionalidad difícil: si el estado es el resultado de reducir una lista, retroceder es reducir una lista más corta.
Hay además una séptima aportación que rara vez se cuenta como idea porque es de otra naturaleza: Flux normalizó el vocabulario. Antes de 2014, dos equipos que hablaban del mismo problema no compartían ni una palabra; después, acción, reducer, store, despacho, selector y efecto significan aproximadamente lo mismo en comunidades que no comparten nada más. Ese vocabulario común es lo que permite que un programador que llega de Angular entienda en veinte minutos la arquitectura de un proyecto en Vue, y su valor acumulado sobre una industria entera es probablemente mayor que el de cualquiera de las técnicas concretas.
De la llamada al valor: la idea que hace posibles las demás
De las seis, una es lógicamente anterior a las otras cinco, y no siempre se la reconoce como tal: representar un cambio como un dato en lugar de como una invocación. Un método añadirAlCarrito es una capacidad que se ejerce y desaparece; un objeto que dice que se añadió tal producto al carrito es un hecho que persiste. La diferencia parece filosófica hasta que se enumeran las consecuencias, y entonces se ve que casi todo lo bueno del patrón se sigue de ella.
// Dos formas de expresar el mismo cambio; solo una se puede guardar.
carrito.anadir(producto, 2) // capacidad ejercida
const hecho = { type: 'carrito/anadido', payload: { id: producto.id, n: 2 } }
// Porque el hecho es un dato, estas cinco cosas salen gratis:
registro.push(hecho) // 1. auditoria
localStorage.setItem('cola', JSON.stringify(registro)) // 2. persistencia
canal.postMessage(hecho) // 3. otra pestana o el servidor
const anterior = registro.slice(0, -1).reduce(reducer, inicial) // 4. viaje en el tiempo
expect(reducer(estado, hecho)).toEqual(esperado) // 5. prueba sin dobles
Ninguna de esas cinco líneas es posible con una llamada a método, porque una llamada no se puede serializar, ni enviar, ni volver a aplicar más tarde sobre otro estado. Y de ahí se sigue una consecuencia que trasciende el navegador: los sistemas que llevan décadas necesitando estas cinco propiedades —bases de datos con registro de transacciones, sistemas de control de versiones, arquitecturas de eventos, editores con deshacer profundo— llegaron todos, por su cuenta y mucho antes, a la misma representación. Flux no inventó la idea: la importó al desarrollo de interfaces, que era el último sitio donde faltaba.
flowchart TD IDEA[el cambio es un valor] --> LOG[registro y auditoria] IDEA --> PER[persistencia y cola sin conexion] IDEA --> RED[reproduccion y viaje en el tiempo] IDEA --> TEST[pruebas sin dobles] IDEA --> SYNC[sincronizacion entre pestanas y clientes] LOG --> DEV[herramientas de desarrollo] RED --> DEV style IDEA fill:#f38ba8,color:#11111b style DEV fill:#a6e3a1,color:#11111b
Lo que también trajo y conviene no heredar
Una herencia honesta incluye el pasivo. Flux y su descendencia dejaron cuatro hábitos que envejecieron mal, y conviene nombrarlos porque siguen practicándose por inercia en bases de código nuevas, defendidos con argumentos que ya no aplican.
El culto a la ceremonia convirtió un peaje en una virtud: se llegó a defender que escribir tres archivos por cada acción era bueno en sí mismo, cuando solo era el precio que la trazabilidad costaba en 2015 y que Redux Toolkit abarató después. El store único como dogma confundió una fuente de verdad por dato con un único objeto para toda la aplicación, y produjo árboles gigantes donde el estado de un menú convivía con la sesión. El estado del servidor como estado propio fue el más caro de todos: años escribiendo a mano cargas, errores y caducidades que hoy resuelve una capa de cache, y que nunca fueron estado sino copias. Y la serialización universal llevó a prohibir clases, mapas, conjuntos y fechas en el store por principio, cuando la regla real es mucho más estrecha: debe ser serializable lo que quieras persistir, reproducir o inspeccionar, no todo lo que exista.
Estos cuatro hábitos comparten una estructura: fueron respuestas correctas a restricciones reales que después desaparecieron, y sobrevivieron a ellas convertidas en normas sin fecha. Ese es el modo habitual en que una comunidad técnica acumula deuda cultural, y es más difícil de detectar que la deuda de código porque no se manifiesta como un fallo sino como una discusión que siempre gana la misma parte. La defensa práctica consiste en exigir a cada norma heredada que nombre el problema concreto que evita hoy; si el problema solo existía en una versión anterior de la herramienta, la norma ya no es una norma sino una costumbre.
Hay un quinto elemento del pasivo que no es un hábito sino una consecuencia del éxito: durante varios años, Redux fue la respuesta por defecto a cualquier pregunta sobre estado, incluidas las que no lo eran. Se metieron en el store formularios enteros, posiciones de scroll, estados de animación y banderas de menús abiertos, no porque nadie lo hubiera razonado sino porque era donde iban las cosas. Esa expansión indiscriminada produjo la reacción posterior —la ola de librerías ligeras y el rechazo generalizado a la ceremonia— y explica por qué el juicio popular sobre Redux es tan desproporcionadamente negativo respecto a sus méritos técnicos: la gente no recuerda la herramienta, recuerda haberla usado para cosas que nunca le correspondieron.
Conviene ser igual de exigente con las modas contrarias, porque la reacción también produce dogmas. Que la ceremonia se abarató no significa que la trazabilidad sea gratis; que el store no deba ser único no significa que fragmentarlo en cincuenta módulos sin plano sea mejor; que el estado del servidor tenga su capa no significa que no quede estado de cliente que gestionar. El péndulo del oficio se mueve entre extremos con una regularidad casi cómica, y la única posición estable es la que evalúa cada garantía por lo que cuesta y por lo que compra en un dominio concreto.
El rastro en herramientas que nunca dijeron Flux
La prueba definitiva de que una idea es patrimonio es encontrarla intacta en sitios donde nadie citó la fuente. El rastro es abundante y cruza lenguajes, plataformas y generaciones enteras de herramientas, y en varios de esos sitios la deuda no se reconoce simplemente porque quienes escribieron el código llegaron al mismo diseño por su cuenta, empujados por las mismas restricciones.
React
useReducer es la firma del patrón dentro del framework. Nadie lo llama Flux, y la comunidad lo enseña como una alternativa a useState.
Vue y Angular
Pinia expone acciones y getters con proxies; NgRx despacha acciones hacia reducers puros sobre streams. Vocabularios distintos, garantías idénticas.
Fuera de la web
Bloc en Flutter, la arquitectura de composición en Swift y los modelos de vista con eventos en Android repiten el anillo sin conocerse entre sí.
En los datos
Las caches de cliente que normalizan por identidad y derivan vistas practican la separación entre lo almacenado y lo derivado, con otro nombre.
// Tres ecosistemas sin relacion entre si, escribiendo la misma firma.
// React, en el nucleo del framework y sin citar a nadie:
const [estado, despachar] = useReducer(reducer, inicial)
// Vue con Pinia: la escritura es estrecha y nombrada, la lectura es derivada.
const useCarrito = defineStore('carrito', {
state: () => ({ items: [] as string[] }),
getters: { total: (s) => s.items.length },
actions: { anadir(id: string) { this.items.push(id) } },
})
// Angular con NgRx: la accion es un valor y el reducer sigue siendo puro.
const reducer = createReducer(inicial, on(anadido, (s, a) => ({ ...s, items: [...s.items, a.id] })))
Las tres formas se escribieron en comunidades que apenas se leen entre sí, con lenguajes de plantilla incompatibles y filosofías de reactividad opuestas. Y aun así comparten la parte que importa: hay un hecho con nombre, una transición que lo aplica y una lectura que deriva en lugar de copiar. Las diferencias visibles —si se muta o se devuelve, si el cambio se observa con proxies o se detecta por referencia— pertenecen a la capa de enlace, que es exactamente la variable que el nivel anterior identificaba como la única que cambia de verdad al cruzar de ecosistema.
El caso de useReducer es especialmente instructivo. React incorporó a su núcleo, como un hook más y sin ninguna referencia histórica, la misma firma que Flux había hecho famosa: un estado, un objeto que describe un hecho y una función pura que los combina. La documentación lo presenta como una alternativa a useState para lógica compleja, sin mencionar de dónde viene ni por qué esa forma concreta. Eso no es un olvido, es la señal de éxito completo: la idea ya no necesita genealogía porque nadie la discute.
Y sin embargo, conocer la genealogía sigue teniendo un valor práctico que no es histórico ni sentimental. Quien sabe de dónde sale esa firma sabe también qué viene detrás de ella —que un hecho con nombre se puede registrar, que un registro se puede reproducir, que una reproducción permite depurar— y por tanto reconoce, al usar useReducer en un componente, que acaba de instalar el germen de una arquitectura completa y que puede hacerla crecer sin cambiar de forma. Quien no la conoce ve solo una manera algo más aparatosa de guardar un objeto, la usa cuando el estado tiene muchos campos y nunca descubre para qué servía. La misma línea de código, dos futuros distintos.
Aplica dos pruebas sencillas a cualquier práctica que te propongan. La primera es la prueba del extraño: ¿aparece esta idea, con otro nombre, en un ecosistema que no comparte comunidad con este? Si el mismo patrón está en el backend, en móvil y en tres frameworks incompatibles, es una respuesta a un problema estructural, no una preferencia. La segunda es la prueba del problema: ¿qué se rompe exactamente si la quito? Si la respuesta es un fallo concreto y reproducible, es patrimonio; si es una incomodidad estética o una apelación a lo que se hace ahora, es moda. Las seis ideas de esta lección pasan ambas pruebas; los cuatro hábitos del apartado anterior fallan la segunda.
Si se busca el hilo que une las seis ideas y que sobrevive a la desaparición de la implementación que las trajo, no se encuentra un principio de organización del código sino una postura sobre qué es aceptable exigirle a un sistema, y esa postura se puede enunciar en una frase: un programa debe poder responder a la pregunta de por qué está en el estado en que está. Todo lo demás se sigue. La dirección única existe porque un grafo sin orientación no admite esa pregunta. El cambio como valor existe porque una llamada no deja rastro que interrogar. La pureza existe porque una decisión que consulta el mundo no se puede volver a examinar. La inmutabilidad existe porque un objeto mutado ha destruido la evidencia de lo que era antes. Las herramientas de desarrollo existen porque la respuesta debe poder verse, no deducirse. Y la derivación existe porque un dato calculado no puede mentir sobre su origen mientras que uno copiado sí. Lo que hizo Flux, entonces, no fue proponer una manera de estructurar aplicaciones sino elevar la explicabilidad al rango de requisito no negociable en un ámbito —la interfaz de usuario— donde durante veinte años se había aceptado que el estado era un asunto turbio que se manoteaba hasta que la pantalla quedaba bien. Esa es la razón de que la herencia sea invisible: cuando una exigencia se generaliza deja de percibirse como una postura y pasa a percibirse como la naturaleza de las cosas, hasta el punto de que hoy un programador se indigna si no puede saber por qué cambió un valor, sin sospechar que esa indignación es un producto cultural con menos de quince años. Y esa es también la razón de que sea la parte de la herencia que más conviene custodiar: las librerías caducan y los catálogos se renuevan cada tres años, pero la pregunta por el porqué es la que separa un sistema del que se puede ser responsable de uno que solo se puede vigilar, y quien la abandona por comodidad no está eligiendo una herramienta más ligera, está renunciando a la única propiedad que hacía el trabajo defendible.
- Elige una herramienta o framework que uses y que nunca haya mencionado Flux ni Redux en su documentación.
- Localiza en ella las seis ideas: dirección del flujo, cambio como valor, transición pura, inmutabilidad como técnica, inspección del historial y derivación frente a almacenamiento.
- Anota cuáles aparecen completas, cuáles aparecen a medias y cuáles faltan. Para cada ausencia, describe qué problema concreto reaparece.
- Busca en tu base de código actual los cuatro hábitos del pasivo y estima cuántos archivos los practican hoy sin justificación vigente.
- Aplica las dos pruebas —la del extraño y la del problema— a tres convenciones de estado que tu equipo dé por supuestas. Clasifícalas en patrimonio o costumbre.
- Escribe en un párrafo qué le explicarías a alguien que empieza en 2030 sobre por qué el cambio se representa como un valor, sin nombrar ninguna librería.