Eliminé Mi Último Flujo de n8n. Convex Ejecutó el Trabajo de 30 Minutos en 80 Líneas de TypeScript.
Esta semana eliminé mi último workflow de n8n. Había estado funcionando durante 8 meses sin quejarse. Lo eliminé porque una noche, viendo mi stack en funcionamiento, noté algo estúpido: Convex, Infisical, NetBird, Traefik, todos corriendo por la misma malla VPN, todos versionados en el mismo monorepo, todos revisables por código.
En pocas palabras: Los editores visuales tenían una característica realmente asesina: "no necesitas código". Claude Code acaba de matar esa característica. La pregunta ahora es si tu trabajo realmente necesita salir de la capa visual, o si vive ahí perfectamente bien.
Todo excepto un servicio. n8n. Su fuente de verdad era un SQLite local más 3 Google Sheets que ya no me atrevía a cerrar.
No tengo nada contra n8n. Lo amé durante 2 años y sangré con sus expresiones JS esotéricas (cualquiera que haya intentado {{$node["Webhook"].json["body"]["data"][0]["nested"]}} lo sabe). Pero esto es pura racionalidad. Tiempo invertido, servicio prestado, entregables vendidos.

No Podía Hacer git diff de Mi Propio Workflow
Acababa de darme cuenta de que era el único servicio en mi stack al que no podía hacerle git diff. No testeable. No fácilmente reproducible. Lo único que se me escapaba.
Y lo peor es que este workflow específico pesaba 2,633 líneas de JSON cuando lo exporté. 2,633 líneas que nadie, yo incluido, podía revisar seriamente. Lo intenté una vez, después de que un compañero junior me preguntara qué hacía un nodo específico y me di cuenta de que lo había olvidado. Abrí la exportación en VS Code, scrolleé durante 4 minutos, me rendí, y volví al editor visual para encontrar el nodo haciendo clic por ahí. Ese fue el momento en que lo supe. Una pieza de infraestructura que solo puedes auditar haciendo clic en ella no es infraestructura. Es un Tamagotchi.
n8n no está mal diseñado. Está diseñado para otro trabajo. El desajuste es mío, no de n8n, y lo había cargado durante 8 meses sin nombrarlo.
El JSON de 2,633 Líneas Que Ni Siquiera Podía Revisar

