🧱 Full Stack ☁️ Cloud, DevOps & Infraestructura

Monorepos full-stack en 2026: Turborepo, Nx y cuándo merece la pena

Un monorepo no es una arquitectura ni una moda: es una forma de organizar código que solo compensa a partir de ciertas señales. Comparamos Turborepo y Nx sin dogmatismo.

📅 18 de septiembre de 2026 ⏱️ 10 min de lectura ✍️ Equipo ProgramacionWebs

Cada vez que un proyecto full-stack en TypeScript empieza a compartir tipos entre frontend y backend, o a extraer una librería de UI común, alguien en el equipo pregunta lo mismo: “¿deberíamos pasarnos a un monorepo?”. La respuesta seria no es “sí, todo el mundo lo hace” ni “no, es innecesariamente complejo”: es entender qué problema concreto resuelve un monorepo, comprobar si tu proyecto lo tiene, y solo entonces elegir la herramienta.

Qué es (y qué no es) un monorepo

Un monorepo es, literalmente, varios paquetes o aplicaciones independientes viviendo dentro de un único repositorio Git, cada uno con su propio package.json, pero compartiendo un mismo historial de commits y —lo que de verdad importa— la posibilidad de referenciarse entre sí sin publicar nada a un registro npm intermedio.

Conviene separar esto de un concepto con el que se confunde con facilidad: un monorepo es una forma de organizar el código fuente, no una decisión de arquitectura de despliegue. Puedes tener un monorepo que despliega un monolito único, o un monorepo que despliega quince servicios independientes que ni siquiera comparten ciclo de vida — de hecho, cuando un sistema separa varios servicios a partir de un monolito modular, el monorepo suele ser precisamente lo que evita que esos servicios divergan en sus tipos y utilidades compartidas.

El problema real que resuelve, en concreto:

  • Tipos compartidos sin publicar versiones. Un paquete @repo/types que define el modelo de datos, importado directamente por la API y por el frontend, sin pasar por npm publish cada vez que cambia un campo.
  • Cambios atómicos entre frontend y backend. Renombrar un campo en la API y actualizar el cliente que lo consume en el mismo commit, en el mismo pull request, revisado por la misma persona.
  • Herramientas y configuración compartidas (ESLint, TypeScript config, componentes de UI) sin la fricción de mantener varios repositorios sincronizados a mano.

Lo que un monorepo no resuelve automáticamente: no mejora por sí solo el rendimiento de tu aplicación, no sustituye una buena definición de límites entre módulos (esa disciplina hay que tenerla igual, con o sin monorepo), y no es gratis a nivel operativo — un repositorio con cientos de paquetes sin herramientas de caché y ejecución selectiva de tareas se vuelve lento de compilar y de testear precisamente por su tamaño.

Herramientas de workspace vs herramientas de build: no son lo mismo

npm, pnpm y yarn ya resuelven, con sus respectivos “workspaces”, la parte de instalar dependencias y enlazar paquetes locales entre sí. Eso es necesario pero no suficiente: en cuanto el monorepo crece más allá de un puñado de paquetes, ejecutar test o build en todos ellos de forma ingenua —sin caché, sin saber qué cambió realmente, sin paralelizar según el grafo de dependencias— convierte cada CI en una espera de minutos que crece de forma lineal con el número de paquetes. Turborepo y Nx existen para resolver exactamente esa segunda capa: qué tareas ejecutar, en qué orden, cuáles se pueden saltar porque su resultado no cambió, y cuáles se pueden paralelizar sin pisarse.

Turborepo: la opción ligera por defecto

Turborepo se apoya deliberadamente en lo que ya tienes: los scripts que ya declaraste en cada package.json y las dependencias que ya declaraste entre paquetes. Encima de eso añade un único archivo de configuración, turbo.json, que define cómo se relacionan las tareas entre sí y qué se puede cachear.

