wandres.dev
NIVEL DIOS: SÍNTESIS · arquitectura y trade-offs

Ser experto: depuración, core y dominio continuo

La maestría en Solid no es conocer la API sino diagnosticar el grafo: distinguir los dos modos de fallo reactivo —no actualiza porque perdiste el tracking, o actualiza de más por sobre-suscripción— y saber inspeccionar sources, observers y ownership para localizar la causa. Esta lección culminante cubre la depuración reactiva avanzada con las Devtools y las técnicas de aislamiento, cómo leer y contribuir al core de Solid y a solid-primitives, y un plan de dominio continuo que sostiene la experticia mucho después de terminar el track y hacia Solid 2.0.

⏱ 20 min

El track termina donde empieza la maestría de verdad, que no consiste en recordar la firma de createResource sino en ver el grafo. Un experto en Solid, ante un bug reactivo, no prueba cosas al azar: sabe que solo hay dos formas de fallar —o algo no actualiza porque perdiste una arista del grafo, o algo actualiza de más porque tienes una arista de sobra— y va directo a la causa inspeccionando sources, observers y ownership. Esta última lección te da esa mirada diagnóstica, te muestra cómo bajar al código del core para leerlo y contribuir, y te deja un plan para que la experticia no se detenga cuando cierres esta guía. Ser experto es un verbo, no un estado.

🎯 Al terminar esta lección sabrás
  • Diagnosticar cualquier bug reactivo reduciéndolo a uno de sus dos únicos modos de fallo.
  • Inspeccionar el grafo con las Devtools y aislar la causa con untrack, on y logging.
  • Leer el core de Solid y contribuir de forma realista, empezando por lo pequeño.
  • Diseñar un plan de dominio continuo que sostenga la experticia y siga a Solid 2.0.

Depuración reactiva avanzada

Casi todo bug reactivo cae en uno de dos cubos, y nombrarlo es medio arreglo. El primero es «no actualiza»: cambiaste un valor y la UI no reaccionó. La causa es siempre la misma familia —perdiste el tracking—: destructuraste props y leíste el valor una vez muerto el listener; leíste un signal fuera de un scope reactivo, en el cuerpo del componente en vez de dentro del JSX o de un efecto; o guardaste algo en una variable plana creyendo que era reactivo.

El segundo es «actualiza de más»: algo recomputa o repinta cuando no debía. La causa es la simétrica —tienes una arista que sobra—: falta un createMemo que corte la propagación, un equals mal configurado que notifica ante valores iguales, o un efecto que escribe un signal que él mismo lee y se realimenta en bucle. La belleza del diagnóstico es que estos dos cubos son exhaustivos: un grafo de sources y observers no tiene una tercera forma de romperse.

flowchart TD
S[sintoma reactivo] --> A[no actualiza]
S --> B[actualiza de mas]
A --> A1[reactividad perdida: props destructuradas]
A --> A2[lectura fuera del scope reactivo]
A --> A3[valor plano donde hacia falta un signal]
B --> B1[sobre-suscripcion: falta un memo que corte]
B --> B2[equals mal notifica ante valores iguales]
B --> B3[efecto que escribe lo que lee y se realimenta]
A1 --> T[inspecciona sources y observers con las Devtools]
B1 --> T
style A fill:#f9e2af,color:#11111b
style B fill:#f38ba8,color:#11111b
style T fill:#89b4fa,color:#11111b

La herramienta principal es Solid Devtools, que dibuja el grafo y el árbol de ownership y te deja ver, literalmente, qué observa qué: un source con más observers de los que esperabas es un diagnóstico de «actualiza de más» hecho imagen.

Las técnicas complementarias son de bisección. Aísla una lectura sospechosa con untrack para confirmar si esa arista era el problema; fuerza dependencias explícitas con on para ver exactamente de qué depende un efecto; nombra tus computaciones y añade console.log dentro para observar cuándo corren. Y cuando la duda es profunda, mira el output compilado: como el compilador decide qué es reactivo, leer en qué convirtió tu JSX resuelve de raíz los misterios de «por qué esto no rastrea».

El fallo más común de «no actualiza» es también el más instructivo, porque su arreglo revela el mecanismo. Al destructurar props lees el valor una vez, muerto el listener, y la arista nunca se crea; la cura es conservar la lectura como acceso a props, que es un proxy reactivo.

// NO ACTUALIZA: destructurar lee el valor una vez y pierde la arista
function Saludo({ nombre }: Props) { return <h1>Hola {nombre}</h1>; } // roto

// ARREGLADO: acceder por props mantiene la lectura dentro del scope reactivo
function Saludo(props: Props) { return <h1>Hola {props.nombre}</h1>; } // vivo

