Claude Code Monorepo: Nx, Turborepo und große Codebasen mit KI verwalten

Ein Monorepo klingt nach einer einfachen Idee: alle Projekte in einem Repository. In der Praxis landet man schnell bei Fragen wie: Welches Paket hat eine Build-Abhängigkeit auf welches? Warum bricht ein PR in einem Package, das er gar nicht anfasst? Wie schreibe ich Changesets so, dass nur die wirklich betroffenen Pakete eine neue Version bekommen?

Claude Code hilft hier auf eine Art, die sich von normaler Dokumentations-Suche unterscheidet: Es liest den gesamten Dependency-Graph, versteht welche Pakete voneinander abhängen, und gibt dir spezifische Antworten für dein Monorepo — nicht generische Ratschläge für Monorepos im Allgemeinen. Dieser Artikel zeigt, wie das konkret funktioniert.

Claude Code Mastery — Monorepos, Agents, Hooks auf Deutsch

Nicht nur Monorepos: der Kurs zeigt, wie du Claude Code wirklich produktiv einsetzt — für komplexe Codebasen, autonome Agents und professionelle Workflows. Einmalig bezahlt, kein Abo.

Zum Kurs — Jetzt starten → Basis ab €29 · Pro ab €49 · Einmalzahlung · Kein Abo

1. Monorepo-Grundlagen und Vorteile

Ein Monorepo ist ein einzelnes Git-Repository, das mehrere Projekte oder Pakete enthält. Das Gegenstück dazu ist das Polyrepo: ein Repository pro Projekt. Beide Ansätze haben echte Vor- und Nachteile — Monorepos sind keine Universallösung, aber für Teams mit starker Code-Wiederverwendung oft die bessere Wahl.

Die drei Kernvorteile, die Monorepos in der Praxis auszeichnen:

Der Nachteil: Monorepos brauchen Werkzeuge. Ohne Build-Orchestrierung werden lange Build-Zeiten zum Problem. Ohne Dependency-Graph-Analyse baut man zu viel oder zu wenig. Genau hier kommen Nx und Turborepo ins Spiel — und genau hier hilft Claude Code am meisten.

Wann ist ein Monorepo sinnvoll? Wenn mehrere Projekte Code teilen und diese Code-Teile sich häufig gemeinsam ändern. Wenn drei unabhängige Teams an drei vollständig isolierten Projekten arbeiten, die nichts miteinander teilen, ist ein Monorepo meistens Overhead ohne Nutzen.

Claude Code und der Monorepo-Überblick

Das erste Problem in einem Monorepo: den Überblick behalten. Wer von wem abhängt, welche Pakete existieren, was wo konfiguriert ist. Claude Code kann dir diesen Überblick verschaffen, ohne dass du selbst durch Dutzende von package.json-Dateien navigieren musst:

claude "Analysiere die Struktur dieses Monorepos: welche Pakete gibt es,
wie hängen sie voneinander ab, und welche sind shared Libraries?"

Claude Code liest alle relevanten package.json-Dateien, die Workspace-Konfiguration und — wenn vorhanden — die Nx- oder Turborepo-Konfiguration, und gibt dir eine klare Übersicht zurück. Nicht als generische Beschreibung, sondern als konkretes Bild deines spezifischen Repos.

2. Nx Workspace einrichten

Nx ist ein Build-System mit starkem Fokus auf Entwicklerproduktivität. Die Kernideen: Computation Caching (Tasks werden nicht doppelt ausgeführt), Affected-Analyse (nur was wirklich betroffen ist, wird gebaut oder getestet), und ein expliziter Dependency-Graph, der aus dem Code abgeleitet wird.

Ein neues Nx Workspace anlegen:

npx create-nx-workspace@latest my-monorepo --preset=ts
cd my-monorepo

Nx strukturiert Projekte in apps/ (ausführbare Anwendungen) und libs/ (wiederverwendbare Libraries). Diese Trennung ist semantisch, nicht technisch erzwungen — aber sie hilft dabei, den Dependency-Flow klar zu halten.