Más de 30 nodos. Google Sheets usado como base de datos, con columnas vacías actuando como máquina de estados, porque eso es lo que haces cuando tu herramienta no te da una máquina de estados real. Un bucle de polling contra una API externa de LLM que toma desde 90 segundos hasta 30 minutos por llamada, y que pierde su lugar en el momento en que n8n se reinicia. Receptores de webhook que se disparan dos veces cuando n8n está bajo carga. Una configuración de modo cola que me tomó un fin de semana ajustar.
No estoy atacando solo a n8n aquí. Make es el mismo problema con peor precio. Pago por operación, sin control de versiones, fricción en el momento en que necesitas algo serio. Zapier es lo mismo otra vez, facturado por tarea, cada IF/ELSE te cuesta. n8n al menos te deja auto-hospedarlo. Esa es la única victoria arquitectónica que tiene sobre los otros dos, e incluso esa victoria viene con una configuración de modo cola que toma un fin de semana ajustar.
Cuando digo "no puedo revisar 2,633 líneas de JSON", estoy siendo caritativo. El propio staff de n8n lo admite. En su propio foro oficial, en un hilo titulado N8n performance and scalability, escriben: "Escalar n8n actualmente no es tan fácil" y "n8n aún no escala apropiadamente". Y un operador en un foro comunitario reportó que su n8n auto-hospedado se cae confiablemente bajo picos de carga de 2,000 requests en pocos minutos. Confiablemente. Como en, puedes planear para ello.
Ese no es un hater en Reddit. Ese es n8n sobre n8n.
El Patrón de 80 Líneas en TypeScript Que Reemplazó Todo Eso
Aquí está el patrón que reemplazó los más de 30 nodos. Polling auto-reprogramable con el scheduler de Convex. Estado persistido en cada paso. Si el worker se cae entre dos polls, el scheduler lo retoma exactamente donde lo dejó. Sin modo cola. Sin SQLite que respaldar.
import { internalAction, internalMutation } from "./_generated/server"
import { internal } from "./_generated/api"
import { v } from "convex/values"
// Kick off a long-running external job
export const startJob = internalAction({
args: { jobId: v.string(), payload: v.any() },
handler: async (ctx, { jobId, payload }) => {
const remoteId = await callExternalLLM(payload)
await ctx.runMutation(internal.jobs.recordStart, { jobId, remoteId })
// Schedule the first poll in 30 seconds
await ctx.scheduler.runAfter(
30_000,
internal.jobs.pollStatus,
{ jobId, remoteId, attempt: 1 }
)
},
})
export const pollStatus = internalAction({
args: {
jobId: v.string(),
remoteId: v.string(),
attempt: v.number(),
},
handler: async (ctx, { jobId, remoteId, attempt }) => {
// 30 min timeout (60 polls of 30s)
if (attempt > 60) {
await ctx.runMutation(internal.jobs.markTimeout, { jobId })
await ctx.runAction(internal.jobs.notifyIncident, { jobId })
return
}
const status = await fetchRemoteStatus(remoteId)
if (status.state === "done") {
await ctx.runMutation(internal.jobs.recordResult, {
jobId,
result: status.result,
})
return
}
if (status.state === "failed") {
await ctx.runMutation(internal.jobs.recordFailure, {
jobId,
error: status.error,
})
return
}
// Still running, reschedule
await ctx.scheduler.runAfter(
30_000,
internal.jobs.pollStatus,
{ jobId, remoteId, attempt: attempt + 1 }
)
},
})
Eso es todo. Aproximadamente 80 líneas una vez que agregas las mutaciones que escriben a la tabla. Hace lo que hacían 30 nodos de n8n, más algunas cosas que n8n nunca podría hacer limpiamente. Versionado con la app, testeado con la app, desplegado en el mismo pase de CI, estado viviendo en la misma tabla de Convex que el frontend ya lee, así que la UI ve la actualización del estado del job en tiempo real sin que yo haga nada extra.
La razón por la que esto rivaliza con el patrón step.sleep() de Inngest es que cada llamada a ctx.scheduler.runAfter() persiste durablemente la siguiente invocación en el propio store de Convex. Si el deployment se cae a mitad del job, cuando vuelve a levantarse el scheduler reanuda desde el último paso persistido. No estoy fingiendo durabilidad con un bucle cron y rezando.
Si no estás en Convex, este patrón exacto vive nativamente en Convex + Claude Code: The Ultimate Duo for Shipping SaaS at 3 AM. Y si estás en Postgres, tienes Inngest, Trigger.dev, Temporal. Todos resuelven el mismo problema real: un job de larga duración, con estado, que debe sobrevivir crashes. Ese es el problema correcto a resolver. No es el de n8n.
La mayoría de tutoriales de automatización se pierden aquí. Te muestran cómo encadenar 12 nodos que llaman a un LLM y escriben a una Sheet, pero nunca te muestran qué pasa cuando la llamada al LLM toma 14 minutos y tu contenedor de n8n se queda sin memoria en el minuto 9. La respuesta es, rehaces el job desde cero y lloras. El patrón de arriba rehace los últimos 30 segundos y sigue adelante.
El Mapa de Arquitectura de 4 Preguntas: Cómo Elijo Realmente