// turbo.json
{
  "$schema": "https://turborepo.dev/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": []
    },
    "lint": {
      "outputs": []
    }
  }
}

"dependsOn": ["^build"] le dice a Turborepo que antes de compilar un paquete hay que compilar primero sus dependencias internas dentro del propio monorepo — el ^ señala “las dependencias de este paquete”, no el propio paquete. Con eso resuelto, Turborepo construye el grafo de tareas automáticamente a partir de los package.json, sin que tengas que declararlo tú a mano en ningún otro sitio.

La pieza que más tiempo ahorra en la práctica es la caché: si el contenido de un paquete y sus dependencias no ha cambiado desde la última ejecución, Turborepo no vuelve a ejecutar la tarea — devuelve el resultado guardado al instante, tanto en local como (con caché remota) entre distintas máquinas de CI. Cifras de la propia industria sitúan la reducción de tiempo de CI gracias a este tipo de caché en un rango del 60-80% sobre una ejecución sin caché, aunque el ahorro real depende mucho de cuánto se solapan los cambios entre ejecuciones consecutivas.

La caché remota de Turborepo está integrada con la infraestructura de Vercel (con un nivel gratuito limitado y planes de pago para equipos), lo que simplifica la puesta en marcha si ya despliegas ahí, pero también es el punto que conviene evaluar si tu equipo no quiere depender de ese proveedor concreto para su tubería de CI.

Nx: cuando necesitas más que caché

Nx parte de la misma idea de fondo —grafo de tareas y caché— pero añade una capa adicional de análisis y herramientas que Turborepo no intenta cubrir:

  • Generadores de código. Comandos que crean un nuevo componente, librería o aplicación siguiendo una plantilla y una convención ya decididas por el equipo, en lugar de copiar y adaptar un paquete existente a mano.
  • Reglas de límites de módulo. Nx puede impedir en tiempo de lint que un paquete etiquetado como “feature” importe directamente de otro paquete etiquetado como “app”, forzando que la comunicación pase por las interfaces públicas que el equipo decidió — el mismo principio de fronteras explícitas que exige un buen monolito modular, aplicado aquí a nivel de configuración de la herramienta en lugar de a pura disciplina de equipo.
  • Plugins para stacks políglota. Además de JavaScript/TypeScript, Nx tiene plugins con soporte oficial para Angular, .NET y Java/Maven, entre otros, lo que permite que un mismo workspace orqueste proyectos en varios lenguajes con el mismo sistema de caché y grafo de dependencias.
  • Nx Cloud, con un nivel gratuito más generoso que la caché remota de Turborepo, además de ejecución distribuida de tareas entre varias máquinas y detección automática de tests inestables (flaky).
// project.json (proyecto individual dentro de un workspace Nx)
{
  "name": "api",
  "targets": {
    "build": {
      "executor": "@nx/esbuild:esbuild",
      "dependsOn": ["^build"],
      "outputs": ["{options.outputPath}"]
    }
  },
  "tags": ["scope:backend", "type:app"]
}

Ese campo tags es lo que después consumen las reglas de límites de módulo: puedes declarar, por ejemplo, que ningún proyecto con scope:frontend puede depender de uno con scope:backend, y Nx lo hace cumplir en el propio lint en lugar de confiar en que nadie rompa la regla en una revisión de código apresurada.

Comparativa honesta

TurborepoNx
Curva de aprendizajeBaja: un archivo de config, reutiliza tus scriptsMedia-alta: conceptos propios (executors, generators, tags)
Caché local y remotaSí; remota ligada a la infraestructura de VercelSí; Nx Cloud con nivel gratuito más generoso
Generadores de códigoNoSí, con plantillas propias por plugin
Reglas de límites entre módulosNo de forma nativaSí, vía tags y reglas de lint
Soporte políglota (más allá de JS/TS)LimitadoPlugins oficiales para varios ecosistemas
Ejecución distribuida de tareas en CIBásicaNx Agents, más orientada a distribuir tareas entre máquinas
FilosofíaHacer una cosa (task running + caché) muy bienPlataforma completa de gestión de monorepo

