Monorepo vs. Polyrepo: die echten Trade-offs
Die Monorepo-Debatte handelt nicht von Tools — sie handelt davon, wie dein Team über Projektgrenzen hinweg kommuniziert, deployt und Code teilt.

Jede wachsende Engineering-Organisation steht irgendwann vor der Monorepo-Frage. Ein großes Repository für alles, oder viele kleine Repositories — eines pro Service oder Bibliothek? Die Antwort ist nicht universell, und beide Ansätze ohne Verständnis der Trade-offs zu übernehmen, führt zu Schmerzen.
Was jeder Ansatz wirklich bedeutet
Ein Monorepo speichert mehrere Projekte in einem einzigen Repository. Ein Polyrepo gibt jedem Projekt sein eigenes Repository. Die Unterscheidung ist relevant für Dependency-Management, CI-Pipeline-Design und teamübergreifende Zusammenarbeit.
# Monorepo structure
my-company/
├── apps/
│ ├── web/ # Next.js frontend
│ ├── api/ # Express backend
│ └── admin/ # Admin dashboard
├── packages/
│ ├── ui/ # Shared component library
│ ├── utils/ # Shared utilities
│ └── config/ # Shared configs (ESLint, TS)
├── package.json
└── turbo.json
# Polyrepo structure
my-company-web/ # Own repo, own CI, own deps
my-company-api/ # Own repo, own CI, own deps
my-company-admin/ # Own repo, own CI, own deps
my-company-ui-lib/ # Published to npm registry
my-company-utils/ # Published to npm registry
Wo Monorepos gewinnen
Atomare projektübergreifende Änderungen
Wenn eine gemeinsam genutzte Bibliothek ihre API ändert, erlaubt das Monorepo, alle Konsumenten in einem einzigen Commit zu aktualisieren.
// ❌ Polyrepo: changing a shared library's API requires coordinated releases
// 1. Update ui-lib, bump version, publish to npm
// 2. Update web app, install new version, fix breaking changes
// 3. Update admin app, install new version, fix breaking changes
// 4. Hope nothing breaks between steps 1 and 3
// ✅ Monorepo: one PR updates the library and all consumers
// packages/ui/src/Button.tsx — change the prop interface
// apps/web/src/pages/Home.tsx — update usage
// apps/admin/src/pages/Dashboard.tsx — update usage
// All in one commit, one CI run, one reviewGemeinsame Konfiguration
// Monorepo root package.json
{
"private": true,
"workspaces": ["apps/*", "packages/*"],
"devDependencies": {
"typescript": "^5.3.0",
"eslint": "^8.56.0",
"prettier": "^3.2.0"
}
}Eine Version von TypeScript, eine ESLint-Konfiguration, eine Prettier-Konfiguration. Kein Drift zwischen Projekten.
Code-Auffindbarkeit
In einem Monorepo funktioniert die Suche nach allen Verwendungen einer Funktion mit einem einzigen grep. In einem Polyrepo musst du über mehrere Repositories hinweg suchen — und weißt womöglich nicht, welche ein bestimmtes Paket überhaupt nutzen.
Wo Polyrepos gewinnen
Unabhängiges Deployment und Skalierung
# ❌ Monorepo CI — any change triggers checks for everything
# (unless you invest heavily in affected-project detection)
on:
push:
branches: [main]
jobs:
test-all:
runs-on: ubuntu-latest
steps:
- run: npm test --workspaces # Tests everything
# ✅ Polyrepo CI — only the changed service is tested and deployed
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: npm test # Tests only this service
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- run: npm run deploy # Deploys only this serviceTeam-Autonomie
Polyrepos geben Teams volle Kontrolle über ihre Technologiewahl, CI-Pipelines und Release-Rhythmen. Team A kann Vitest nutzen, während Team B Jest nutzt. Team C kann stündlich deployen, während Team D wöchentlich deployt.
Einfacheres Tooling
Monorepos brauchen spezialisierte Tools (Turborepo, Nx, Lerna, Bazel), um inkrementelle Builds, Task-Caching und die Erkennung betroffener Projekte zu handhaben. Polyrepos funktionieren von Haus aus mit Standard-Tooling.
Die Build-Tool-Steuer
Monorepos funktionieren nur gut mit ordentlicher Build-Orchestrierung. Ohne sie wachsen die CI-Zeiten linear mit der Größe der Codebasis.
// turbo.json — Turborepo task pipeline
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {},
"typecheck": {
"dependsOn": ["^build"]
}
}
}# Only build and test projects affected by the current changes
turbo run build test --filter=...[HEAD~1]Ohne Caching und Erkennung betroffener Projekte wird ein Monorepo mit 20 Paketen quälend langsam. Diese Investition ins Tooling ist der eigentliche Preis von Monorepos.
Entscheidungsrahmen
| Faktor | Monorepo | Polyrepo |
|---|---|---|
| Teamgröße | < 50 Engineers | > 50 oder mehrere autonome Teams |
| Gemeinsamer Code | Starkes Teilen zwischen Projekten | Wenig projektübergreifendes Teilen |
| Deployment-Rhythmus | Ähnlich über Projekte hinweg | Sehr unterschiedlich pro Team |
| Tech-Stack | Größtenteils homogen | Divers (unterschiedliche Sprachen) |
| Tooling-Investition | Bereit, Turborepo/Nx zu lernen | Will Standard-Git + CI |
Der hybride Ansatz ist ebenfalls gültig: ein Monorepo für eng verwandte Services, die Code teilen, mit separaten Repos für unabhängige Systeme.
Häufige Fehler
// ❌ Monorepo anti-pattern: circular dependencies
// packages/auth imports from packages/user
// packages/user imports from packages/auth
// Build order becomes impossible to resolve
// ✅ Extract shared types into a separate package
// packages/shared-types — no dependencies on other packages
// packages/auth — depends on shared-types
// packages/user — depends on shared-typesIn Polyrepos ist der entsprechende Fehler, Services eng über gemeinsamen Datenbankzugriff oder synchrone API-Ketten zu koppeln, die eigentlich ein einziger Service hätten sein können.
Die wichtigsten Punkte
- Monorepos ermöglichen atomare Änderungen über gemeinsam genutzte Bibliotheken und all ihre Konsumenten hinweg
- Polyrepos geben Teams Autonomie über Tooling, CI und Deployment-Rhythmus
- Monorepos erfordern Investitionen in Build-Tools — ohne Turborepo oder Nx werden CI-Zeiten unerträglich
- Polyrepos erfordern Registry-Infrastruktur, um Bibliotheken über Repositories hinweg zu teilen
- Wähle basierend auf Teamstruktur und Code-Sharing-Mustern, nicht nach Trends


