wandres.dev
EL PATRÓN FLUX · flujo unidireccional

El problema que motivó Flux

Flux no salió de una pizarra teórica: salió de un bug de notificaciones que Facebook no lograba matar. Esta lección reconstruye el enlace bidireccional del MVC clásico y muestra cómo, al crecer los modelos y las vistas, las actualizaciones se propagan en cascadas que nadie puede seguir ni predecir. El problema no era un fallo puntual sino la forma misma de la arquitectura: cuando cualquier pieza puede escribir en cualquier otra, el número de caminos por los que un cambio viaja crece más rápido que la capacidad de un equipo para razonar sobre ellos, y la causalidad deja de ser rastreable.

⏱ 16 min

Las buenas arquitecturas rara vez nacen de la elegancia; casi siempre nacen del dolor. Flux es el ejemplo perfecto: no fue una teoría que Facebook decidió aplicar, sino la respuesta a un bug que reaparecía sin importar cuántas veces lo arreglaran. El indicador de mensajes sin leer se desincronizaba, alguien lo corregía, y semanas después volvía por otro camino. Ese juego del topo no era mala suerte ni malos programadores: era la firma de un problema estructural en el corazón del MVC con enlace de dos vías. Entender ese problema con precisión es entender por qué existe todo lo que veremos en este nivel.

🎯 Al terminar esta lección sabrás
  • Reconstruir el bug de las notificaciones que empujó a Facebook a repensar su arquitectura.
  • Explicar por qué el enlace bidireccional del MVC clásico genera cascadas de actualizaciones.
  • Mostrar que el número de caminos de propagación crece de forma combinatoria, no lineal.
  • Formular con exactitud el problema que Flux se propondría resolver.

El contador que no se apagaba

La historia canónica la contó Jing Chen en la F8 de 2014. Facebook tenía un indicador de mensajes sin leer en la barra superior. El usuario abría el chat, leía los mensajes, y el contador debía volver a cero. A veces lo hacía; a veces reaparecía solo, o mostraba un número que no correspondía a ningún mensaje real. El equipo lo arreglaba, cerraba el ticket, y el fantasma volvía meses después desde un rincón distinto del código.

El diagnóstico habitual —“un programador se equivocó”— era falso. El problema no vivía en ninguna línea concreta; vivía en la forma de la arquitectura. En un MVC clásico del navegador, la vista y el modelo se enlazan en dos direcciones: la vista escribe en el modelo cuando el usuario interactúa, y el modelo actualiza la vista cuando su dato cambia. Con una vista y un modelo eso es manejable. Con decenas de modelos que se observan entre sí, deja de serlo.

// MVC con enlace de dos vias: la vista escribe en el modelo...
vistaMensajes.on("leer", () => modeloMensajes.marcarLeidos());

// ...y el modelo, al cambiar, actualiza vistas y OTROS modelos...
modeloMensajes.on("cambio", () => {
  vistaContador.repintar();
  modeloNotificaciones.recalcular(); // que a su vez dispara mas cambios
});

// ...y ese otro modelo vuelve a tocar la misma vista, cerrando un ciclo.
modeloNotificaciones.on("cambio", () => vistaContador.repintar());

Fíjate en la última línea: modeloNotificaciones vuelve a tocar vistaContador, que puede volver a disparar modeloMensajes. No hay un principio ni un final claros; hay un grafo con ciclos donde una sola interacción del usuario puede rebotar por varias piezas antes de estabilizarse —si es que se estabiliza.

MVC y el enlace de dos vías

El pecado no es el MVC en sí, sino la bidireccionalidad sin restricciones. Cuando cualquier pieza puede escribir en cualquier otra, el grafo de dependencias deja de ser un árbol y se vuelve una red. Rastrear “por qué cambió este valor” exige recorrer todos los caminos entrantes, y esos caminos se cruzan, se realimentan y a veces forman bucles que el navegador resuelve en un orden que nadie eligió conscientemente.

flowchart LR
VA[vista chat] <--> M1[modelo mensajes]
M1 <--> M2[modelo notificaciones]
M2 <--> VC[vista contador]
VC <--> M1
M2 <--> VB[vista barra]
VB <--> M1
style M1 fill:#f38ba8,color:#11111b
style M2 fill:#fab387,color:#11111b

