Código duplicado, monorepos y las tres herramientas: npm workspaces, Turborepo y Nx
Cuándo usar cada una. El sobredimensionamiento es caro — y empezar por lo más simple casi siempre es la respuesta correcta.
Hay un momento en la vida de cualquier proyecto en el que te das cuenta de que tienes el mismo código en dos sitios. Un componente que renderiza posts en tu landing, y el mismo componente — con ligeras variaciones — en tu blog. Una función getPosts() que consulta Supabase, duplicada entre dos apps. Cuando cambias algo, tienes que acordarte de cambiarlo en los dos. Y cuando se te olvida, la desincronización silenciosa.
Me pasó esta semana construyendo Agentikas. Tenía dos sitios generados por dos repos de Next.js:
agentikas.ai/blog— landing con blog integrado*.blog.agentikas.ai— plataforma multi-tenant para otros blogs
Mismos posts (misma base de datos Supabase), dos frontends, dos implementaciones. Cada vez que añadía una feature — un botón de like, una sección de "author picks", un filtro de tags — tenía que implementarla dos veces. La última vez lo descubrí porque el botón de like estaba en un sitio y no en el otro. El lector pensaba que le odiaba.
Es ahí cuando toca decidir: monorepo.
Qué problema resuelve un monorepo
Un monorepo es simplemente un repositorio que contiene varios proyectos relacionados. En lugar de esto:
agentikas-ai/ (repo 1)
src/components/PostCard.tsx
src/data/blog.ts
agentikas-blog/ (repo 2)
src/components/PostCard.tsx ← copia
src/data/blog.ts ← copia
Tienes esto:
agentikas/ (un solo repo)
apps/
landing/
blog/
dashboard/
packages/
blog-ui/ ← un solo PostCard
blog-data/ ← un solo getPosts()
Cada app importa del paquete compartido. Cambias algo una vez. Se propaga a todas las apps. Fin de la duplicación.
Pero no todos los monorepos son iguales. Hay tres herramientas dominantes, cada una con su filosofía. Vamos a verlas por orden de complejidad.
npm workspaces: lo mínimo viable
npm workspaces es una feature nativa de npm desde la versión 7. No instalas nada. Solo añades esto a tu package.json raíz:
{
"workspaces": ["apps/*", "packages/*"]
}
Y npm entiende que tus paquetes son locales. Cuando haces npm install en la raíz:
- Instala todas las dependencias de todos los paquetes
- Crea un solo
node_modulesen la raíz (hoisting) - Enlaza los paquetes locales entre sí mediante symlinks
Si apps/blog tiene "@agentikas/blog-ui": "*" en sus dependencias, npm crea un symlink de apps/blog/node_modules/@agentikas/blog-ui → packages/blog-ui. No hay publicación, no hay versiones. Tu paquete vive en disco y se importa como si fuera un módulo de npm cualquiera.
Qué ganas: - Cero herramientas nuevas - Cero configuración - Disponible en cualquier proyecto con Node moderno
Qué no te da:
- No hay cache de builds
- No hay ejecución paralela automática de tareas
- Si un paquete compartido tiene un error, next build del paquete consumidor lo detecta, pero no hay visualización del grafo de dependencias
Para proyectos pequeños o medianos — 3 a 5 apps, un puñado de paquetes compartidos — npm workspaces es suficiente. De hecho es más que suficiente. Es lo que estoy usando en Agentikas.
Turborepo: cuando el build empieza a doler
Turborepo es una herramienta de Vercel que se pone encima de npm workspaces (o pnpm, o yarn). Su propuesta de valor es una palabra: cache.
Turborepo analiza el grafo de dependencias de tu monorepo. Sabe que apps/blog depende de packages/blog-ui. Cuando ejecutas turbo build, hace lo siguiente:
- Ordena las tareas topológicamente (primero los paquetes base, luego los que dependen de ellos)
- Ejecuta en paralelo lo que puede paralelizarse
- Cachea el output de cada tarea basándose en un hash del código fuente y las dependencias
La segunda vez que haces turbo build, si no cambiaste nada en packages/blog-ui, Turborepo no recompila. Te devuelve el output cacheado en milisegundos. En CI, con cache remoto (Vercel o tu propio servidor), un push que solo cambia una app puede evitar rebuilds completos de las demás.
Qué ganas:
- Builds incrementales (solo recompila lo que cambió)
- Paralelización sin configurar
- Cache remoto compartido entre developers y CI
- Un comando para todo: turbo dev, turbo test, turbo build
Qué cuesta:
- Un archivo turbo.json con pipelines de tareas
- Aprender la sintaxis de tasks y dependsOn
- Si estás usando Vercel, cache remoto gratis. Si no, montar tu propio backend o pagar
Turborepo brilla cuando: - Tienes 5+ apps - Tus builds empiezan a tardar más de un minuto - Tu CI se vuelve un cuello de botella
Antes de eso, es una herramienta que añade ceremonia sin resolver un dolor real.
Nx: la opción para equipos grandes
Nx es el más pesado de los tres. Viene del mundo Angular pero hoy soporta todos los frameworks. Hace lo mismo que Turborepo — cache, grafo de dependencias, ejecución paralela — pero con más ambición.
Nx no es solo una herramienta de builds. Es un sistema de generación de código. Puedes definir generadores para tus propias convenciones: "crea una nueva app con este template, este linter, estos tests, esta estructura de carpetas". Puedes migrar automáticamente entre versiones de frameworks con sus migraciones codificadas. Puedes visualizar el grafo de dependencias en un navegador con nx graph.
Qué ganas: - Todo lo de Turborepo, más - Generadores de código para estandarizar nuevas apps/paquetes - Migraciones automáticas entre versiones - Una plataforma en la nube con analytics, cache remoto, CI optimizado
Qué cuesta: - Configuración considerable - Curva de aprendizaje - Una filosofía más opinionada - Si no aprovechas los generadores y las migraciones, estás pagando complejidad sin extraer valor
Nx tiene sentido cuando: - Tienes 10+ apps y quieres forzar convenciones consistentes - Tienes varios equipos trabajando en paralelo y necesitas gobernanza - Estás dispuesto a invertir tiempo en setup a cambio de productividad a largo plazo
Para un solo desarrollador o un equipo pequeño, es desproporcionado.
La decisión: cuál elegir cuándo
No hay respuesta universal. La regla práctica que sigo:
Tu situación Herramienta Acabas de tener duplicación y quieres solucionarla rápido npm workspaces Tienes 3-4 apps, builds razonables (< 1 min) npm workspaces Tienes 5+ apps y el CI empieza a doler Turborepo Equipo grande, muchas apps, necesitas gobernanza NxLa tentación del sobredimensionamiento es real. Es fácil mirar un post de Vercel sobre Turborepo y pensar "necesito esto". Pero si next build tarda 800ms en tu máquina, Turborepo te ahorra 800ms. Y el primer turbo build de todas formas no tiene cache. Estás añadiendo una dependencia, un archivo de configuración, y un modelo mental nuevo para optimizar algo que no es tu cuello de botella.
Los problemas que vas a encontrar (y que nadie cuenta)
Todas estas herramientas presumen de "just works". No es verdad. Estos son los problemas reales con los que te vas a topar:
Symlinks y bundlers modernos. Turbopack — el bundler nuevo de Next.js — tiene restricciones sobre seguir symlinks fuera del directorio del proyecto. Cuando tu app importa un paquete local via workspace, el paquete vive fuera de apps/tu-app/. Turbopack puede negarse a compilarlo a menos que configures turbopack.root correctamente. Lo descubrí de la peor manera.
Hoisting y dependencias duplicadas. npm workspaces hoistea dependencias comunes a la raíz. A veces eso rompe herramientas que esperan que cada app tenga su propio node_modules/next. Pasa más con Turbopack que con webpack clásico.
Transpilación de paquetes TypeScript. Si tu paquete compartido está en TypeScript sin precompilar, Next.js necesita saberlo vía transpilePackages: ["@agentikas/blog-ui"]. Si se te olvida, el error que ves es críptico.
Caché de Cloudflare Pages / Vercel con monorepos. Ambas plataformas soportan monorepos, pero el build command y el output directory cambian. Un despliegue que antes funcionaba puede romperse porque la plataforma está mirando en el sitio equivocado.
Tests que comparten estado. Si tus tests cacheaban módulos asumiendo que cada app tenía su copia, un monorepo con paquetes compartidos puede romper esos tests de forma sutil.
Conclusión: empieza pequeño
Mi consejo honesto, después de haber pasado por los tres:
- Empieza con npm workspaces. Resuelve el 80% de los problemas de duplicación.
- Si el build se vuelve lento (y solo entonces), añade Turborepo encima. Es aditivo, no destructivo.
- No pases a Nx a menos que tengas un equipo que lo necesite.
La peor decisión es sobredimensionar la infraestructura antes de tener el problema. La segunda peor es dejar la duplicación creciendo. La correcta es el punto medio: la herramienta más simple que resuelve tu dolor actual.
Para Agentikas, voy con npm workspaces. Tres apps, un par de paquetes compartidos. Si el día que esto crezca tengo que migrar a Turborepo, serán unas horas de trabajo. Y habré pasado meses sin preocuparme por pipelines, caches y generadores que no necesitaba.
En Agentikas construimos infraestructura para la web agéntica. Nuestro blog es open source y puedes ver exactamente cómo está estructurado el monorepo en github.com/agentikas/agentikas-blog.
Comentarios
Cargando comentarios…
Inicia sesión en tu dashboard para participar.