wandres.dev
NIVEL DIOS: SÍNTESIS · la teoría del estado

Errores de arquitectura de estado

El catálogo de patologías del estado, diagnosticadas con el plano de las lecciones anteriores. La tesis unificadora es que todo error de arquitectura es un desajuste de coordenada: o pusiste el dato en la clase equivocada —servidor en un store, estado derivado guardado en lugar de calculado, sopa de booleanos donde tocaba una máquina— o le diste la dosis equivocada de reactividad o disciplina —el store monstruo, el estado sobreelevado, las cadenas de efectos que sincronizan a mano, la ceremonia de Redux para un interruptor—. Cada patología se presenta como una tríada de síntoma, causa y refactor, para que aprendas a diagnosticarla por sus señales visibles antes de conocer su origen, igual que un médico lee la fiebre antes que el cultivo.

⏱ 20 min

Ya sabes ubicar cada dato en el plano y asignarle su herramienta. Esta lección enseña lo contrario y complementario: reconocer cuándo alguien lo hizo mal, incluido tu yo de hace seis meses. Porque los errores de arquitectura de estado no son infinitos ni misteriosos; son un catálogo corto de patologías recurrentes, y todas comparten un mismo diagnóstico de fondo. Un error de arquitectura de estado es siempre un desajuste de coordenada: o pusiste el dato en la clase equivocada del plano, o le diste la dosis equivocada de reactividad o de disciplina. Nada más. Vamos a recorrer las patologías más frecuentes como lo haría un médico: primero el síntoma, lo que ves sin abrir nada; luego la causa, el desajuste de coordenada que lo produce; y por último el refactor, el movimiento en el plano que lo cura. Aprende a leer los síntomas y diagnosticarás una base de código ajena en minutos, sin haber escrito una línea de ella.

🎯 Al terminar esta lección sabrás
  • Adoptar el diagnóstico unificado: todo error de estado es un desajuste de coordenada en el plano.
  • Reconocer las patologías por su síntoma visible antes de conocer su causa.
  • Aplicar el refactor correcto como un movimiento deliberado en el plano, no como un parche.
  • Auditar una base de código ajena leyendo señales, igual que se lee una radiografía.

Errores de clase equivocada: el dato está en la región errónea del plano

La primera familia de errores no es de dosis, sino de ubicación: el dato acabó en una clase que no le corresponde, y arrastra consigo todos los problemas de esa clase ajena. Son los más caros porque parecen funcionar hasta que no lo hacen.

El error cardinal es el estado de servidor viviendo en un store de cliente. El síntoma es inconfundible: banderas de isLoading esparcidas por todas partes, lógica de refetch escrita a mano, datos que se quedan rancios y bugs de invalidación de caché que aparecen y desaparecen. La causa es tratar una copia de algo ajeno como si fuera verdad propia. El refactor es sacarlo del store y dárselo a TanStack Query, que gestiona frescura y revalidación como lo que son, un problema de caché.

El segundo es el estado derivado guardado en lugar de calculado. El síntoma son dos campos que deberían concordar y que, cada cierto tiempo, discrepan —el total del carrito que no cuadra con los artículos, el contador que se queda desfasado—, y casi siempre un efecto escrito para mantenerlos sincronizados a mano. La causa es duplicar una verdad que debería derivarse de otra. El refactor es borrar la copia y calcularla con un useMemo, un selector o un computed, para que sea imposible que difieran.

El tercero es la sopa de booleanos donde tocaba una máquina. El síntoma es la combinación imposible que aparece en pantalla: el spinner girando sobre un mensaje de error, la lista visible mientras el error dice que falló. La causa es modelar modos excluyentes como banderas independientes, de modo que el tipo admite combinaciones que no deberían existir. El refactor es sustituir los booleanos por un estado con transiciones declaradas, una máquina o al menos una unión discriminada, que vuelve irrepresentable lo imposible.

🌐

Servidor en el store

Síntoma: isLoading por todas partes, refetch a mano, datos rancios. Causa: caché tratada como verdad propia. Refactor: TanStack Query.

👯

Derivado guardado

Síntoma: dos campos que deberían concordar se desincronizan. Causa: verdad duplicada. Refactor: derivar con useMemo o selector y borrar la copia.

🍜

Sopa de booleanos

Síntoma: spinner sobre error, estados imposibles en pantalla. Causa: modos excluyentes como flags sueltas. Refactor: máquina o unión discriminada.

🏗️

Store monstruo

Síntoma: todo se re-renderiza ante cualquier cambio. Causa: una jaula para siete clases. Refactor: separar por clase y afinar selectores.

Errores de dosis equivocada: la región es buena, la cantidad no

La segunda familia acierta la clase pero yerra la cantidad: demasiada reactividad, demasiada poca, demasiada disciplina o demasiada poca. Son más sutiles porque el dato está donde debe; lo que falla es su calibración.

El store monstruo es el arquetipo del exceso de centralización con reactividad demasiado gruesa. El síntoma es que cualquier cambio, por pequeño que sea, despierta a media aplicación, y nadie sabe ya por qué se recomputa lo que se recomputa. La causa es haberse saltado la clasificación y usar un único objeto global para las siete clases a la vez. El refactor es partirlo por clase, colocar cada trozo donde se usa y afinar la propagación con selectores o átomos.

