Gestionar monorepos con Claude Code: pnpm, Turborepo, Nx y CI
Guía práctica para gestionar monorepos con Claude Code: mapa del repo, pnpm workspace, affected tasks, CODEOWNERS y CI.
Un monorepo es un repositorio Git que gestiona varias aplicaciones y librerías en el mismo lugar. Permite compartir tipos, componentes UI, configuración y reglas de CI. El riesgo aparece cuando no hay límites claros: Claude Code puede hacer un cambio que parece pequeño en packages/shared y terminar afectando apps/web, apps/api y varios paquetes a la vez.
El flujo práctico es este: pedir primero un mapa del repositorio, definir package boundaries, usar pnpm workspace con workspace:*, y ejecutar solo las tareas afectadas con Turborepo o Nx. Para alinear conceptos, usa las guías oficiales de Nx sobre monorepos, Nx affected, Nx mental model, pnpm y Turborepo.
Estructura objetivo
Antes de editar, Claude Code debe entender la arquitectura.
graph TD
WEB["apps/web"] --> UI["packages/ui"]
WEB --> SHARED["packages/shared"]
API["apps/api"] --> SHARED
UI --> CONFIG["packages/config"]
SHARED --> CONFIG
CI["CI affected tasks"] --> WEB
CI --> API
apps/* son aplicaciones desplegables. packages/* son piezas reutilizables. Una package boundary define qué paquete puede depender de otro. Es mejor darle esas reglas a Claude Code de forma explícita.
Primer prompt para Claude Code
Analiza este repositorio como monorepo.
Supuestos:
- apps/web es la app Next.js
- apps/api es el servidor API
- packages/ui contiene UI reutilizable
- packages/shared contiene tipos, validaciones y funciones puras
- packages/config contiene ESLint, TypeScript, Prettier y configuración de tests
Reglas:
- apps/* no debe depender directamente de apps/*
- packages/* no debe depender de apps/*
- los paquetes internos usan workspace:*
- después de editar, lint/test/build deben correr como affected tasks
Primero genera un mapa del repo: dependencias, ciclos peligrosos, archivos demasiado compartidos y comandos de CI.
No edites archivos todavía.
Esa última línea evita que Claude Code empiece a mover código antes de entender el sistema.
pnpm workspace
packages:
- "apps/*"
- "packages/*"
En el package.json raíz conviene exponer comandos estables.
{
"name": "acme-monorepo",
"private": true,
"packageManager": "[email protected]",
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"typecheck": "turbo run typecheck",
"ci:affected": "turbo run lint test build --affected",
"check:deps": "node scripts/check-workspace-deps.cjs"
}
}
Las dependencias internas deben declararse así:
{
"dependencies": {
"@acme/shared": "workspace:*",
"@acme/ui": "workspace:*"
}
}
Prompt recomendado:
Haz que apps/web pueda usar @acme/ui y @acme/shared.
Usa workspace:* en package.json.
No uses imports con ../../packages.
La modificación debe poder verificarse con pnpm check:deps y pnpm ci:affected.
Turborepo y Nx affected
Turborepo funciona muy bien cuando cada paquete ya tiene scripts.
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"lint": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
},
"typecheck": {
"dependsOn": ["^build"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
Nx aporta un project graph más fuerte y una lógica affected más rica.
pnpm dlx nx@latest init
pnpm nx affected -t lint test build --base=origin/main --head=HEAD
La instrucción correcta para Claude Code no es “ejecuta todo”, sino “ejecuta los checks afectados por este diff”.
CODEOWNERS y política de dependencias
/apps/web/ @acme/frontend
/apps/api/ @acme/backend
/packages/ui/ @acme/design-system
/packages/shared/ @acme/platform
/packages/config/ @acme/platform
/pnpm-workspace.yaml @acme/platform
/turbo.json @acme/platform
La política también debe validarse con código. Guarda esto como scripts/check-workspace-deps.cjs.
const fs = require("node:fs");
const path = require("node:path");
const ROOT = process.cwd();
const WORKSPACE_DIRS = ["apps", "packages"];
const DEP_FIELDS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
function findPackageDirs(baseDir) {
const absoluteBase = path.join(ROOT, baseDir);
if (!fs.existsSync(absoluteBase)) return [];
return fs
.readdirSync(absoluteBase, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => path.join(absoluteBase, entry.name))
.filter((dir) => fs.existsSync(path.join(dir, "package.json")));
}
const packages = WORKSPACE_DIRS.flatMap(findPackageDirs).map((dir) => {
const manifest = readJson(path.join(dir, "package.json"));
return { dir, name: manifest.name, manifest };
});
const byName = new Map(packages.map((pkg) => [pkg.name, pkg]));
let failed = false;
for (const pkg of packages) {
for (const field of DEP_FIELDS) {
const deps = pkg.manifest[field] || {};
for (const [name, range] of Object.entries(deps)) {
const internal = byName.get(name);
if (!internal) continue;
const fromDir = path.relative(ROOT, pkg.dir).replace(/\\/g, "/");
const toDir = path.relative(ROOT, internal.dir).replace(/\\/g, "/");
if (!String(range).startsWith("workspace:")) {
console.error(`${pkg.name}: ${name} must use workspace:* in ${field}`);
failed = true;
}
if (toDir.startsWith("apps/")) {
console.error(`${pkg.name}: ${fromDir} must not depend on app package ${toDir}`);
failed = true;
}
}
}
}
if (failed) process.exit(1);
console.log(`Checked ${packages.length} workspace packages.`);
Checklist de CI
name: monorepo-ci
on:
pull_request:
push:
branches: [main]
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm check:deps
- run: pnpm ci:affected
fetch-depth: 0 es importante porque los checks affected necesitan historial Git suficiente.
Casos de uso
-
Cambiar un Button en
packages/ui. Claude Code debe mantener estable la API pública y listar las pantallas afectadas enapps/web. -
Mover DTO compartidos a
packages/shared. Un DTO es la forma de datos entre API y UI; no es el modelo de base de datos. -
Actualizar TypeScript, Next.js o herramientas de test. Empieza por
packages/configy verifica solo las apps afectadas. -
Implementar funcionalidades transversales como pagos o búsqueda. Pide a Claude Code dividir el trabajo en PRs pequeños.
Errores comunes
Primero, convertir packages/shared en un cajón de sastre. Solo debe tener código estable, genérico y fácil de probar.
Segundo, usar imports relativos como ../../packages/shared/src. Funcionan al principio, pero rompen la frontera del paquete.
Tercero, introducir Turborepo y Nx profundamente al mismo tiempo. Elige un modelo principal y añade el otro solo si hace falta.
Cuarto, aceptar “funciona localmente” como prueba suficiente. El PR debe explicar paquetes modificados, apps afectadas, comandos ejecutados y riesgos pendientes.
Prompt de revisión
Revisa este diff desde la perspectiva de monorepo.
Comprueba:
- no hay dependencia directa de apps/* a apps/*
- packages/* no depende de apps/*
- las dependencias internas usan workspace:*
- packages/shared solo contiene código compartido estable
- affected lint/test/build cubre el cambio
- CODEOWNERS deja clara la responsabilidad de revisión
Devuelve:
- blockers
- correcciones recomendadas
- comandos verificados
- resumen de impacto para el PR
Para profundizar, lee Claude Code y Nx workspace, Claude Code y pnpm workspace, Claude Code con Turborepo y colaboración en equipo con Claude Code.
Si tu equipo quiere introducir Claude Code en un monorepo real, el trabajo importante es definir límites, owners, CI y prompts de revisión. ClaudeCodeLab puede ayudar con formación y consultoría de Claude Code usando tu propio repositorio como base.
Resumen
Claude Code funciona bien en monorepos cuando las restricciones son explícitas. Repo map, package boundaries, pnpm workspace, Turborepo/Nx affected tasks, CODEOWNERS, política de dependencias y checklist de CI convierten una salida de IA en un flujo repetible. En la práctica, imponer workspace:* y estandarizar pnpm ci:affected reduce revisiones incompletas y CI innecesario.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Crea un log de presupuesto para Claude Code antes de que el coste se vuelva borroso
Registra quién usa Claude Code, para qué trabajo y qué resultado produjo en el equipo.
Revisión de 3 minutos antes del commit: confirma qué tocó Claude Code
Cómo detectar en 3 minutos los cambios que Claude Code amplió por su cuenta antes del commit: alcance, diff, prueba y stage selectivo.
El registro de riesgos antes de llevar Claude Code a tu equipo
Cómo armar un registro de riesgos que evita accidentes de permisos, CI y publicación al llevar Claude Code a tu equipo.