Generators: Projekte konsistent anlegen

Der häufigste Fehler beim manuellen Monorepo-Aufbau: Projekte werden unterschiedlich konfiguriert, weil jeder Entwickler einen leicht anderen Ausgangspunkt wählt. Nx-Generators lösen das:

# Neue React-Anwendung anlegen
nx g @nx/react:app customer-portal

# Neue shared Library anlegen
nx g @nx/react:lib ui-components --publishable --importPath=@my-org/ui-components

# Neue Utility-Library
nx g @nx/js:lib utils --publishable --importPath=@my-org/utils

Jede Library und App bekommt dieselbe Basisstruktur, dieselbe TypeScript-Konfiguration, dasselbe Test-Setup. Claude Code kann dir helfen, eigene Generators zu schreiben, wenn die Standard-Generators nicht passen:

claude "Schreib einen Nx-Generator der eine neue API-Bibliothek
anlegt mit: TypeScript-Konfiguration, Jest-Setup, einer index.ts
mit einem Barrel-Export, und einem passenden Eintrag in tsconfig.base.json"

Executors: Tasks definieren und orchestrieren

Executors sind Nx's Art, Build-Tasks zu beschreiben. In project.json definierst du, welcher Executor für welche Task verwendet wird:

{
  "name": "ui-components",
  "targets": {
    "build": {
      "executor": "@nx/rollup:rollup",
      "options": {
        "outputPath": "dist/libs/ui-components",
        "tsConfig": "libs/ui-components/tsconfig.lib.json",
        "project": "libs/ui-components/package.json",
        "entryFile": "libs/ui-components/src/index.ts",
        "external": ["react", "react-dom"]
      }
    },
    "test": {
      "executor": "@nx/jest:jest",
      "options": {
        "jestConfig": "libs/ui-components/jest.config.ts"
      }
    }
  }
}

Affected: nur bauen was sich geändert hat

Das mächtigste Feature von Nx für den CI-Einsatz:

# Nur Projekte bauen, die von Änderungen betroffen sind
nx affected --target=build --base=main --head=HEAD

# Nur Tests ausführen für betroffene Projekte
nx affected --target=test --base=main --head=HEAD

# Dependency-Graph visualisieren
nx graph

Claude Code kann dir helfen, die Affected-Analyse zu verstehen und zu debuggen, wenn sie unerwartet zu viel oder zu wenig erkennt:

claude "nx affected baut immer alle Projekte, auch wenn ich nur eine
Datei in ui-components ändere. Warum könnte das passieren und
wie finde ich die fehlerhafte Abhängigkeit?"

Häufige Falle mit impliziten Abhängigkeiten: Nx leitet den Dependency-Graph primär aus TypeScript-Imports ab. Wenn ein Paket ein anderes über einen Pfad-Alias importiert, der nicht in tsconfig.base.json konfiguriert ist, sieht Nx diese Abhängigkeit nicht — und affected gibt Fehlbefunde.

3. Turborepo optimieren

Turborepo ist ein Build-System mit Fokus auf Geschwindigkeit durch intelligentes Caching. Wo Nx einen breiteren Scope hat (Generators, Executors, Plugins), ist Turborepo schlanker und leichter in bestehende Setups integrierbar.