Ninguna fila de esta tabla es “Nx gana” o “Turborepo gana” en abstracto: cada capacidad extra de Nx es también una pieza más de configuración que alguien tiene que entender y mantener. La pregunta correcta no es cuál herramienta es objetivamente mejor, sino cuál de esas capacidades adicionales tu equipo va a usar de verdad.

Señales de que tu proyecto está listo para un monorepo

  • Ya tienes (o vas a tener de inmediato) al menos un paquete de tipos, utilidades o componentes de UI que dos aplicaciones distintas necesitan importar sin publicarlo a npm.
  • Frontend y backend cambian juntos con frecuencia — un cambio de API casi siempre implica tocar también el cliente que lo consume, y hoy eso significa dos pull requests coordinados manualmente en dos repositorios.
  • El equipo es lo bastante pequeño (o está lo bastante coordinado) como para que un cambio atómico entre paquetes tenga sentido revisarlo en un único pull request, en lugar de exigir releases independientes por paquete.
  • Ya sientes el dolor de mantener sincronizada la configuración (ESLint, TypeScript, CI) entre varios repositorios casi idénticos.

Señales de que NO lo está (todavía, o nunca)

  • El proyecto es una única aplicación sin ningún paquete interno que compartir con nadie más: un monorepo de un solo paquete no añade nada, solo la configuración de la propia herramienta.
  • Los equipos que tocarían cada parte del repositorio necesitan ciclos de release completamente independientes, con su propio calendario y sus propias ventanas de despliegue — forzarlos al mismo repositorio genera más fricción de coordinación de la que elimina.
  • El stack es genuinamente políglota y dispar (un servicio de datos en Python, otro en Go, un frontend en TypeScript) sin apenas código compartido real entre ellos: el beneficio de compartir tipos o utilidades cae mucho cuando no hay casi nada que compartir.
  • El repositorio ya arrastra un historial de Git enorme y con mucho ruido: fusionar varios repositorios grandes y antiguos en uno solo es un proyecto de migración en sí mismo, no una tarde de trabajo.

Cómo empezar sin sobre-diseñar

  1. Configura primero los workspaces de tu gestor de paquetes (pnpm-workspace.yaml, o el campo workspaces de package.json) — es la base sobre la que corre cualquiera de las dos herramientas.
  2. Añade Turborepo con la configuración mínima (build, test, lint con sus dependencias declaradas) antes de plantearte generadores, tags o ejecución distribuida: la mayoría del beneficio inicial viene solo de la caché.
  3. Extrae a un paquete compartido (@repo/types, @repo/ui) únicamente lo que ya estás duplicando de verdad entre dos o más proyectos — no anticipes paquetes “por si acaso” que nadie importa todavía.
  4. Si con el tiempo el equipo empieza a necesitar generadores de código consistentes, límites de módulo forzados por herramienta o soporte políglota real, es la señal concreta para evaluar migrar a Nx — no antes, y no solo porque tenga más funciones sobre el papel.

Recomendación práctica

Para la mayoría de proyectos full-stack que arrancan en 2026, Turborepo es el punto de partida razonable: resuelve el problema real (tareas lentas sin caché, coordinación manual entre paquetes) con la mínima superficie de configuración posible. Nx merece la inversión cuando el equipo ya es lo bastante grande, lo bastante políglota o lo bastante dependiente de convenciones forzadas por herramienta como para que sus capacidades adicionales —generadores, límites de módulo, Nx Cloud— se usen de verdad y no queden como configuración sin explotar. Y si tu proyecto es una única aplicación sin nada que compartir, la respuesta correcta sigue siendo no montar un monorepo en absoluto: la herramienta no soluciona un problema que todavía no tienes.

Compartir