Y el fallo de «actualiza de más» se aísla con on y untrack: declaras la dependencia real y lees el resto sin suscribirte, de modo que solo lo que debe disparar el efecto lo dispara.

// aislar la causa: si al envolver en untrack el sintoma cambia,
// esa lectura era la arista culpable
createEffect(on(
  () => props.id,                 // dependencia EXPLICITA: solo id lo dispara
  (id) => {
    const extra = untrack(() => props.datos); // leido sin suscribir
    cargar(id, extra);
  }
));

La disciplina que unifica todo esto es negarse a adivinar. Ante un síntoma reactivo, el principiante cambia cosas hasta que «funciona» y no sabe por qué; el experto formula una hipótesis —«esta arista sobra», «esta arista falta»—, la aísla con una herramienta, y confirma o descarta. Depurar deja de ser prueba y error y se vuelve ciencia: observación, hipótesis, experimento controlado. Esa transición mental, de tantear a razonar sobre el grafo, es exactamente el umbral de la experticia.

Leer y contribuir al core

Llega un punto en que la documentación no basta y la respuesta está en la fuente. El ecosistema de Solid es un monorepo de piezas legibles: solid-js con el núcleo reactivo, dom-expressions con el compilador, solid-router y solid-start con el meta-framework, y seroval con la serialización que hace posible el SSR.

El orden de lectura importa, y hay uno óptimo. Empieza por el archivo de signals de solid-js, que define createSignal, createEffect y createMemo sobre la misma mecánica de sources, observers y el listener actual que ya entiendes; sigue por el módulo de ownership, donde viven createRoot, getOwner y la disposición; y solo entonces baja a dom-expressions para ver cómo el compilador teje ambos. Leído en ese orden, el core no es un salto al vacío: es reconocer en TypeScript, capa por capa, lo que este track te explicó en prosa.

Para comprobar que lo entiendes, el núcleo reactivo cabe en un puñado de líneas. Un signal es una lista de observers y un valor; leerlo registra al listener actual, escribirlo notifica a todos. Reconocer este esqueleto en el código real del core deja de ser intimidante cuando lo has escrito tú.

// La esencia del core, sin azucar: la maquina que este track te explico
let listenerActual: (() => void) | null = null;

function signal<T>(valor: T) {
  const observers = new Set<() => void>();
  const leer = () => {
    if (listenerActual) observers.add(listenerActual); // arista de grafo al leer
    return valor;
  };
  const escribir = (v: T) => {
    valor = v;
    for (const obs of [...observers]) obs();            // notifica al cambiar
  };
  return [leer, escribir] as const;
}

function efecto(fn: () => void) {
  const correr = () => { listenerActual = correr; fn(); listenerActual = null; };
  correr(); // al correr, cada signal leido lo registra: tracking automatico
}

Contribuir no exige reescribir ese runtime. El camino real empieza por lo pequeño: una reproducción mínima de un bug en un issue vale oro y es una contribución de pleno derecho; una mejora de documentación aclara el punto en que tú tropezaste; un primitivo en solid-primitives, la colección oficial, aplica el patrón del cuarteto que ya dominas y pasa por revisión de los mantenedores.

Desde ahí se sube hacia el diseño en vivo, que ocurre en el Discord y en las discusiones de RFC. Y el momento para entrar es ahora, porque Solid 2.0 está reescribiendo el núcleo reactivo hacia una reactividad asíncrona transparente: el async/await deja de ser un ciudadano aparte y se integra en el grafo, de modo que un memo puede esperar sin romper el tracking. Entender ese cambio probándolo, reportando y discutiendo es la forma más directa de pasar de consumidor a co-autor del modelo, y quien lo hace ahora llega temprano a la próxima década de Solid.

💡
Reconstruir un primitivo desde cero es el ejercicio que más enseña

Si quieres saber si de verdad entiendes el core, reimplementa createSignal y createMemo con tus propias manos: una lista de observers, una variable global para el listener actual, una función que al leerse se registra y al escribirse notifica. En cincuenta líneas reproduces la esencia de la reactividad de Solid, y en el proceso cada concepto abstracto —tracking automático, push/pull, el listener actual— se vuelve algo que has construido, no algo que has leído. Ese ejercicio, hacerlo una vez al año a medida que el core evoluciona, es la forma más rápida de mantener la comprensión viva y de estar listo para leer el runtime real cuando lo necesites.

Un plan de dominio continuo

La experticia se oxida si no se riega, y un framework que se mueve —Solid 2.0 reescribe el runtime hacia una reactividad asíncrona transparente— la oxida más rápido. Un plan sostenible tiene cinco ejes, cada uno una práctica en presente continuo, no una tarea que se tacha.