Ein turbo.json definiert, wie Tasks voneinander abhängen:

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "test": {
      "dependsOn": ["^build"],
      "outputs": [],
      "cache": false
    },
    "lint": {
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Das ^-Präfix ist entscheidend: "dependsOn": ["^build"] bedeutet, dass die build-Task aller Dependencies zuerst abgeschlossen sein muss, bevor das aktuelle Paket gebaut wird. Ohne dieses Präfix würden alle build-Tasks parallel gestartet, auch wenn Abhängigkeiten noch nicht gebaut sind.

Lokales Caching: identische Tasks nicht doppelt ausführen

Turborepo speichert Task-Outputs lokal in .turbo/. Wenn du dieselbe Task mit denselben Inputs ein zweites Mal ausführst, liest Turborepo aus dem Cache statt neu zu bauen:

turbo build
# Erster Lauf: baut alle Pakete, ~45 Sekunden

turbo build
# Zweiter Lauf (ohne Änderungen): alle aus Cache, ~0.5 Sekunden

Claude Code kann dir helfen, Cache-Misses zu verstehen und zu beheben:

claude "Turborepo baut mein ui-components-Paket immer neu, obwohl
ich nichts geändert habe. Wie debugge ich den Cache-Miss?
Hier ist mein turbo.json: [inhalt]"

Remote Caching: Team-weites Cache-Sharing

Lokales Caching hilft dem einzelnen Entwickler. Remote Caching teilt den Cache über das gesamte Team — und mit der CI-Pipeline:

# Vercel Remote Cache (offizieller Anbieter)
npx turbo login
npx turbo link

# Selbst gehosteter Remote Cache (open-source Alternative)
# ducktape/turborepo-remote-cache oder ähnliche
TURBO_API="https://cache.example.com" \
TURBO_TOKEN="your-token" \
TURBO_TEAM="your-team" \
turbo build

Mit Remote Caching baut die CI-Pipeline nicht, was ein Entwickler auf seinem lokalen Rechner bereits gebaut hat. In großen Teams kann das die CI-Zeit um 60–80 % reduzieren.

Parallele Ausführung kontrollieren

# Maximale Parallelität explizit setzen (Standard: CPU-Kerne - 1)
turbo build --concurrency=4

# Sequenziell (für Debugging)
turbo build --concurrency=1

Turborepo vs. Nx: Beide Tools lösen dasselbe Kernproblem — effiziente Task-Ausführung in Monorepos. Turborepo ist leichter zu integrieren und braucht weniger Konfiguration. Nx bietet mehr Features (Generators, Plugins, DevServer-Integration), braucht dafür mehr Einarbeitung. Für Teams die schnell starten wollen: Turborepo. Für Teams die ein vollständiges Build-System wollen: Nx.

4. Paket-Management im Monorepo

Das Paket-Management ist das Fundament jedes Monorepos. Drei Ansätze dominieren den Markt: npm Workspaces (seit npm 7), pnpm Workspaces und Yarn Workspaces. pnpm hat sich in den letzten Jahren als bevorzugte Wahl für Monorepos etabliert, weil es das Phantom-Dependency-Problem löst und dabei deutlich weniger Speicherplatz verbraucht.

pnpm Workspaces einrichten

Eine pnpm-workspace.yaml im Root des Repos definiert, welche Verzeichnisse Pakete enthalten:

packages:
  - 'apps/*'
  - 'libs/*'
  - 'packages/*'

Jedes Paket bekommt eine eigene package.json. Lokale Pakete werden über das workspace:-Protokoll referenziert:

{
  "name": "@my-org/customer-portal",
  "dependencies": {
    "@my-org/ui-components": "workspace:*",
    "@my-org/utils": "workspace:*",
    "react": "^18.3.0"
  }
}

Das workspace:*-Protokoll sorgt dafür, dass beim lokalen Entwickeln immer die aktuelle Version aus dem Monorepo verwendet wird — keine veralteten Builds aus dem npm-Cache.

npm Workspaces (wenn pnpm nicht infrage kommt)

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "apps/*",
    "libs/*"
  ]
}
# Alle Dependencies installieren
npm install

# Nur in einem bestimmten Workspace
npm install lodash --workspace=apps/customer-portal

# Befehl in allen Workspaces ausführen
npm run build --workspaces --if-present

Changesets: Versionen und Changelogs verwalten

Changesets ist das Standard-Tool für Versionierung in Monorepos. Es löst das Problem, dass eine einzelne Änderung mehrere Pakete betrifft und diese Pakete einzeln versioniert werden müssen:

# Changesets installieren
pnpm add -D @changesets/cli -w
pnpm changeset init

# Nach einer Änderung: Changeset erstellen
pnpm changeset
# → interaktiver Prompt: welche Pakete wurden geändert?
# → welcher Versionstyp (patch/minor/major)?
# → Beschreibung der Änderung

