Esta es la última lección del curso, y no es un resumen perezoso. Un resumen te recordaría los nombres de las cosas; lo que necesitas ahora es otra cosa: criterio. Saber usar Express, Mongoose o BullMQ es una habilidad que se adquiere en semanas. Saber cuándo usar cada uno, qué se rompe cuando el sistema crece, qué comprobar antes de dejar algo en manos de usuarios reales y cómo justificar una decisión ante un equipo, eso es lo que separa a alguien que "sabe Node" de alguien en quien se confía un servicio en producción.
Esta lección tiene cinco partes: el mapa del viaje, la lista de comprobación de puesta en producción, el marco para tomar decisiones técnicas, lo que este curso no ha cubierto, y cómo seguir aprendiendo. Al final, una última mirada a Escena Viva y una propuesta concreta para quien quiera continuar por su cuenta.
Contenido
- El mapa del viaje: qué construiste y qué demostró
- La lista de comprobación de puesta en producción
- Cómo se toma una decisión técnica
- Deuda técnica: cuándo asumirla y cuándo pagarla
- Lo que este curso no ha cubierto y hacia dónde seguir
- Cómo seguir aprendiendo
- Una última mirada a Escena Viva
- Una propuesta de continuación
- El mapa del viaje: qué construiste y qué demostró
Cada módulo enseñó algo y ese algo tuvo que demostrarse en una pieza real de Escena Viva. Esta tabla es el curso entero en una pantalla:
| Módulo | Lo que aprendiste | La pieza de Escena Viva que lo demostró |
|---|---|---|
| M1 | Node, nvm, JavaScript moderno, ESM y CommonJS | El catálogo de eventos en un array y el primer script que listaba evt-001, evt-002 y evt-003 |
| M2 | Arquitectura, bucle de eventos, promesas, async/await, EventEmitter |
El emisor de eventos de venta y por qué un cálculo síncrono largo congelaba todas las peticiones |
| M3 | fs.promises, path, streams, pipeline, Buffers |
El almacén de sesiones en JSON, resolverDentroDe contra el recorrido de directorios y la exportación de ventas por streams |
| M4 | node:http a mano, cabeceras, estados, ETag/304, fetch |
El primer servidor de la API de aforo, sin framework, con caché por ETag |
| M5 | npm, npm ci, scripts, publicación, npm audit |
El paquete de utilidades de códigos EV-<año>-<6 dígitos> |
| M6 | Express 5, crearAplicacion(), routers, middleware, zod, errores centralizados |
La API de eventos y sesiones con validación y { error: { codigo, mensaje, estado, detalles } } |
| M7 | MongoDB/Mongoose, PostgreSQL/Sequelize, repositorios, índices, transacciones | El aforo de 3000 plazas con 1811 vendidas y la transacción que impide la sobreventa |
| M8 | bcrypt, JWT + refresco rotatorio, autenticar/exigirRol, política pura, OWASP |
El registro de Lucía y Marc, los roles asistente/organizador/administrador y las entradas que solo ve su dueño |
| M9 | Mocha, Chai, Sinon, supertest, fábricas, cobertura, depuración | La batería que impide vender la butaca 3001 y que ningún cambio reintroduzca el fallo |
| M10 | cluster, worker threads, Redis, BullMQ, rendimiento, REST avanzado, GraphQL |
El envío de entradas en cola, la caché del catálogo, la idempotencia del pago y el p99 bajo carga |
| M11 | Config con zod, pino, prom-client, sondas, PM2, Docker, PaaS, CI/CD |
El despliegue completo con docker compose, /salud/listo y la tubería que publica sola |
| M12 | Cuatro productos satélite completos | Soporte en directo, tienda de merchandising, magazine y herramienta interna |
Lee la última columna de arriba abajo. No es una lista de ejercicios: es la historia de una empresa que empieza con un array en un fichero y termina con una plataforma operada de verdad. Esa continuidad es la lección más difícil de transmitir y la más valiosa que te llevas.
- La lista de comprobación de puesta en producción
Esto es lo que hay que revisar antes de que un usuario real toque el sistema. No es aspiracional: cada punto se puede comprobar hoy en tu proyecto.
Corrección
- [ ] Las pruebas pasan en CI, no solo en tu máquina (M9, M11).
- [ ] La cobertura es razonable donde importa: dominio, cálculo de importes, transiciones de estado, autorización. Un 90 % global con el cálculo de dinero sin probar es un número que miente.
- [ ] Existe al menos una prueba de integración por flujo crítico: comprar una entrada, pagar un pedido, publicar un artículo.
- [ ] Los casos límite están probados: carrito vacío, cero resultados, última unidad, fecha en el pasado.
- [ ] El linter y el formateador se ejecutan en CI y bloquean el
merge.
Seguridad
- [ ]
npm auditsin vulnerabilidades altas o críticas sin justificar (M5), y actualizaciones automatizadas. - [ ] Ningún secreto en el repositorio. Todos por variables de entorno, validadas con zod al arrancar (M11). Si alguna vez se filtró uno, se rota: borrarlo del código no lo borra del historial de git.
- [ ] HTTPS obligatorio, con
HSTS; cookieshttpOnly,SecureySameSite(M8). - [ ]
helmet,corscon lista de orígenes explícita, límites de tamaño de cuerpo (M6). - [ ] Límite de frecuencia en autenticación, registro, recuperación de contraseña y endpoints caros (M6, M8).
- [ ] Autorización probada, no supuesta: por cada endpoint que devuelve datos ajenos posibles, una prueba que confirme el 403 o el 404 (M8, 12-04).
- [ ] Toda entrada validada con zod, incluidos los eventos de socket (12-01) y los parámetros de consulta.
- [ ] Ficheros subidos validados por número mágico, renombrados por el servidor y servidos desde otro origen (M3, 12-03).
- [ ] Salida escapada al mostrar; HTML de usuario saneado con lista blanca (12-03).
Fiabilidad
- [ ] Apagado ordenado:
SIGTERMdeja de aceptar conexiones, termina las peticiones en curso, cierra sockets, colas y pool de base de datos, y sale (M11, 12-01). - [ ] Sondas
/salud/vivoy/salud/listodiferenciadas: viva no es lo mismo que lista para recibir tráfico (M11). - [ ] Tiempo límite en toda llamada externa.
AbortSignal.timeoutenfetch(M4); tiempos de conexión y consulta en la base de datos. Una llamada sin tiempo límite es una fuga de recursos esperando a ocurrir. - [ ] Reintentos con retroceso exponencial y solo en operaciones idempotentes (M10).
- [ ] Idempotencia donde el reintento es inevitable: pagos, webhooks, trabajos de cola (M10, 12-02).
- [ ] Trabajos de cola con
attempts,backoffy cola de fallidos revisada por alguien. - [ ] Migraciones aplicadas en un paso de
releaseseparado, nunca al arrancar cada réplica (M11).
Observabilidad
- [ ] Registros estructurados con pino, con
idPeticionen cada línea y un registrador hijo por petición (M11). - [ ] Nunca se registran contraseñas, tokens, tarjetas ni datos personales innecesarios.
- [ ] Métricas con
prom-client: latencia por ruta, tasa de error, profundidad de las colas, aciertos de caché (M11). - [ ] Panel con las cuatro señales de oro: latencia, tráfico, errores y saturación.
- [ ] Alertas por síntoma, no por causa. Alerta por "el p99 de compra supera 2 s" o "hay pedidos en
pendiente_pagode más de una hora", no por "la CPU está al 80 %". La CPU alta puede ser inofensiva; un cliente que no puede pagar, nunca lo es. - [ ] Cada alerta tiene un procedimiento asociado. Una alerta sin acción es ruido que enseña al equipo a ignorar alertas.
Operación
- [ ] Migraciones reversibles o, al menos, compatibles hacia atrás durante un despliegue (añadir columna, desplegar, migrar datos, borrar columna en un despliegue posterior).
- [ ] Copias de seguridad probadas. Una copia que nunca se ha restaurado no es una copia: es una esperanza. Restaura en un entorno aparte con calendario fijo.
- [ ] Plan de reversión documentado y ensayado: cómo se vuelve a la versión anterior y qué pasa con los datos escritos por la nueva.
- [ ] API documentada con OpenAPI y versionada (M10); los cambios incompatibles pasan por versión nueva.
- [ ]
READMEcon: cómo levantar el entorno, qué variables hacen falta y a quién avisar si algo se rompe.
Si tu proyecto no supera esta lista, no significa que esté mal: significa que sabes exactamente qué te falta. Eso ya es una ventaja enorme.
- Cómo se toma una decisión técnica
Las decisiones que has visto en el curso no salieron de una preferencia personal. Siguieron —explícita o implícitamente— este marco:
flowchart TD R["1. Requisito<br/>que problema real resuelvo"] C["2. Restricciones<br/>equipo, plazo, presupuesto, volumen"] O["3. Opciones<br/>al menos dos reales"] T["4. Compromiso<br/>que gano y que pierdo con cada una"] D["5. Decision<br/>reversible o no reversible"] R --> C --> O --> T --> D D -->|"reversible"| P["decide rapido y prueba"] D -->|"no reversible"| L["decide despacio y documenta"]
El paso 5 es el que más gente se salta y el que más tiempo ahorra. Pregunta siempre: ¿cuánto cuesta deshacer esto?
- Elegir una librería de validación: reversible en una tarde. Decide rápido.
- Elegir el modelo de datos de los pedidos: casi irreversible una vez hay datos reales. Decide despacio.
Tres ejemplos del propio curso, con el marco aplicado:
| Decisión | Requisito y restricciones | Opciones | Compromiso | Resultado |
|---|---|---|---|---|
| JSON en fichero frente a base de datos (M3 → M7) | Al principio: un catálogo pequeño, sin concurrencia. Después: aforo compartido y ventas simultáneas | Fichero JSON / MongoDB / PostgreSQL | El fichero es simple pero no tiene transacciones ni consultas ni concurrencia | Fichero mientras el requisito fue "guardar datos"; base de datos en cuanto apareció "no vender la misma butaca dos veces". La decisión no fue mala: caducó |
| Sesiones frente a JWT (M8) | API consumida por front-end y móvil, varias réplicas sin estado compartido | Sesión en servidor / JWT sin estado / JWT corto + refresco en cookie | La sesión revoca al instante pero necesita almacén compartido; el JWT escala pero no se revoca hasta que caduca | JWT de 15 min más refresco rotatorio: el compromiso explícito entre escalado y revocación. Ni dogma ni moda: una ventana de exposición acotada |
| REST frente a GraphQL (M10) | Clientes distintos con necesidades de datos distintas; caché HTTP valiosa para el catálogo | Solo REST / Solo GraphQL / Ambos | GraphQL evita el exceso y el defecto de datos, pero complica la caché HTTP, el límite de frecuencia y el análisis de consultas | REST para el núcleo y GraphQL para las vistas compuestas. Con DataLoader, porque sin él el problema N+1 llega solo |
| Caché en proceso frente a Redis (M10) | Catálogo leído miles de veces, varias réplicas | Map en memoria / Redis / ambos |
El Map es instantáneo y gratis, pero cada réplica tiene su copia y la invalidación no cruza procesos |
Redis, por la misma razón que el adaptador de sockets del 12-01: el estado local deja de ser válido en cuanto hay más de un proceso |
Registrar las decisiones: los ADR. Un ADR (Architecture Decision Record) es un fichero corto en el repositorio, uno por decisión relevante:
# ADR 007: JWT de acceso corto con refresco rotatorio
Fecha: 2026-03-14
Estado: aceptada
## Contexto
La API sirve a un front-end web y a una aplicación móvil, y se despliega
con varias réplicas sin estado compartido. Necesitamos autenticación que
escale y que permita revocar el acceso de una cuenta comprometida.
## Decisión
Token de acceso JWT de 15 minutos + token de refresco rotatorio en cookie
httpOnly, con familias revocables.
## Consecuencias
- Positivas: sin almacén de sesiones en la ruta caliente; escala horizontalmente.
- Negativas: un acceso robado sigue siendo válido hasta 15 minutos.
- Mitigación: revocación de familia al detectar reutilización de refresco.
## Alternativas descartadas
- Sesión en Redis: revocación inmediata, pero una consulta por petición
y un punto de fallo más en la ruta crítica.
- JWT de larga duración: descartado, no hay forma sensata de revocar.Lo valioso de un ADR no es el formato: es que dentro de dos años alguien —probablemente tú— leerá "¿por qué demonios está esto así?" y encontrará la respuesta. Sin ADR, las decisiones se reabren cada seis meses con menos información que la primera vez.
- Deuda técnica: cuándo asumirla y cuándo pagarla
Deuda técnica no significa código malo. Significa una decisión que acelera hoy a cambio de un coste futuro, igual que un préstamo. Como un préstamo, puede ser una excelente idea o una ruina, y la diferencia está en si es consciente y si se paga.
| Deuda razonable | Deuda peligrosa |
|---|---|
| Guardar en JSON mientras validas si el producto interesa | No tener pruebas en el cálculo de importes |
Un TODO con fecha y responsable para paginar un listado que hoy tiene 40 filas |
Autorización a medias "que ya arreglaremos" |
| Aplazar GraphQL hasta tener un segundo cliente | Secretos en el código "solo en desarrollo" |
| Copiar y pegar una función dos veces antes de decidir la abstracción | Migraciones irreversibles sin copia de seguridad |
| Cachear con TTL antes de construir la invalidación fina | Dependencias sin actualizar durante años |
La regla práctica: la deuda en seguridad, en datos y en dinero no se asume. Todo lo demás es negociable si se anota. Y se anota en un sitio con fecha y dueño, porque un TODO sin dueño es una decoración.
La señal de que la deuda te está ahogando es siempre la misma: cada cambio pequeño tarda cada vez más y rompe cosas cada vez más lejanas. Cuando notas eso, la prioridad ya no es la siguiente funcionalidad.
- Lo que este curso no ha cubierto y hacia dónde seguir
Honestidad ante todo: este curso te ha dado una base sólida y completa para construir servicios reales con Node. No te ha dado —ni podía— todo. Esto es lo que queda fuera, por qué importa y cuándo tocarlo:
| Tema | Qué es | Por qué importa | Cuándo abordarlo |
|---|---|---|---|
| TypeScript | JavaScript con tipos estáticos comprobados al compilar | El paso siguiente más recomendable. En un proyecto como los de este módulo, los tipos convierten en errores de compilación media docena de fallos que hoy solo verías en producción: un precioCentimos que llega como cadena, una transición de estado inexistente, un campo renombrado a medias. Además es el estándar de facto del ecosistema profesional |
Ya. Empieza con // @ts-check y JSDoc sobre tu código actual, y migra fichero a fichero |
| ESM | Los módulos import/export como destino del ecosistema |
Este curso usó CommonJS por pragmatismo (la mayoría del código existente lo usa), pero las librerías nuevas publican solo ESM y Node ya lo soporta sin fricción | Al empezar un proyecto nuevo, nace en ESM |
| Microservicios | Dividir el sistema en servicios desplegables por separado | Resuelve problemas organizativos (equipos independientes) a cambio de costes técnicos enormes: red poco fiable, transacciones distribuidas, trazabilidad, despliegue coordinado | Solo cuando el monolito modular duela por razones de equipo, no de moda. Un monolito bien estructurado sirve a la mayoría de productos durante años |
| Serverless | Funciones que se ejecutan bajo demanda sin servidor propio | Excelente para cargas esporádicas y picos; malo para conexiones persistentes (adiós Socket.IO), arranques en frío y pools de base de datos | Para trabajos por eventos, webhooks y tareas puntuales |
| Arquitectura hexagonal y DDD | Separar el dominio de la infraestructura; modelar el lenguaje del negocio | Ya lo has probado sin nombrarlo: repositorios, política pura, funciones de dominio sin Express. El siguiente paso es hacerlo sistemático | Cuando el dominio sea complejo de verdad y varias personas lo mantengan |
| WebAssembly | Ejecutar código compilado (Rust, C) dentro de Node | Para cálculo intensivo donde JavaScript no llega: imagen, criptografía, compresión | Cuando midas y el cuello de botella sea CPU en tu propio código |
| Deno y Bun | Ejecutores alternativos: seguridad por permisos, TypeScript nativo, arranque rápido | Empujan al ecosistema y conviene conocerlos; Node sigue siendo la apuesta segura en producción | Curiosidad activa, no migración urgente |
| Escalado de bases de datos | Réplicas de lectura, particionado, agrupación de conexiones, planes de consulta | El primer límite real de casi todos los sistemas no es Node: es la base de datos | En cuanto tengas volumen. Empieza por EXPLAIN y por el pool |
| Colas y mensajería distribuida | Kafka, RabbitMQ, NATS, patrones de entrega y orden | BullMQ te llevó lejos; los sistemas de eventos entre servicios juegan en otra liga | Cuando haya varios servicios que deban reaccionar a los mismos hechos |
| Kubernetes | Orquestación de contenedores a escala | Docker te dio el contenedor; K8s lo opera, lo escala y lo recupera. Potente y caro en complejidad | Cuando el PaaS se quede corto o lo exija la empresa |
| Accesibilidad y front-end | Interfaces usables por todo el mundo | Ninguno de estos cuatro proyectos existe sin interfaz, y una API impecable con una interfaz inutilizable no sirve a nadie | En paralelo, siempre |
Si tuvieras que elegir uno solo: TypeScript. El retorno por hora invertida es el más alto de la lista, y hace mejores todos los demás pasos.
- Cómo seguir aprendiendo
Cuatro hábitos que distinguen a quien sigue creciendo de quien se estanca:
Lee la documentación oficial, no la tercera respuesta de un buscador. La documentación de Node es excelente y contiene detalles que ningún tutorial menciona: el comportamiento exacto de pipeline ante errores, las garantías de cluster, las opciones de crypto.timingSafeEqual. Media hora en la documentación oficial rinde más que tres horas de fragmentos sueltos.
Lee el código fuente de lo que usas. Ya lo tienes en node_modules. Cuando algo se comporte de forma rara —Express 5 y sus errores asíncronos, cómo BullMQ implementa jobId, cómo Socket.IO decide el transporte— ábrelo y míralo. Descubrirás que las librerías que te intimidan son código normal escrito por gente normal. Es el salto de "usuario de la herramienta" a "ingeniero".
Sigue las notas de versión de Node. Las versiones LTS (Long Term Support, soporte a largo plazo) son las pares (20, 22, 24…) y reciben mantenimiento unos 30 meses: son las que se usan en producción. Las impares son de vida corta y sirven para probar novedades. Cada versión trae cosas que sustituyen dependencias: el fetch global, el ejecutor de pruebas integrado, el cargador de .env, node --watch. Menos dependencias es menos superficie de ataque y menos mantenimiento.
Contribuye y construye. Empieza por lo pequeño en proyectos libres: una errata en la documentación, una prueba que falta, un caso límite. Aprenderás tanto del proceso de revisión como del código. Y, sobre todo, construye algo tuyo de principio a fin, con usuarios reales aunque sean cinco. Ninguna lección enseña lo que enseña un despliegue que se rompió un domingo y tuviste que arreglar.
- Una última mirada a Escena Viva
Merece la pena mirar atrás el sistema completo.
Empezó siendo un array de tres eventos en un fichero JavaScript y un console.log. Hoy, sobre el papel de este curso, Escena Viva es:
- Una API REST versionada y documentada con OpenAPI, con validación en la frontera, errores homogéneos
{ error: { codigo, mensaje, estado, detalles } }y paginación por cursor; más una capa GraphQL con DataLoader para las vistas compuestas. - Un catálogo de 3 eventos y 7 sesiones sobre tres salas, con un aforo de 3000 plazas y 1811 entradas vendidas que nunca se sobrevenden, gracias a transacciones con bloqueo.
- Autenticación con contraseñas cifradas con bcrypt, JWT de acceso corto, refresco rotatorio en cookie
httpOnlycon revocación por familias, y una política de autorización pura sobre tres roles. - Tiempo real: soporte en directo con Socket.IO, salas por conversación, confirmaciones, presencia y adaptador de Redis para funcionar con varias réplicas.
- Comercio: tienda de merchandising con variantes y stock, precios congelados en el pedido, importes calculados en el servidor en céntimos, pasarela de pago con webhooks firmados e idempotentes y una máquina de estados que rechaza lo imposible.
- Contenido: magazine con flujo editorial, publicación programada, Markdown saneado con lista blanca, imágenes validadas por número mágico y procesadas en cola a varios tamaños.
- Colaboración interna: espacios por sala y evento, permisos por pertenencia empujados a la consulta, bloqueo optimista con 412, actividad inmutable y notificaciones agrupadas.
- Infraestructura: colas BullMQ con consumidor aparte, caché en Redis con invalidación, pool de worker threads, registros estructurados con pino e
idPeticion, métricas conprom-client, sondas de salud, Docker multietapa,docker composecon api, consumidor, postgres, mongo y redis, y una tubería de CI/CD que prueba, audita, construye y despliega sola. - Y una batería de pruebas que impide que nada de lo anterior se rompa en silencio.
Ninguna de esas piezas es exótica. Todas son las que sostienen productos reales que usas a diario. La diferencia entre este sistema y uno de verdad no es de naturaleza: es de escala, de dinero y de años de retoques. Pero las ideas son las mismas, y ahora las tienes.
- Una propuesta de continuación
Si quieres seguir solo, aquí van cuatro ampliaciones bien delimitadas. Cada una cabe en un fin de semana o dos y toca cosas nuevas de verdad:
-
Migrar un proyecto a TypeScript. Elige el más pequeño (la herramienta de tareas o la tienda). Empieza por
tsconfig.jsonconstrict: true, activaallowJs, y convierte primero el dominio (funciones puras), luego los repositorios, luego los controladores. Escribe tipos para el objeto de configuración y para el formato de error. Anota cuántos fallos reales te encuentra el compilador: te sorprenderá. -
Sustituir el envío de correo simulado por uno real y observable. Integra un proveedor de correo, añade reintentos, gestiona rebotes por webhook, y monta un panel con dos métricas: correos enviados y correos fallidos por tipo. Es pequeño y toca colas, webhooks, idempotencia y observabilidad a la vez.
-
Añadir un panel de administración con estadísticas en vivo. Métricas de ocupación por sala y evento con agregaciones (M7), servidas por SSE —no WebSocket: el flujo es de un solo sentido, y aplicar aquí la elección del 12-01 al revés es un ejercicio de criterio excelente— y cacheadas en Redis con invalidación por venta.
-
Cerrar la lista de comprobación del punto 2 sobre tu propio proyecto. Sin código nuevo, o casi. Audítalo punto por punto, escribe lo que falta, y arregla los tres más graves. Es la ampliación menos vistosa y la que más te va a enseñar.
Errores Comunes y Consejos
- Confundir "funciona" con "está listo". Que la funcionalidad responda es el principio del trabajo, no el final. La lista del punto 2 es la diferencia.
- Elegir tecnología por popularidad. La pregunta no es qué es moderno, sino qué problema tienes y qué cuesta deshacer la elección.
- Optimizar sin medir. Todo lo del M10 empieza con un número: p99, perfil, flamegraph. Sin medición, optimizar es superstición.
- Añadir complejidad "por si acaso". Microservicios, colas distribuidas o caché en cinco niveles antes de tener el problema es deuda técnica pagada por adelantado y sin haber recibido el préstamo.
- Copiar de una respuesta de internet sin entenderla —o de un asistente de IA—. Si no puedes explicar por qué funciona, no puedes arreglarlo cuando falle. Y fallará.
- No documentar las decisiones. El coste no lo pagas hoy, lo paga tu yo de dentro de dos años.
- Consejo final: el mejor código que escribirás es el que otra persona entiende sin preguntarte nada. Nombres claros, funciones cortas, dependencias inyectadas y errores explícitos ganan siempre a la genialidad compacta.
Ejercicios
Estos ejercicios son de síntesis: no hay una respuesta correcta única, y el valor está en el proceso.
-
Auditoría de tu propio proyecto. Coge el proyecto del módulo 12 que más te haya interesado y recórrelo entero con la lista de comprobación del punto 2. Marca cada punto como cumplido, parcial o ausente. Después ordena lo ausente por riesgo (probabilidad × impacto) y escribe un plan de tres acciones concretas.
-
Escribe un ADR de una decisión del curso. Elige una: MongoDB frente a PostgreSQL para el magazine, posiciones fraccionarias frente a LexoRank, WebSocket frente a SSE para el soporte, o confirmar el pago por webhook en vez de por retorno del navegador. Redáctalo con el formato del punto 3, incluyendo alternativas descartadas y consecuencias negativas asumidas.
-
Planifica una de las cuatro ampliaciones. Elige una del punto 8 y escribe su plan: alcance, lo que queda explícitamente fuera, riesgos, orden de trabajo y cómo sabrás que está terminada.
Soluciones
1. Auditoría — cómo se ve una hecha bien
No basta con una lista de casillas. Un resultado útil de esta auditoría es una tabla de riesgo priorizado:
| Punto ausente | Probabilidad de que ocurra | Impacto si ocurre | Riesgo | Acción |
|---|---|---|---|---|
| Sin prueba de autorización en el listado de tareas | Alta (cualquier refactor lo rompe) | Alto (filtración entre espacios) | Crítico | Añadir prueba de listado por cada endpoint, hoy |
| Copias de seguridad nunca restauradas | Media | Muy alto (pérdida total) | Crítico | Restauración de prueba mensual en entorno aparte |
| Sin tiempo límite en la llamada a la pasarela | Media | Medio (peticiones colgadas) | Alto | AbortSignal.timeout(8000) y reintento idempotente |
| Sin alertas por síntoma | Alta | Medio (te enteras por el cliente) | Alto | Alerta de pedidos en pendiente_pago > 1 h |
| Sin OpenAPI publicado | Alta | Bajo | Medio | Generar desde los esquemas zod |
Las tres acciones del plan: (a) pruebas de autorización en todos los listados; (b) restauración de copia probada con calendario; (c) tiempos límite y alerta de síntoma en el flujo de pago. Fíjate en que ninguna es una funcionalidad nueva y las tres reducen riesgo real.
2. ADR — ejemplo resuelto
# ADR 012: Confirmar el pago por webhook y no por el retorno del navegador
Fecha: 2026-08-15
Estado: aceptada
## Contexto
Tras pagar, el navegador vuelve a nuestra página de éxito. La tentación es
marcar el pedido como pagado en ese momento, porque es simple y el usuario
ve el resultado al instante.
## Decisión
El pedido pasa a 'pagado' únicamente al recibir el webhook firmado
payment_intent.succeeded. La página de éxito solo consulta GET /pedidos/:id.
## Consecuencias
- Positivas: el estado es correcto aunque el usuario cierre el navegador;
la fuente de verdad es quien realmente cobró; resistente a manipulación.
- Negativas: el usuario puede ver 'pendiente_pago' unos segundos; hay que
implementar verificación de firma, idempotencia y sondeo en el cliente.
- Mitigación: sondeo corto o aviso por Socket.IO al confirmarse el pedido.
## Alternativas descartadas
- Confirmar en el retorno del navegador: un cierre de pestaña deja el pedido
cobrado y sin confirmar. Y el retorno es manipulable por el cliente.
- Consultar a la pasarela cada N segundos desde el servidor: funciona, pero
desperdicia llamadas y añade latencia frente a un webhook que ya existe.3. Plan de ampliación — ejemplo con la migración a TypeScript
- Alcance:
src/dominio,src/serviciosysrc/repositoriosde la herramienta de tareas, constrict: true. - Fuera de alcance: controladores y pruebas (segunda fase); no se cambia ninguna funcionalidad.
- Riesgos: tipos de Sequelize costosos de escribir; tentación de usar
anyy perder todo el beneficio. - Orden: (1)
tsconfig.jsonconallowJsycheckJs; (2) tipos del dominio (Tarea,Espacio,Pertenencia, estados como uniones literales); (3) funciones puras; (4) repositorios con tipos de retorno explícitos; (5) construcción en CI. - Terminado cuando:
tsc --noEmitpasa sin errores, no queda ningúnanyexplícito en el dominio, todas las pruebas siguen verdes y CI compila antes de probar.
Nota la elección de orden: se empieza por el dominio porque es donde los tipos aportan más y donde menos infraestructura hay que tipar. Empezar por los controladores es el error habitual y el que hace abandonar la migración.
Conclusión
Aquí termina el curso.
Empezaste instalando Node y ejecutando un fichero con tres eventos dentro de un array. Terminas con una plataforma completa —catálogo, aforo sin sobreventa, autenticación con refresco rotatorio, tiempo real, comercio con pagos, contenido, colaboración interna, colas, caché, pruebas, contenedores y despliegue automático— y, más importante, con los criterios para decidir qué construir, qué no construir y qué comprobar antes de que alguien dependa de ello.
Si hay una idea que conviene que se quede por encima de todas las demás, es esta: las herramientas cambian y los criterios permanecen. Express dará paso a otro framework, Mongoose y Sequelize a otros ORM, y alguna de las librerías de este curso habrá quedado obsoleta antes de lo que parece. Lo que no caduca es saber por qué el precio se congela en el pedido, por qué el estado local deja de valer en cuanto hay dos procesos, por qué la autorización se empuja a la consulta, por qué un reintento exige idempotencia y por qué una copia de seguridad que no se ha restaurado no es una copia de seguridad. Eso lo has aprendido construyendo, que es la única forma en que se aprende de verdad.
Ya no necesitas este curso. Necesitas un problema que te importe y tiempo para equivocarte con él. Escribe código, ponlo delante de usuarios, obsérvalo fallar, arréglalo y vuelve a empezar. Ese bucle —el mismo que has recorrido doce veces con Escena Viva— es todo el oficio.
Buen viaje.
Curso de Node.js: De Principiante a Avanzado
Módulo 1: Introducción a Node.js
- ¿Qué es Node.js?
- Instalación y Configuración del Entorno
- Tu Primer Programa en Node.js
- El REPL de Node.js
- JavaScript Moderno para Node.js
- El Proyecto del Curso: la Plataforma Escena Viva
Módulo 2: Conceptos Básicos
- Arquitectura de Node.js
- El Bucle de Eventos (Event Loop)
- Callbacks y Programación Asíncrona
- Promesas y async/await
- Eventos y EventEmitter
- Módulos CommonJS y require()
- Módulos ES e Interoperabilidad
Módulo 3: Sistema de Archivos y E/S
- Lectura y Escritura de Archivos
- El Módulo fs a Fondo
- Rutas Multiplataforma con el Módulo path
- Trabajando con Streams
- Streams de Transformación y pipeline
- Buffers y Datos Binarios
Módulo 4: HTTP y Servidores Web
- Creando un Servidor HTTP Simple
- Manejo de Solicitudes y Respuestas
- Enrutamiento Manual
- Sirviendo Archivos Estáticos
- Recibiendo Datos: Cuerpos de Petición y JSON
- Consumiendo APIs Externas desde Node.js
Módulo 5: NPM y Gestión de Paquetes
- Introducción a NPM y package.json
- Instalación y Uso de Paquetes
- Versionado Semántico y package-lock
- Scripts de npm y Automatización del Proyecto
- Creación y Publicación de Paquetes
- Seguridad y Mantenimiento de Dependencias
Módulo 6: Framework Express.js
- Introducción a Express.js
- Configuración de una Aplicación Express
- Enrutamiento en Express
- Middleware
- Middleware de Terceros Esenciales
- Validación de Datos de Entrada
- Manejo de Errores
Módulo 7: Bases de Datos y ORMs
- Introducción a las Bases de Datos
- Usando MongoDB con Mongoose
- Operaciones CRUD
- Relaciones, Poblado y Consultas Avanzadas
- Usando Bases de Datos SQL con Sequelize
- Migraciones, Transacciones y Datos de Prueba
Módulo 8: Autenticación y Autorización
- Introducción a la Autenticación
- Registro de Usuarios y Hash de Contraseñas
- Sesiones y Cookies con Passport.js
- Autenticación con JWT
- Control de Acceso Basado en Roles
- Buenas Prácticas de Seguridad en APIs
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias con Mocha y Chai
- Dobles de Prueba con Sinon
- Pruebas de Integración
- Cobertura y Automatización de las Pruebas
- Depuración de Aplicaciones Node.js
Módulo 10: Temas Avanzados
- El Módulo Cluster
- Hilos de Trabajo (Worker Threads)
- Caché y Colas de Trabajo con Redis
- Optimización del Rendimiento
- Construcción de APIs RESTful
- GraphQL con Node.js
Módulo 11: Despliegue y DevOps
- Configuración y Variables de Entorno
- Registro y Monitorización en Producción
- Usando PM2 para la Gestión de Procesos
- Empaquetado con Docker
- Desplegando en Heroku y Otras PaaS
- Integración y Despliegue Continuos