Construye algo real y no trivial, porque solo un proyecto de verdad te enfrenta a las decisiones de arquitectura de las lecciones anteriores; los tutoriales no tienen deuda técnica ni casos límite, y ahí es donde de verdad se aprende.

Enseña lo que sabes —escribe, explica, responde en el Discord—, porque enseñar expone justo lo que creías entender y no: no hay auditoría más despiadada de tu modelo mental que tener que hacerlo comprensible a otro.

Lee la fuente cada cierto tiempo, empezando por el archivo de signals y reconstruyéndolo con tus manos, para que tu comprensión siga anclada al código real y no a una versión congelada en tu memoria.

Sigue el trabajo de quien empuja el modelo, en especial el desarrollo de Solid 2.0, que es hacia donde va todo; leer las discusiones de diseño te enseña no solo el qué, sino el porqué de cada decisión. Y contribuye desde lo pequeño para entrar en el bucle donde el framework se piensa, porque se aprende distinto desde dentro que desde fuera.

No es una lista de tareas que se completa: es una práctica que se mantiene, y el que la mantiene es experto de forma perpetua, no titular de un diploma que caduca.

🔬

Diagnosticar

Reduce todo bug a «no actualiza» —tracking perdido— o «actualiza de más» —arista de sobra—. Inspecciona el grafo, no adivines.

🛠️

Contribuir

Empieza por reproducciones, docs y un primitivo en solid-primitives. Lee el archivo de signals: es el track hecho código.

♾️

Sostener

Construye, enseña, lee la fuente, sigue Solid 2.0 y contribuye. La experticia es una práctica, no un diploma.

El experto no conoce más API: ve el grafo y no deja de mirarlo

La diferencia entre alguien competente y un experto en Solid no está en cuántos primitivos recuerda —esos se buscan en un segundo— sino en que ha dejado de ver una lista de funciones y ha empezado a ver una estructura viva: cuando algo falla, no recorre una checklist de “prueba esto, prueba lo otro”, sino que reduce el síntoma a uno de los dos únicos modos de fallo que la arquitectura permite, porque sabe que un grafo de sources y observers solo puede romperse de dos maneras —te falta una arista y entonces algo no actualiza, o te sobra una y entonces algo actualiza de más—, y desde ese diagnóstico binario va directo a inspeccionar qué observa qué en lugar de tantear. Esa mirada, la de ver el grafo y el árbol de ownership superpuestos sobre tu código como si fueran visibles, es la misma que te deja bajar al archivo de signals del core y reconocer en cincuenta líneas de TypeScript todo lo que este track te contó en prosa, y es la que convierte “leer la fuente” de un acto intimidante en un reencuentro; por eso el ejercicio de reconstruir createSignal con tus manos enseña más que cualquier tutorial, porque te obliga a materializar el listener actual y la lista de observers hasta que dejan de ser metáforas. Pero la maestría tiene una segunda mitad que el talento solo no cubre: es un verbo en presente continuo, no un estado que se alcanza y se guarda, porque el framework se mueve —Solid 2.0 reescribe el runtime hacia una reactividad asíncrona transparente— y la comprensión que no se riega se queda obsoleta sin avisar; el experto perpetuo es el que construye algo real que lo obliga a decidir arquitectura, el que enseña y descubre en la explicación los huecos de su propio entendimiento, el que vuelve a la fuente cada tanto y el que contribuye desde lo pequeño para estar dentro del bucle donde el modelo se piensa. Ver el grafo te hace bueno hoy; no dejar nunca de mirarlo, mientras el grafo mismo evoluciona, es lo que te mantiene experto mañana. Ese es el último salto del track y también el único que no termina: has aprendido a aprender Solid para siempre.

⚔️ Conviértete en experto que se sostiene
  1. Toma un bug reactivo real y clasifícalo en “no actualiza” o “actualiza de más” antes de tocar nada; deriva la causa probable desde esa clasificación.
  2. Aísla la arista culpable con untrack y fuerza las dependencias con on; confirma la hipótesis observando cuándo corre una computación nombrada.
  3. Abre las Solid Devtools sobre una app tuya y localiza un source con más observers de los que esperabas; explica por qué y si falta un memo.
  4. Reimplementa createSignal y createMemo en cincuenta líneas con un listener actual y una lista de observers; contrástalo con el archivo real del core.
  5. Escribe tu plan de dominio continuo con un compromiso concreto por eje —construir, enseñar, leer la fuente, seguir Solid 2.0 y contribuir— y una primera contribución pequeña que harás esta semana.