Necesitaba una regla de decisión que sobreviva a los próximos 4 lanzamientos de frameworks. Aquí están las 4 preguntas que hago ahora.
Pregunta 1. ¿Job de menos de 30 segundos, trabajo de pegamento entre herramientas SaaS, sin lógica de negocio?
Usa las cosas visuales. Zapier, Make, n8n, todas defendibles. El costo del editor visual es real pero la ventaja (incorporas a un colega no-dev en una tarde) también es real. Elegir código aquí es sobre-ingeniería.
Pregunta 2. ¿Job entre 5 y 30 minutos, estado durable, reintentos, reanudar después de crash?
Código o mueres. Scheduler de Convex si ya estás en Convex. Inngest, Trigger.dev o Temporal de lo contrario. Nunca editor visual. Un testimonio de cliente de Trigger.dev publicado en su landing page lo dice sin rodeos sobre Zapier y n8n: "se vuelven complejos, realmente lentos, caros y que consumen mucho tiempo de manejar para automatizaciones grandes". Eso coincide exactamente con mi propia experiencia.
Pregunta 3. ¿CRUD más tiempo real más frontend reactivo?
Convex directo. No agregues Inngest encima. Duplicar la capa de orquestación es duplicar el estado, duplicar los modos de falla, duplicar el baile de autenticación. El scheduler de Convex es suficiente para el 90% de jobs en segundo plano que necesita una app SaaS. Creo que esta es la línea donde estoy menos seguro, honestamente. Si tus necesidades de tiempo real crecen hacia multi-región con garantías estrictas de orden, Convex podría no ser la respuesta en 18 meses y querrás una cola apropiada. Por ahora se mantiene.
Pregunta 4. ¿Pipeline de datos pesados, SQL complejo, DAGs?
Supabase más pg_cron, o Airflow. Convex no hace SQL analítico ad-hoc. No trates de hacerlo. Aquí es donde Convex deja de ser la herramienta correcta y la infraestructura con forma de Postgres empieza a ganar.
Ese es todo el árbol de decisión. Los 2 ejes (duración y acoplamiento) están debajo, pero no necesitas pensar en ellos cada vez. Solo preguntas cuánto vive el job y qué tan estrechamente toca el resto de tu código, y la herramienta sale de la respuesta.
La razón por la que confío más en este framework que en comparaciones herramienta por herramienta es que ha sobrevivido a 3 migraciones de stack en 2 años. Me moví de Supabase a Convex, de Vercel cron al scheduler de Convex, de n8n a TypeScript, y las 4 preguntas no cambiaron ni una vez a través de ninguno de esos movimientos. Solo cambiaron los nombres de las herramientas. La prueba real de cualquier regla de decisión arquitectónica, en mi experiencia, es si todavía tiene sentido 18 meses después, cuando la mitad de los productos en la presentación tienen una nueva página de precios, un nuevo logo, o un nuevo comprador. Si todavía tiene sentido, mantenlo. Si no, era promoción de herramientas disfrazada de framework, y puedes tirarlo con la presentación.
Para Qué Todavía Usaría n8n
No estoy en una cruzada. Hay trabajos donde n8n todavía gana, y Convex obviamente sería la respuesta equivocada.
Sincronizar una Google Sheet a un canal de Slack cuando una celda cambia. Notificación por email cuando aparece una nueva fila en PostgreSQL. Webhook de Stripe reenviado a Discord. Cualquier job estable, repetitivo, sin lógica de negocio de menos de 30 segundos. Para todos estos, n8n le gana a escribir TypeScript. No porque n8n sea genial. Porque el job es lo suficientemente pequeño que el overhead del editor visual es menor que el overhead del código. El punto de equilibrio es real y n8n se sienta cómodamente del lado correcto para esta clase de trabajo.
Escribí una pieza completa sobre por qué un repo open-source convirtió a Claude Code en un arquitecto de n8n hace unas semanas. Ese artículo y este no son contradictorios. Claude Code hace a n8n mucho mejor en las cosas en las que n8n ya es bueno. Este artículo es sobre el momento en que un job deja de ser una de esas cosas. El momento en que tu workflow necesita estado durable, reintentos, replay, revisión de código, el momento en que un compañero junior mira la exportación JSON y pregunta "¿esto es normal?" Ahí es cuando te vas.
(Mi tipo de la piscina vino ayer por la mañana mientras pensaba en todo esto, me preguntó qué estaba construyendo, y le dije "una cosa que vigila otra cosa durante 30 minutos". Asintió lentamente y dijo "ok" de la manera que dices "ok" a alguien que claramente no ha dormido lo suficiente. No está equivocado. Los niños volvieron de la escuela una hora después, el perro se robó una tortilla del mostrador, y me olvidé de Convex por el resto de la tarde.) 🐕
Elige Orquestación Como Infraestructura, No Como Herramienta
Elegir una capa de orquestación no es una elección de herramienta. Es una elección de arquitectura. Las herramientas se mueven cada 18 meses. Convex tendrá un competidor. Inngest tendrá un nuevo nivel de precios. n8n lanzará una v3. El framework subyacente (cuánto vive el job y qué tan estrechamente toca el resto de tu código) no se mueve. Era cierto antes de n8n. Será cierto después.
Y si estás leyendo esto pensando que tu workflow actual de n8n debería moverse a código, tal vez. Tal vez no. Ejecuta las 4 preguntas. Si la respuesta es "trabajo de pegamento entre SaaS, menos de 30 segundos, sin lógica de negocio", quédate en n8n. Está literalmente construido para eso.
Elimina un workflow esta semana. El más pequeño que tengas. Ve cómo se siente.
Fuentes
- "Scaling n8n is currently not that easy", respuesta del staff de n8n en el foro oficial, hilo N8n performance and scalability: https://community.n8n.io/t/n8n-performance-and-scalability/3150/2
- Reporte de operador sobre n8n auto-hospedado cayéndose bajo picos de carga de 2,000 req, hilo trying to scale N8N: https://forum.cloudron.io/topic/7174/trying-to-scale-n8n/2
- Testimonio de cliente de Trigger.dev sobre complejidad de Zapier y n8n: https://trigger.dev/
- Resumen de orquestación TypeScript (Temporal vs Trigger.dev vs Inngest), Matthieu Mordrel: https://medium.com/@matthieumordrel/the-ultimate-guide-to-typescript-orchestration-temporal-vs-trigger-dev-vs-inngest-and-beyond-29e1147c8f2d
Este post puede contener enlaces de afiliados. Si haces clic en ellos, podría ganar una pequeña comisión, no te cuesta nada, y me ayuda a seguir enviando artículos de calidad todos los días para tu placer de lectura.