Su espejo es el estado sobreelevado. El síntoma es un dato que vive muchos pisos por encima de donde se usa, con cadenas de props interminables o un contexto que hace re-renderizar ramas enteras por un valor que solo mira una hoja. La causa es violar la regla de la altura: no usar el nivel más bajo que resuelve. El refactor es bajar el estado hasta su punto de uso, o poner el límite de contexto justo donde empieza a hacer falta.

La cadena de efectos que sincroniza estado es la patología reactiva por excelencia en React. El síntoma son varios useEffect encadenados que reaccionan unos a otros, renders de más, parpadeos de estados intermedios y carreras difíciles de reproducir. La causa es usar el efecto como mecanismo de propagación —reactividad manual y a destiempo— en lugar de derivar. El refactor casi siempre es calcular el valor durante el render o mover el evento a su origen; la mayoría de esos efectos, sencillamente, sobran.

Y en el otro extremo del eje de la disciplina está la ceremonia injustificada. El síntoma es Redux con tres archivos para un interruptor, o una máquina de estado para una bandera de dos valores. La causa es elegir una coordenada de disciplina más alta de la que el problema pedía. El refactor es bajar: un useState para el interruptor, un useReducer para lo que apenas crece. La disciplina de más no es virtud, es peaje.

flowchart TD
SINT[Sintoma en tu app] --> A{el dato se queda rancio o refetcheas a mano?}
A -->|si| C1[servidor en store: pasalo a TanStack Query]
A -->|no| B{dos campos que deben concordar se desincronizan?}
B -->|si| C2[derivado guardado: calculalo con un memo]
B -->|no| D{ves combinaciones imposibles en pantalla?}
D -->|si| C3[sopa de booleanos: modela con una maquina]
D -->|no| E{todo se re-renderiza ante cualquier cambio?}
E -->|si| C4[store monstruo: separa por clase y afina selectores]
E -->|no| C5[revisa la altura y la dosis de disciplina]
style SINT fill:#f38ba8,color:#11111b
style C1 fill:#a6e3a1,color:#11111b
style C3 fill:#f9e2af,color:#11111b
style C4 fill:#89b4fa,color:#11111b
⚠️
El síntoma más silencioso: la mutación directa

Hay una patología que no grita, y por eso es la más peligrosa: mutar el estado en lugar de reemplazarlo. Empujas un elemento a un array que ya estaba en el store, o editas un campo de un objeto por dentro, y el valor cambia pero la referencia no. El síntoma es una interfaz que no se actualiza aunque los datos sí cambiaron, o un undo y un time-travel que se comportan de forma imposible. La causa es romper la inmutabilidad que casi toda la reactividad y toda la disciplina unidireccional dan por supuesta. El refactor es reemplazar en vez de mutar —con spread, con Immer, con copy— y, mejor aún, hacer el estado inmutable por tipo para que el compilador te impida el pecado. Este bug es silencioso porque no lanza ningún error: simplemente, la pantalla miente.

Diagnosticar es medir la distancia entre lo que el problema pedía y lo que elegiste

El salto de nivel que da esta lección es dejar de ver los bugs de estado como accidentes irrepetibles y empezar a verlos como desajustes medibles en un plano. Un ingeniero junior depura un bug de estado a base de console.log y suerte, persiguiendo el síntoma sin nombre. El experto hace algo distinto: lee el síntoma, lo traduce a una coordenada equivocada y aplica el movimiento en el plano que la corrige. Datos rancios y refetch a mano significan un dato en la clase servidor metido en la clase cliente: el vector de corrección apunta a la caché. Campos que se desincronizan significan una verdad duplicada que debía ser derivada: el vector apunta a borrar la copia. Todo se recomputa significa reactividad demasiado gruesa sobre demasiadas clases juntas: el vector apunta a separar y afinar. Estados imposibles en pantalla significan disciplina demasiado baja para un dato con modos excluyentes: el vector apunta a la máquina. Tres archivos para un interruptor significan disciplina demasiado alta: el vector apunta hacia abajo. Cuando internalizas esto, el refactor deja de ser una reescritura a ciegas y se convierte en un desplazamiento deliberado y mínimo: mueves el dato exactamente hasta la coordenada que su naturaleza pedía, ni un paso más. Y adquieres una habilidad que impresiona en cualquier equipo: abres una base de código que no has visto nunca, lees tres síntomas y diagnosticas su arquitectura de estado sin haber entendido todavía qué hace la app, porque las patologías tienen firmas y tú aprendiste a leerlas. Esa es la diferencia entre apagar fuegos y practicar medicina.

⚔️ Diagnostica una base de código
  1. Abre un proyecto tuyo y busca banderas de isLoading escritas a mano: cada una es un candidato a estado de servidor mal ubicado, muévelo mentalmente a la caché.
  2. Encuentra dos campos de estado que deban concordar y comprueba si uno debería derivarse del otro; si es así, es una verdad duplicada esperando a desincronizarse.
  3. Busca el useEffect cuya única misión es copiar un estado en otro y razona cómo eliminarlo derivando durante el render.
  4. Localiza el store más grande del proyecto y clasifica cuántas de las siete clases conviven dentro; propón por cuál empezarías a partirlo.
  5. Encuentra un caso de ceremonia injustificada —demasiada disciplina para lo que el dato pide— y escribe la versión de menor altura que lo resolvería igual.
  6. Toma el último bug de estado que te costó un día y reescríbelo como una frase con la forma el dato estaba en la coordenada X y debía estar en la Y.