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": "pnpm@10.12.1",
"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
Permission receipt para Claude Code: alcance, prueba y rollback
Patrón de permission receipt para Claude Code: acciones permitidas, aprobación, pruebas, rollback y CTA de ingresos.
Agent Harness seguro para Claude Code y Codex: permisos, verificacion y rollback
Diseña un Agent Harness seguro para Claude Code y Codex con permisos, plan, verificaciones y rollback.
Subagentes de Claude Code: guía práctica para delegar trabajo de forma segura
Guía práctica de subagentes en Claude Code para dividir artículos y código: reglas, prompts, riesgos y checklist.