# Alle ausstehenden Changesets anwenden (Versionen bumpen)
pnpm changeset version

# Pakete veröffentlichen
pnpm changeset publish

Claude Code kann dir helfen, Changesets richtig einzusetzen und häufige Fehler zu vermeiden:

claude "Erkläre mir, wann ich patch, minor oder major für ein
Changeset wählen soll. Ich habe eine breaking change in meiner
ui-components Library gemacht: ich habe die Props eines Buttons umbenannt."
"Das ist eine breaking change — also major für ui-components. Alle Pakete, die ui-components direkt importieren, müssen ihre Abhängigkeit auf die neue Major-Version updaten. Turborepo wird diese Pakete automatisch als affected erkennen, wenn sie die neue Version konsumieren."

5. Shared Libraries und Code-Splitting

Shared Libraries sind der eigentliche Wertversprechen eines Monorepos. Aber sie brauchen Struktur — sonst entsteht eine God-Library, die alles enthält und bei jeder Änderung irgendwo etwas bricht.

Design System als shared Library

Eine Design-System-Library enthält Basiskomponenten (Buttons, Inputs, Cards), Tokens (Farben, Abstände, Typografie) und eventuell Layout-Hilfsmittel. Sie hat keine App-spezifische Logik und keine Business-Logik.

libs/
  ui-components/
    src/
      components/
        Button/
          Button.tsx
          Button.stories.tsx
          Button.test.tsx
          index.ts
        Input/
          ...
      tokens/
        colors.ts
        spacing.ts
        typography.ts
      index.ts          ← Barrel-Export

Der Barrel-Export (index.ts) kontrolliert, was nach außen sichtbar ist:

// libs/ui-components/src/index.ts
export { Button } from './components/Button';
export { Input } from './components/Input';
export { Card } from './components/Card';
export * from './tokens/colors';
export * from './tokens/spacing';

Claude Code kann dir helfen, die Library-Struktur sauber zu halten und Barrel-Exports zu optimieren:

claude "Mein ui-components-Barrel-Export exportiert alles, was dazu führt
dass Apps die gesamte Library im Bundle haben. Wie restructuriere ich
das für besseres Tree-Shaking?"

Utils-Library: Klare Grenzen setzen

Eine Utils-Library verführt dazu, alles hineinzuwerfen was "irgendwie nützlich" ist. Das Ergebnis ist eine unstrukturierte Sammlung, in der niemand mehr findet, was er sucht. Besser: explizite Kategorisierung.

libs/
  utils/
    src/
      date/
        formatDate.ts
        parseDate.ts
        index.ts
      string/
        slugify.ts
        truncate.ts
        index.ts
      validation/
        email.ts
        phone.ts
        index.ts
      index.ts

Gemeinsame TypeScript-Types

Eine Types-Library vermeidet, dass jedes Paket seine eigene Definition von User, Product oder ApiResponse hat:

// libs/types/src/domain.ts
export interface User {
  id: string;
  email: string;
  name: string;
  role: 'admin' | 'member' | 'viewer';
}

export interface Product {
  id: string;
  sku: string;
  name: string;
  price: number;
  currency: string;
}

export type ApiResponse<T> = {
  data: T;
  error: null;
} | {
  data: null;
  error: { message: string; code: string };
};

Claude Code kann die Typen über alle Pakete hinweg analysieren und Inkonsistenzen finden:

claude "In meinem Monorepo hat jedes Paket eine eigene User-Definition.
Analysiere alle Varianten und erstelle eine einheitliche Definition
in libs/types, die alle Anwendungsfälle abdeckt."

Code-Splitting und Lazy Loading in Apps

Apps in einem Monorepo importieren häufig aus mehreren shared Libraries. Ohne Code-Splitting landet alles im Initial-Bundle. Mit dynamischen Imports lässt sich das verhindern:

// Statt statischem Import:
// import { DataTable } from '@my-org/ui-components';

// Dynamischer Import (React lazy):
const DataTable = React.lazy(() =>
  import('@my-org/ui-components').then(m => ({ default: m.DataTable }))
);

