Monorepo práctico con Claude Code y pnpm workspace
Diseña un monorepo pequeño con Claude Code y pnpm workspace: dependencias, filter, CI y errores frecuentes.
pnpm workspace evita que tu producto se llene de copias
Soy Masa, el operador de claudecode-lab.com.
Un producto pequeño rara vez sigue siendo una sola aplicación. Primero aparece la web pública, luego el panel de administración, después componentes compartidos, configuración, scripts de contenido, trabajos de email y utilidades de prueba. El problema real no es tener muchas carpetas. El problema empieza cuando copias el mismo nombre de variable de entorno, el mismo helper de UI o el mismo schema de validación en tres sitios.
pnpm workspace sirve para gestionar varios packages dentro de un mismo repositorio Git. La documentación oficial de pnpm Workspace explica que un workspace une varios proyectos y necesita un archivo pnpm-workspace.yaml en la raíz.
Claude Code encaja mejor como revisor que como generador ciego. Puede revisar si falta workspace:*, si packages/* importa desde apps/*, si CI ejecuta todo cuando solo cambió un paquete, o si un paquete compartido se está convirtiendo en un cajón de sastre. Ese tipo de revisión repetida es lo que hace que un monorepo siga siendo manejable.
Este artículo usa pnpm 11.5.0. Para el contexto general, lee también gestión de monorepos con Claude Code y gestión de dependencias.
Estructura objetivo: cuatro packages bastan para empezar
No necesitas crear una plataforma gigante el primer día. Esta estructura suele ser suficiente:
flowchart LR
web["apps/web\n@acme/web"] --> ui["packages/ui\n@acme/ui"]
web --> config["packages/config\n@acme/config"]
admin["apps/admin\n@acme/admin"] --> ui
admin --> config
acme-workspace/
apps/web/src/main.ts
apps/web/package.json
apps/admin/src/main.ts
apps/admin/package.json
packages/config/src/index.ts
packages/config/package.json
packages/ui/src/index.ts
packages/ui/package.json
pnpm-workspace.yaml
package.json
.npmrc
CLAUDE.md
Hay al menos tres casos de uso claros. Primero, packages/ui contiene helpers de presentación y piezas pequeñas que usan la web y el panel admin. Segundo, packages/config centraliza nombres de flags, URL públicas y constantes para que las apps no diverjan. Tercero, más adelante puedes añadir packages/contracts para compartir tipos de API o schemas Zod entre frontend y backend.
El anti-patrón es crear packages/common y meter todo ahí. Código compartido significa “tiene el mismo significado para todos los consumidores”, no “no sé dónde ponerlo”. Al pedir ayuda a Claude Code, conviene decir: “extrae solo el helper de UI duplicado y deja la lógica de facturación en la app”.
Configuración mínima copiable
Empieza con pnpm-workspace.yaml. La página oficial de pnpm-workspace.yaml muestra cómo incluir y excluir directorios.
packages:
- "apps/*"
- "packages/*"
catalog:
typescript: ^5.8.3
El package.json raíz solo coordina comandos.
{
"name": "acme-workspace",
"private": true,
"packageManager": "pnpm@11.5.0",
"scripts": {
"check:web": "pnpm --filter @acme/web build",
"build": "pnpm -r --sort --if-present build",
"test": "pnpm -r --if-present test",
"changed:test": "pnpm --filter \"...[origin/main]\" --if-present test"
},
"devDependencies": {
"typescript": "catalog:"
}
}
tsconfig.base.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"skipLibCheck": true,
"noEmit": true
}
}
En .npmrc, evita resoluciones ambiguas:
link-workspace-packages=false
save-workspace-protocol=rolling
shared-workspace-lockfile=true
strict-peer-dependencies=true
auto-install-peers=false
La parte importante es usar workspace:* para dependencias internas. La documentación de pnpm indica que el protocolo workspace: no resuelve fuera del workspace local. Eso evita instalar por accidente un paquete del registry con el mismo nombre.
packages/config/package.json:
{
"name": "@acme/config",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": {
"types": "./src/index.ts",
"import": "./src/index.ts"
}
}
}
export const appConfig = {
productName: "Acme Workspace",
supportEmail: "support@example.com",
publicSiteUrl: "https://example.com"
} as const;
packages/ui depende de config con el protocolo workspace:
{
"name": "@acme/ui",
"version": "0.1.0",
"private": true,
"type": "module",
"dependencies": {
"@acme/config": "workspace:*"
},
"exports": {
".": {
"types": "./src/index.ts",
"import": "./src/index.ts"
}
}
}
import { appConfig } from "@acme/config";
export function renderPrimaryButton(label: string): string {
return `[${appConfig.productName}] ${label}`;
}
La app declara únicamente lo que usa:
{
"name": "@acme/web",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"build": "tsc -p tsconfig.json"
},
"dependencies": {
"@acme/config": "workspace:*",
"@acme/ui": "workspace:*"
}
}
apps/web/tsconfig.json:
{
"extends": "../../tsconfig.base.json",
"include": ["src"]
}
import { appConfig } from "@acme/config";
import { renderPrimaryButton } from "@acme/ui";
console.log(appConfig.publicSiteUrl);
console.log(renderPrimaryButton("Start trial"));
Ejecuta:
corepack pnpm install
corepack pnpm --filter @acme/web build
corepack pnpm -r --sort --if-present build
Enseña a Claude Code los límites del workspace
Claude Code tiene una guía oficial para monorepos and large codebases. La idea clave es reducir lo que el agente lee. En un pnpm workspace, si lanzas Claude Code desde la raíz sin contexto, puede mezclar reglas de packages que no tienen relación con la tarea.
El CLAUDE.md raíz debe contener solo reglas globales:
# Acme Workspace
This repository is a pnpm workspace.
Packages:
- apps/web: customer-facing TypeScript app
- apps/admin: internal admin app
- packages/ui: shared UI helpers
- packages/config: shared runtime constants
Rules:
- Use pnpm, not npm or yarn.
- Add internal dependencies with workspace:*.
- Run focused commands with pnpm --filter before full workspace commands.
- Do not move business logic into packages/ui.
Las reglas locales van en el directorio correspondiente. La documentación de memory de Claude Code explica que CLAUDE.md funciona como instrucciones persistentes. Por eso conviene que sean concretas y mantenibles.
claude -p "
Inspect this pnpm workspace. Do not edit files yet.
List the package graph, scripts, and risky dependency directions.
Then propose the smallest change needed to share UI helpers between apps/web and apps/admin.
"
Esta forma obliga a Claude Code a inspeccionar antes de editar y reduce cambios demasiado amplios.
filter mantiene ligeros el desarrollo y CI
pnpm Filtering limita un comando a un subconjunto de packages.
pnpm --filter @acme/web build
pnpm --filter @acme/web... build
pnpm --filter ...@acme/ui test
pnpm --filter "...[origin/main]" --if-present test
El error común está en la dirección de .... @acme/web... selecciona web y sus dependencias. ...@acme/ui selecciona ui y quienes dependen de ui. Si cambias UI y ejecutas solo @acme/ui..., puedes saltarte las pruebas de web y admin.
Un workflow de CI puede centrarse en lo afectado:
name: workspace-check
on:
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: corepack enable
- run: corepack prepare pnpm@11.5.0 --activate
- run: pnpm install --frozen-lockfile
- run: pnpm --filter "...[origin/main]" --if-present test
- run: pnpm --filter "...[origin/main]" --if-present build
Errores frecuentes y correcciones concretas
El primer error es escribir una dependencia interna como semver normal:
{
"dependencies": {
"@acme/ui": "^0.1.0"
}
}
Dentro del workspace debe ser:
{
"dependencies": {
"@acme/ui": "workspace:*"
}
}
El segundo error es poner lógica de negocio dentro de un paquete compartido. packages/ui no debería llamar APIs ni decidir planes de facturación. El tercer error es crear dependencias circulares: si packages/ui importa desde apps/web, la dirección está mal.
Pide a Claude Code una revisión de límites:
claude -p "
Check this workspace for circular dependencies and misplaced imports.
Focus on packages/* importing from apps/*, duplicated config values,
and dependencies that should be workspace:*.
Return findings with file paths and minimal fixes.
"
Si vas a publicar packages, añade Changesets:
pnpm add -Dw @changesets/cli
pnpm changeset init
pnpm changeset
pnpm changeset version
pnpm -r publish --access public
pnpm no intenta resolver todo el versionado por sí solo; la documentación recomienda herramientas como Changesets o Rush. Mantén apps/* con private: true salvo que realmente publiques esas apps como paquetes.
Resumen y resultado práctico
pnpm workspace no es una herramienta pesada. Es una base pequeña para convertir UI, configuración, tipos y pruebas compartidas en dependencias explícitas. Claude Code funciona mejor cuando lo usas para revisar el grafo de packages, proponer cambios mínimos y acotar fallos de CI.
Como siguiente paso, revisa buenas prácticas de CLAUDE.md y estrategias de testing con Claude Code. Para reglas de equipo o adopción interna, la página de Claude Code training es el punto de entrada.
Probé esta estructura con Windows, Node.js 22, Corepack y pnpm 11.5.0. En la práctica, los fallos que más aparecen son olvidar workspace:* y usar el filter en la dirección equivocada. Hacer que Claude Code muestre primero el package graph ayuda a detectar abstracciones innecesarias y dependencias circulares antes de editar.
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
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.