Wann ein Repository reicht, und wann nicht
Monorepo oder Polyrepo ist eine der folgenreichsten Architekturentscheidungen für Git-Projekte, weil sie Commit-Verhalten, Zugriffsrechte, CI/CD-Pipelines und die tägliche Entwicklerroutine über Jahre hinweg prägt. Dieser Artikel vergleicht beide Strategien anhand konkreter Kriterien wie Teamgröße, Kopplung und Tooling und zeigt, wie Magento-Agenturen Kundenprojekte und Custom-Module sinnvoll zwischen app/code und eigenen Composer-Paketen aufteilen.
Inhaltsverzeichnis
- 1. Monorepo vs. Polyrepo: zwei Grundmodelle der Repository-Organisation
- 2. Monorepo: atomare Commits und synchronisierte Abhängigkeiten
- 3. Monorepo: Repository-Wachstum, Performance und Tooling-Grenzen
- 4. Polyrepo: klare Ownership und unabhängige Releases
- 5. Polyrepo: koordinierte PRs, Dependency-Drift und CI-Duplizierung
- 6. Team-Größe und Kopplung als Entscheidungskriterium
- 7. Magento-Module: app/code-Monorepo oder eigene Composer-Pakete?
- 8. Git-Tooling für Monorepos: sparse-checkout und worktree
- 9. Monorepo vs. Polyrepo im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Monorepo vs. Polyrepo: zwei Grundmodelle der Repository-Organisation
Ein Monorepo ist ein einzelnes Git-Repository, das mehrere Projekte, Pakete oder Module gemeinsam enthält, oft mit eigener Ordnerstruktur pro Teilprojekt, aber einer einzigen, gemeinsamen Commit-Historie. Ein Polyrepo-Ansatz verteilt dieselben Projekte stattdessen auf viele einzelne Repositories, jedes mit eigener Historie, eigenen Branches und eigenem Zugriffsschutz. Beide Modelle lösen dasselbe Grundproblem, nämlich wie Code zwischen Teams und Diensten organisiert wird, aber mit fundamental unterschiedlichen Kompromissen bei Kopplung, Tooling und Governance.
Wichtig ist: Monorepo bedeutet nicht automatisch ein einziges deploybares Artefakt. Google, Meta und viele andere Konzerne betreiben Monorepos mit tausenden unabhängig baubaren und deploybaren Paketen in einem Repository. Umgekehrt bedeutet Polyrepo nicht automatisch saubere Entkopplung, denn stark verzahnte Services in getrennten Repos können trotzdem eng gekoppelt bleiben, nur eben ohne dass Git diese Kopplung sichtbar macht. Die Entscheidung ist also weniger eine Frage von richtig oder falsch, sondern eine Frage, welche Kompromisse zur eigenen Teamstruktur und Projektlandschaft passen.
2. Monorepo: atomare Commits und synchronisierte Abhängigkeiten
Der größte praktische Vorteil eines Monorepos ist der atomare Commit über Projektgrenzen hinweg: Ändert sich die Signatur einer gemeinsam genutzten Bibliothek, lassen sich die Bibliothek selbst und alle ihre Konsumenten in einem einzigen Commit anpassen und gemeinsam reviewen. Es gibt keinen Zwischenzustand, in dem ein Consumer-Projekt gegen eine bereits veraltete Interface-Version kompiliert, weil Bibliothek und Aufrufer nie getrennt versioniert werden müssen. Das reduziert Breaking-Change-Koordination auf ein einziges Pull-Request-Review statt auf eine Kette aus abgestimmten Releases über mehrere Repositories.
Daraus folgt der zweite große Vorteil: Abhängigkeiten sind per Definition immer synchron. Es gibt kein Szenario, in dem Projekt A noch auf Version 1.2 einer internen Library hängt, während Projekt B bereits auf Version 2.0 migriert ist. Jeder Checkout des Monorepos liefert automatisch konsistente, kompatible Versionsstände aller enthaltenen Pakete, was ganze Klassen von Integrationsfehlern und Versionskonflikten von vornherein ausschließt, ohne dass ein separates internes Package-Registry-Management nötig wird.
3. Monorepo: Repository-Wachstum, Performance und Tooling-Grenzen
Die Kehrseite zeigt sich mit wachsender Größe. Ein Repository, das über Jahre alle Projekte, Assets und deren komplette Historie ansammelt, wird zwangsläufig groß, und git clone, git status sowie git log werden spürbar langsamer, weil Git standardmäßig die gesamte Historie und den gesamten Arbeitsbaum lädt. Ab einer gewissen Größe reicht ein gewöhnlicher git clone nicht mehr aus, sondern erfordert gezielt --depth, --filter=blob:none oder Sparse-Checkout-Konfiguration, um praktikabel zu bleiben.
Ein weiteres Problem ist granulare Zugriffskontrolle: Git kennt von Haus aus keine Berechtigungen auf Ordnerebene innerhalb eines Repositories. Wenn ein externes Freelancer-Team nur an einem Teilprojekt arbeiten soll, aber keinen Zugriff auf den Rest der Codebasis haben darf, lässt sich das im reinen Monorepo nur über Zusatztools wie GitHub CODEOWNERS in Kombination mit serverseitigen Branch-Protection-Regeln annähern, nie aber sauber auf Dateisystemebene erzwingen. Auch CI-Pipelines müssen aktiv auf geänderte Pfade eingeschränkt werden, sonst baut jede Änderung reflexhaft das gesamte Repository neu.
4. Polyrepo: klare Ownership und unabhängige Releases
Ein Polyrepo-Ansatz macht Ownership technisch erzwingbar statt nur organisatorisch vereinbart: Jedes Repository hat eigene Zugriffsrechte, eigene Branch-Protection-Regeln und eigene CODEOWNERS, sodass ein Team oder ein externer Dienstleister exakt und ausschließlich Zugriff auf sein eigenes Projekt bekommt. Diese Eins-zu-eins-Beziehung zwischen Repository und Verantwortungsbereich vereinfacht Audits, Offboarding und die Vergabe von Zugriffsrechten an externe Vertragspartner erheblich, weil ein einzelner Zugriffs-Widerruf nie versehentlich andere Projekte mit betrifft.
Da jedes Repository nur seinen eigenen Code enthält, bleiben Clone- und Checkout-Zeiten dauerhaft klein, unabhängig davon, wie viele andere Projekte im Unternehmen existieren. Zusätzlich erlaubt Polyrepo echte unabhängige Versionierung und Release-Zyklen: Ein Service kann täglich deployen, ein anderer quartalsweise, ohne dass ein gemeinsamer Tag oder Branch-Zustand im selben Repository beide künstlich synchronisiert. Für lose gekoppelte Microservices oder komplett getrennte Kundenprojekte ist das oft der natürlichere Zuschnitt.
5. Polyrepo: koordinierte PRs, Dependency-Drift und CI-Duplizierung
Der Preis für diese Trennung zeigt sich bei projektübergreifenden Änderungen. Eine Refaktorierung, die eine gemeinsam genutzte Bibliothek und fünf abhängige Services betrifft, erfordert im Polyrepo-Modell fünf plus eins koordinierte Pull-Requests, jeden mit eigenem Review, eigener CI-Pipeline und eigenem Merge-Zeitpunkt. Ohne strikte Reihenfolge und ohne Feature-Flags entstehen dabei zwangsläufig Zwischenzustände, in denen manche Services bereits die neue Bibliotheksversion nutzen und andere noch nicht, was Testabdeckung und Debugging erschwert.
Daraus resultiert Dependency-Drift: Ohne aktives Versionsmanagement driften Projekte über Zeit auseinander, weil niemand zentral erzwingt, dass alle Consumer zeitnah auf die neueste interne Library-Version aktualisieren. Zusätzlich muss CI/CD-Konfiguration, etwa Linting-Regeln, Test-Runner-Setup oder Deployment-Skripte, in jedem Repository separat gepflegt werden, was bei zehn oder zwanzig Repositories schnell zu inkonsistenten, auseinanderdriftenden Pipelines führt, wenn nicht zentral über wiederverwendbare CI-Templates oder eine gemeinsame Actions-Bibliothek gegengesteuert wird.
6. Team-Größe und Kopplung als Entscheidungskriterium
Die praxistaugliche Entscheidungsregel orientiert sich an zwei Faktoren: Teamgröße und tatsächliche Kopplung zwischen den Projekten, nicht an Ideologie. Ein kleines Team, das ein einziges, eng gekoppeltes Magento-2-Projekt inklusive mehrerer Custom-Module betreut, profitiert fast immer von einem Monorepo oder zumindest von einer sehr kleinen Anzahl an Repositories, weil der Koordinationsaufwand mehrerer Repos den Nutzen der Trennung bei dieser Größe klar übersteigt. Die Kernkompetenz eines kleinen Teams liegt darin, schnell über Modulgrenzen hinweg zu arbeiten, nicht darin, Zugriffsrechte fein zu granulieren.
Umgekehrt gilt: Mehrere unabhängige Kundenprojekte, die unterschiedliche Deployment-Zyklen, unterschiedliche Vertragspartner und keinerlei gemeinsamen Code haben, gehören in getrennte Repositories, selbst bei einem kleinen Team. Lose gekoppelte Services mit eigenen Release-Rhythmen folgen derselben Logik. Ein einfacher Test hilft bei der Einordnung: Wird ein Commit typischerweise mehrere Projekte gleichzeitig betreffen? Dann spricht das für Monorepo. Betrifft ein Commit fast immer nur ein einziges, isoliertes Projekt? Dann spricht das für Polyrepo.
7. Magento-Module: app/code-Monorepo oder eigene Composer-Pakete?
Für eine Magento-Agentur stellt sich diese Frage sehr konkret: Sollen Custom-Module für ein Kundenprojekt direkt im app/code-Verzeichnis des Haupt-Magento-Repositories liegen, also monorepo-artig, oder als eigenständige Composer-Pakete in separaten Repositories und per composer.json eingebunden werden, also polyrepo-artig? Für Module, die untrennbar mit genau einem Kundenprojekt verwoben sind, etwa ein individuelles Checkout-Feature, ist app/code im Haupt-Repository fast immer die richtige Wahl: keine Versionsverwaltung nötig, keine Composer-Repository-Konfiguration, sofortige Sichtbarkeit im Code-Review des Gesamtprojekts.
Module, die über mehrere Kundenprojekte hinweg wiederverwendet werden, etwa eine generische SEO-Erweiterung oder ein Cookie-Consent-Modul, gehören dagegen in ein eigenes Composer-Paket mit eigenem Repository und Semantic Versioning. Nur so lässt sich ein Bugfix gezielt für alle Kundenprojekte ausrollen, ohne bei jedem Kunden manuell Dateien zu kopieren, und nur so bleibt nachvollziehbar, welche Version eines wiederverwendbaren Moduls in welchem Kundenprojekt aktiv ist.
# Example monorepo layout for a Magento agency: shared core in vendor,
# client-specific customizations in app/code, one Git history for all
project-monorepo/
├── app/
│ └── code/
│ └── Mironsoft/
│ ├── ClientCheckout/ # tightly coupled to this one client
│ └── ClientPricing/ # tightly coupled to this one client
├── vendor/
│ └── mironsoft/
│ └── seo-suite/ # reusable, installed via Composer
├── composer.json
└── .git/ # single shared history
{
"name": "mironsoft/seo-suite",
"description": "Reusable SEO module, versioned and released independently of any single client project",
"type": "magento2-module",
"require": {
"php": "^8.4",
"magento/framework": "^103.0"
},
"autoload": {
"psr-4": {
"Mironsoft\\SeoSuite\\": "src/"
}
},
"extra": {
"magento": {
"module": "Mironsoft_SeoSuite"
}
}
}
8. Git-Tooling für Monorepos: sparse-checkout und worktree
git sparse-checkout löst das Problem, dass ein Entwickler in einem großen Monorepo selten alle Ordner gleichzeitig braucht. Statt den kompletten Arbeitsbaum auszuchecken, definiert Sparse-Checkout genau die Pfade, die im lokalen Working Directory materialisiert werden, während die restliche Historie im Objektspeicher bleibt, aber nicht auf die Festplatte ausgecheckt wird. In Kombination mit git clone --filter=blob:none (partial clone) lässt sich zusätzlich verhindern, dass Blob-Inhalte nicht benötigter Pfade überhaupt erst heruntergeladen werden, was Clone-Zeiten bei sehr großen Repositories drastisch reduziert.
git worktree löst ein anderes, aber verwandtes Problem: Innerhalb eines Monorepos gleichzeitig an mehreren Branches arbeiten, etwa einen Hotfix für Kundenprojekt A parallel zu einem Feature für Kundenprojekt B, ohne ständig git stash und git checkout zu wechseln. Jeder Worktree ist ein eigenständiges Arbeitsverzeichnis mit eigenem ausgechecktem Branch, das sich dieselbe .git-Objektdatenbank teilt, wodurch kein zweiter vollständiger Clone nötig ist und Speicherplatz gespart wird.
# git sparse-checkout: only materialize the paths this developer needs
$ git clone --filter=blob:none --sparse git@github.com:mironsoft/monorepo.git
$ cd monorepo
$ git sparse-checkout set app/code/Mironsoft/ClientCheckout vendor/mironsoft/seo-suite
# Working directory now only contains the two paths above,
# full history stays available in .git without a full checkout
$ git sparse-checkout list
app/code/Mironsoft/ClientCheckout
vendor/mironsoft/seo-suite
# git worktree: work on a hotfix and a feature branch at the same time,
# without stashing, sharing one .git object database
$ git worktree add ../monorepo-hotfix hotfix/client-a-checkout-bug
$ git worktree add ../monorepo-feature feature/client-b-pricing-rules
$ git worktree list
/home/dev/monorepo a1b2c3d [main]
/home/dev/monorepo-hotfix e4f5g6h [hotfix/client-a-checkout-bug]
/home/dev/monorepo-feature i7j8k9l [feature/client-b-pricing-rules]
9. Monorepo vs. Polyrepo im direkten Vergleich
Im Monorepo läuft jede CI-Pipeline standardmäßig Gefahr, bei jedem Commit das gesamte Repository zu bauen und zu testen, selbst wenn sich nur eine einzelne Zeile in einem isolierten Modul geändert hat. Moderne CI-Systeme lösen das über Path-Filtering: Der Pipeline-Trigger prüft anhand von git diff gegen den Ziel-Branch, welche Pfade betroffen sind, und startet ausschließlich die Jobs, deren zugeordnete Pfade sich tatsächlich geändert haben. Damit bleibt die CI-Laufzeit auch in einem großen Monorepo proportional zur Änderung, nicht zur Gesamtgröße des Repositories.
# GitHub Actions: scope a job to changed paths only, monorepo-style
name: ci
on: [pull_request]
jobs:
changes:
runs-on: ubuntu-latest
outputs:
checkout: ${{ steps.filter.outputs.checkout }}
seo-suite: ${{ steps.filter.outputs.seo-suite }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
checkout:
- 'app/code/Mironsoft/ClientCheckout/**'
seo-suite:
- 'vendor/mironsoft/seo-suite/**'
test-checkout-module:
needs: changes
if: needs.changes.outputs.checkout == 'true'
runs-on: ubuntu-latest
steps:
- run: bin/phpunit app/code/Mironsoft/ClientCheckout
Die folgende Tabelle stellt beide Strategien entlang der Dimensionen gegenüber, die für die tägliche Entwicklerarbeit am stärksten spürbar sind.
| Dimension | Monorepo | Polyrepo | Empfehlung |
|---|---|---|---|
| Cross-Projekt-Refactoring | Ein atomarer Commit | Mehrere koordinierte PRs | Monorepo bei enger Kopplung |
| CI-Geschwindigkeit ohne Path-Filtering | Baut oft alles neu | Automatisch nur eigenes Repo | Path-Filtering im Monorepo Pflicht |
| Zugriffskontrolle | Nur über Zusatztools annäherbar | Nativ pro Repository | Polyrepo bei externen Teams |
| Onboarding neuer Entwickler | Ein Clone, ein Kontext | Viele Repos, viele Setups | Monorepo bei kleinen Teams |
| Abhängigkeits-Konsistenz | Immer synchron | Dependency-Drift möglich | Monorepo bei geteilten Libraries |
Mironsoft
Repository-Strategie, Git-Workflows und CI/CD-Pipelines für PHP- und Magento-Teams
Monorepo oder Polyrepo für euer Projekt entscheiden?
Wir analysieren eure Projektlandschaft und Teamstruktur und helfen bei der Entscheidung zwischen Monorepo und Polyrepo, inklusive sparse-checkout-Setup, CI-Path-Filtering und sauberer Composer-Paketierung wiederverwendbarer Magento-Module.
Repository-Audit
Bestehende Repo-Struktur analysieren und auf Kopplung und Teamgröße abstimmen
Git-Tooling-Setup
sparse-checkout, worktree und Composer-Paketierung für Magento-Module einrichten
CI/CD-Path-Filtering
Pipelines auf geänderte Pfade beschränken, statt jedes Mal alles neu zu bauen
10. Zusammenfassung
Ein Monorepo bündelt mehrere Projekte in einem einzigen Repository mit gemeinsamer Historie und ermöglicht dadurch atomare Cross-Projekt-Commits und dauerhaft synchronisierte Abhängigkeiten, verlangt aber gezieltes Tooling wie sparse-checkout, partial clone und CI-Path-Filtering, sobald die Größe wächst. Ein Polyrepo-Ansatz erzwingt klare Ownership-Grenzen und erlaubt unabhängige Release-Zyklen pro Projekt, erkauft das aber mit koordinierten Pull-Requests über mehrere Repositories bei projektübergreifenden Änderungen und dem Risiko von Dependency-Drift.
Für eine Magento-Agentur bedeutet das konkret: Kundenspezifische Custom-Module gehören in der Regel monorepo-artig in app/code des Hauptprojekts, wiederverwendbare Module über mehrere Kunden hinweg gehören als eigenständige Composer-Pakete in separate Repositories. Die Entscheidung folgt nicht einem starren Dogma, sondern der tatsächlichen Kopplung der Projekte und der Größe des Teams, das sie pflegt.
Monorepo vs. Polyrepo: Die wichtigsten Punkte auf einen Blick
Monorepo
Atomare Commits über Projekte hinweg, immer synchrone Abhängigkeiten, aber wachsende Repo-Größe und Tooling-Bedarf.
Polyrepo
Klare Ownership pro Repository, unabhängige Releases, aber koordinierte PRs und Risiko von Dependency-Drift.
Entscheidungskriterium
Teamgröße und tatsächliche Kopplung der Projekte, nicht Ideologie oder Trend.
Magento-Praxis
Kundenspezifisch in app/code, wiederverwendbar als eigenes Composer-Paket.