Cada flecha de doble punta es un canal por el que un cambio puede viajar en ambos sentidos. El diagrama tiene solo seis nodos y ya contiene varios ciclos. Ahora imagina la aplicación real de Facebook, con cientos de modelos y vistas: la red se vuelve un ovillo donde el orden de propagación depende de detalles de implementación —qué escuchador se registró primero, en qué orden emite eventos la librería— que ningún desarrollador tiene en la cabeza.

⚠️
El síntoma engaña, la causa es estructural

El error de gestión más caro fue tratar cada reaparición del bug como un incidente independiente. Cada parche añadía una guardia o un if defensivo, y cada guardia añadía un nuevo camino al grafo, haciendo el siguiente bug aún más difícil de rastrear. La arquitectura se defendía de los parches empeorando. Cuando un mismo síntoma vuelve por rutas distintas, no tienes varios bugs: tienes uno solo, y es la forma del sistema.

La cascada combinatoria

Aquí está la matemática del desastre. Si tienes N piezas y cualquiera puede comunicarse con cualquier otra, el número de canales posibles es del orden de N * (N - 1), es decir, crece con el cuadrado de N. Añadir la pieza número cincuenta no suma un vínculo: suma potencialmente cuarenta y nueve. El coste de razonar sobre el sistema no crece con tu equipo ni con tu código de forma lineal; explota.

Esa explosión combinatoria tiene una consecuencia práctica brutal: llega un tamaño en el que ningún ser humano puede predecir qué ocurrirá al cambiar un dato, porque la respuesta depende de un orden de propagación emergente que nadie diseñó. La aplicación sigue funcionando la mayor parte del tiempo, lo que es peor que fallar siempre, porque esconde el problema hasta que una combinación rara de estados produce el contador fantasma. El sistema no es incorrecto: es impredecible, y en software esas dos palabras acaban significando lo mismo.

La solución no era mejor código, era menos libertad

La lección profunda de este bug es contraintuitiva y define todo lo que sigue. El instinto de un buen ingeniero, ante un sistema frágil, es escribir código más cuidadoso: más pruebas, más guardias, más disciplina. Facebook probó eso durante años y el fantasma volvía, porque la fragilidad no estaba en la calidad del código sino en la topología de la comunicación. La red de canales bidireccionales admitía tantos caminos de causalidad que ninguna cantidad de cuidado podía cubrirlos todos. El salto conceptual de Flux fue renunciar a la flexibilidad en lugar de intentar domarla: si el problema es que cualquier pieza puede escribir en cualquier otra, la cura es prohibirlo por construcción, no vigilarlo por convención. Se sacrifica el enlace de dos vías —que parecía una comodidad— a cambio de una propiedad que vale infinitamente más en un equipo grande: que la causa de cada cambio de estado sea rastreable siguiendo una sola dirección. Predecir se vuelve posible no porque el código sea mejor, sino porque el sistema tiene menos formas de comportarse. Esta idea —que restringir la forma de un sistema puede eliminar clases enteras de bugs, en vez de solo cazarlos uno a uno— es el corazón de Flux, de Redux, de Elm y de casi toda la arquitectura de estado que usarás en 2026. La flexibilidad es un lujo que solo los sistemas pequeños se pueden permitir.

⚔️ Diagnostica una cascada
  1. Dibuja el grafo de dependencias de una pantalla real de tu app: pon un nodo por cada trozo de estado y una flecha por cada lugar donde un cambio provoca otro. Marca los canales bidireccionales.
  2. Busca un ciclo en tu grafo —un camino que salga de un nodo y vuelva a él—. Si lo encuentras, tienes una cascada potencial: describe con palabras qué pasaría si ambos extremos cambian casi a la vez.
  3. Cuenta tus piezas de estado y estima cuántos canales tendrías si todas pudieran hablar con todas. Compara ese número con los canales que realmente usas: la diferencia es la libertad que no necesitas.
  4. Recuerda el último bug de “un valor de la UI que no se actualiza o se actualiza de más”. Reconstruye por qué caminos pudo llegar el estado inconsistente, y pregúntate si el fallo era una línea o una forma.
  5. Escribe en una frase el problema que Flux debe resolver, sin nombrar ninguna solución todavía. Guárdala: las próximas lecciones la responderán pieza por pieza.