Praktisches Monorepo mit Claude Code und pnpm workspace
Baue ein kleines Monorepo mit Claude Code und pnpm workspace: Abhängigkeiten, filter, CI und typische Fehler.
pnpm workspace verhindert Kopien im Codebestand
Ich bin Masa, Betreiber von claudecode-lab.com.
Auch ein kleines Produkt bleibt selten eine einzige App. Es gibt bald eine öffentliche Web-App, ein Admin-Panel, gemeinsame UI-Helfer, Konfiguration, Content-Skripte, E-Mail-Jobs und Tests. Das Problem ist nicht die Anzahl der Ordner. Das Problem beginnt, wenn dieselbe Umgebungsvariable, derselbe Button-Helper oder dasselbe Validierungsschema in mehreren Projekten kopiert wird.
pnpm workspace verwaltet mehrere packages in einem Git-Repository. Die offizielle Dokumentation zu pnpm Workspace erklärt, dass ein Workspace mehrere Projekte in einem Repository verbindet und eine pnpm-workspace.yaml im Root benötigt.
Claude Code ist hier weniger Generator als Prüfer. Es kann fehlende workspace:* Abhängigkeiten, falsche Import-Richtungen, zu breite CI-Kommandos und zu große Shared Packages erkennen. Genau diese kleinen Fehler machen ein Monorepo später schwer zu ändern.
Dieser Artikel nutzt pnpm 11.5.0. Für den größeren Kontext passen Claude Code Monorepo Management und Claude Code Dependency Management dazu.
Zielstruktur: Vier packages reichen am Anfang
Du brauchst nicht sofort ein riesiges Platform-Repository. Zwei Apps und zwei Shared Packages sind ein guter Start.
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
Es gibt mindestens drei praktische Use Cases. Erstens kann packages/ui kleine Präsentationshelfer und UI-Primitives enthalten, die Web und Admin gemeinsam nutzen. Zweitens sammelt packages/config öffentliche URLs, Feature-Flag-Namen und Produktkonstanten. Drittens kann später packages/contracts für API-Typen oder Zod-Schemas zwischen Frontend und Backend hinzukommen.
Der Fehler ist ein Paket wie packages/common, in dem alles landet. Shared Code sollte aus jeder App dieselbe Bedeutung haben. Eine gute Claude-Code-Anweisung lautet: “Extrahiere nur den doppelten UI-Helper; Billing-Logik bleibt in der App.”
Minimale Konfiguration zum Kopieren
Lege zuerst pnpm-workspace.yaml an. Die offizielle Seite zu pnpm-workspace.yaml zeigt, wie Verzeichnisse einbezogen oder ausgeschlossen werden.
packages:
- "apps/*"
- "packages/*"
catalog:
typescript: ^5.8.3
Das Root-package.json orchestriert nur die Package-Kommandos.
{
"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
}
}
In .npmrc machst du lokale Auflösung eindeutig:
link-workspace-packages=false
save-workspace-protocol=rolling
shared-workspace-lockfile=true
strict-peer-dependencies=true
auto-install-peers=false
Wichtig ist workspace:*. pnpm dokumentiert, dass das workspace: Protokoll nicht außerhalb des lokalen Workspaces auflöst. So installierst du nicht versehentlich ein Registry-Paket mit demselben Namen wie dein internes Paket.
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 hängt über das Workspace-Protokoll von config ab.
{
"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}`;
}
Die Web-App deklariert nur ihre direkten internen Abhängigkeiten.
{
"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"));
Ausführen:
corepack pnpm install
corepack pnpm --filter @acme/web build
corepack pnpm -r --sort --if-present build
Claude Code muss die Package-Grenzen kennen
Claude Code hat eine offizielle Anleitung für monorepos and large codebases. Die wichtigste Aussage: irrelevante Dateien sollen nicht unnötig in den Kontext. Bei pnpm workspaces bedeutet das, Aufgaben klar auf Package-Ebene zu begrenzen.
Das Root-CLAUDE.md bleibt global:
# 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.
Lokale Regeln gehören in das jeweilige Verzeichnis. Die Memory-Dokumentation von Claude Code erklärt, dass CLAUDE.md dauerhafte Instruktionen liefert. Deshalb sollten diese Dateien kurz und konkret bleiben.
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.
"
Diese Aufforderung erzwingt Inspektion vor Bearbeitung und senkt das Risiko unnötig großer Refactors.
filter hält Alltag und CI klein
pnpm Filtering beschränkt ein Kommando auf bestimmte Packages.
pnpm --filter @acme/web build
pnpm --filter @acme/web... build
pnpm --filter ...@acme/ui test
pnpm --filter "...[origin/main]" --if-present test
Der häufigste Fehler ist die Richtung der .... @acme/web... wählt web und dessen Abhängigkeiten. ...@acme/ui wählt ui und die Packages, die ui verwenden. Nach einer UI-Änderung nur @acme/ui... auszuführen kann Web/Admin-Tests überspringen.
Ein fokussierter CI-Workflow:
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
Typische Fehler und konkrete Gegenmaßnahmen
Der erste Fehler ist eine interne Abhängigkeit als normale semver-Version:
{
"dependencies": {
"@acme/ui": "^0.1.0"
}
}
Im Workspace sollte sie so aussehen:
{
"dependencies": {
"@acme/ui": "workspace:*"
}
}
Der zweite Fehler ist App-spezifische Logik im Shared Package. packages/ui sollte keine APIs aufrufen und keine Billing-Pläne entscheiden. Der dritte Fehler sind Zyklen. Wenn packages/ui aus apps/web importiert, ist die Richtung falsch.
Ich lasse Claude Code regelmäßig prüfen:
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.
"
Wenn Packages veröffentlicht werden, ergänze Changesets:
pnpm add -Dw @changesets/cli
pnpm changeset init
pnpm changeset
pnpm changeset version
pnpm -r publish --access public
pnpm selbst bringt keinen kompletten Release-Prozess für Workspace-Versionierung mit. Die Dokumentation verweist auf Changesets oder Rush. App-Packages in apps/* bleiben normalerweise private: true.
Zusammenfassung und Praxisergebnis
pnpm workspace ist keine schwere Build-Plattform, sondern eine kleine Grundlage für explizite Abhängigkeiten zwischen UI, Konfiguration, Typen und Tests. Claude Code ist stark, wenn es den Package-Graph prüft, minimale Änderungen vorschlägt und CI-Fehler eingrenzt.
Als Nächstes helfen CLAUDE.md Best Practices und Claude Code Testing Strategies. Für Teamregeln und Einführung ist Claude Code training der praktische Einstieg.
Ich habe die Beispiele mit Windows, Node.js 22, Corepack und pnpm 11.5.0 geprüft. In echten Projekten sind fehlendes workspace:* und falsch gerichtete filter die häufigsten Fehler. Claude Code vor jeder Änderung den Package-Graph ausgeben zu lassen, findet unnötige Abstraktionen und Zyklen deutlich früher.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude Code Workflow von Obsidian zu CLAUDE.md
Obsidian-Arbeitsnotizen in CLAUDE.md-Betriebsnotizen verwandeln und Kontext nicht ständig neu erklären.
Claude Code Revenue CTA Routing: Artikel zu PDF, Gumroad und Beratung führen
Ein Claude-Code-Ablauf, der Leser nach Absicht zu Gratis-PDF, Gumroad oder Beratung führt.
Claude-Code-Team-Handoff-Regeln: Belege, Berechtigungen, Rollback und Umsatzpfade
Ein praktisches Claude-Code-Handoff für Review-Belege, Berechtigungen, Rollback, Gratis-PDF, Gumroad und Beratung.