Barrel-Exports und Tree-Shaking: Wenn eine Library alles aus einer einzigen index.ts exportiert, kann der Bundler oft nicht effektiv Tree-Shaken. Besser ist es, granulare Exports in package.json unter dem exports-Feld zu definieren, damit Bundler direkt auf die Quelldateien zugreifen können.

6. CI/CD für Monorepos

CI/CD in einem Monorepo hat ein spezifisches Problem: Naiv konfiguriert baut und testet die Pipeline bei jedem Push alles — auch wenn nur eine Dokumentationsdatei in einem Paket geändert wurde. Mit Affected-Analyse und intelligentem Caching wird das beherrschbar.

GitHub Actions mit Nx Affected

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  affected:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Wichtig: vollständige History für affected

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'

      - uses: pnpm/action-setup@v4

      - run: pnpm install --frozen-lockfile

      - name: Nx Affected Build
        run: pnpm nx affected --target=build --base=origin/main --head=HEAD

      - name: Nx Affected Test
        run: pnpm nx affected --target=test --base=origin/main --head=HEAD

Das fetch-depth: 0 ist kritisch: Ohne vollständige Git-History kann nx affected den Vergleich mit dem Basis-Branch nicht durchführen und fällt auf "alles gebaut" zurück.

GitHub Actions Matrix für parallele Jobs

Für sehr große Monorepos: betroffene Projekte aufteilen und parallel in separaten Jobs testen:

jobs:
  get-affected:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.affected.outputs.matrix }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: pnpm/action-setup@v4
      - run: pnpm install --frozen-lockfile
      - id: affected
        run: |
          AFFECTED=$(pnpm nx show projects --affected --base=origin/main --head=HEAD --json)
          echo "matrix={\"project\":$AFFECTED}" >> $GITHUB_OUTPUT

  test:
    needs: get-affected
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJson(needs.get-affected.outputs.matrix) }}
      fail-fast: false
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: pnpm/action-setup@v4
      - run: pnpm install --frozen-lockfile
      - run: pnpm nx test ${{ matrix.project }}

Turborepo Remote Cache in GitHub Actions

name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
      TURBO_TEAM: ${{ vars.TURBO_TEAM }}

    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'

      - run: pnpm install --frozen-lockfile

      - name: Build
        run: pnpm turbo build

      - name: Test
        run: pnpm turbo test

Mit Remote Cache lädt Turborepo Artefakte, die ein Entwickler oder ein früherer CI-Lauf bereits erzeugt hat — und überspringt die entsprechenden Tasks vollständig.

Cache-Strategien: was invalidiert den Cache?

Claude Code kann dir helfen, zu verstehen warum dein Cache nicht funktioniert wie erwartet:

claude "Mein Turborepo-Cache wird in GitHub Actions nie getroffen,
obwohl ich Remote Cache konfiguriert habe. Ich vermute, dass
Umgebungsvariablen den Cache invalidieren. Wie kann ich das prüfen
und beheben?"

Häufige Ursachen für Cache-Misses, die Claude Code erkennt und erklärt:


Zwei verwandte Artikel die auf diesem Thema aufbauen:


Claude Code Mastery — von Monorepos bis zum produktiven Agenten

Monorepos sind nur ein Thema. Im Kurs lernst du Agents, MCP-Server, Hooks, Multi-Agent-Workflows und mehr — vollständig auf Deutsch, einmalig bezahlt.

Jetzt starten → Basis ab €29 · Pro ab €49 · Einmalzahlung · Kein Abo

Kurs · Claude Code Mastery

Monorepos, Agents, CI/CD — auf Deutsch meistern

Nx. Turborepo. Changesets. Shared Libraries. CI-Pipelines. Agents. MCP. Alles auf Deutsch, einmalig bezahlt — kein Abo, keine Plattformabhängigkeit.

Jetzt einsteigen → Kursübersicht ansehen →

Basis ab €29 · Pro ab €49 · Einmalzahlung · Kein Abo