Monorepos - Vor- und Nachteile - Feature-Bild

Monorepos – Vor- und Nachteile

Autor-Bild
von Sven Woltmann – 28. August 2020

Artikel über Monorepos gibt es zahlreiche, deshalb werde ich in diesem Artikel insbesondere auf die praktischen Aspekte eingehen: Performance und das Mergen von bestehenden Repositories.

Folgende Fragen wird dieser Artikel im Detail klären:

  • Was ist ein Monorepo?
  • Was sind die Vor- und Nachteile eines Monorepos?
  • Wie performant ist ein Git-Monorepo?
  • Wie kann man mehrere Git-Repositories in ein Monorepo zusammenführen (mergen) und dabei deren History beibehalten?

Was ist ein Monorepo?

Ein Monorepo (kurz für "monolithic repository") ist ein Repository, das den Quellcode mehrerer oder aller Projekte eines Teams oder eines Unternehmens enthält.

Einige der größten Internetunternehmen, wie Google, Facebook, Microsoft und Twitter, arbeiten mit Monorepos.

Warum kenne ich mich mit Monorepos aus?

Bei AndroidPIT haben wir im August 2016 ein neues Produkt, das heute aus 25 Microservices und 23 Libraries besteht, als Git-Monorepo aufgesetzt. Zu Beginn waren alle Entwickler zögerlich, doch unser Team entschied gemeinsam es auszuprobieren. Wir hielten uns die Option offen das Repository wieder aufzusplitten, falls wir unzufrieden sein sollten. Zwei Jahre später – nachdem wir überwiegend positive Erfahrung gemacht hatten – entschieden wir uns, alle anderen Produkte ebenfalls in Monorepos zusammenzuführen.

Vor- und Nachteile von Monorepos

Zwei lesenswerte, englischsprachige Artikel zu diesem Thema sind:

Im folgenden findest du die Vor- und Nachteile, wie ich sie im Programmiereralltag erfahren habe, bzw. meine Sicht zu den verbreiteten Kritikpunkten.

Was sind die Vorteile eines Monorepos?

Reduzierung von Aufwand / Kosten:

  • Weniger Repositories:
    Entwickler müssen nur ein (oder wenige) Repositories auschecken und aktualisieren. Neue Projekte werden in Unterverzeichnissen erstellt und somit automatisch an die anderen Entwickler verteilt, ohne dass diese weitere Repositories auschecken müssen.
  • Konsistenz des Gesamtprojekts:
    Entwickler haben zu jeder Zeit einen konsistenten Stand des Gesamtprojekts – nicht nur im main-Branch, sondern auch auf Branches und alten Commits. Bei vielen Einzelrepositories kommt es hingegen schnell zu Inkompatibilitäten.
  • Atomare Commits:
    Zusammengehörige Änderungen an mehreren Teilprojekten können gemeinsam eingecheckt werden.
  • Einfacher mergen:
    Das Anlegen und Mergen von Branches für Änderungen, die mehrere Teilprojekte umfassen, ist aufwändig, wenn die Projekte in separaten Repositories liegen. In einem Monorepo muss man nur einmal mergen.
  • Einfacherere Auflösung von Merge-Konflikten:
    Das Behandeln von Merge-Konflikten wird vereinfacht, da sich in einem Monorepo während der Behandlung des Konflikts die komplette Codebasis in einem konsistenten Zustand befindet. Bei mehreren Repositories muss man diese ggf. erst mühsam auf einen einheitlichen Zustand bringen.

Verbesserte Wartbarkeit:

  • Code verschieben:
    Code kann deutlich einfacher zwischen den Verzeichnissen eines Monorepos verschoben werden als von einem Repository in ein anderes. Somit können Entwickler die Modulgrenzen bei Änderungen von Anforderungen oder Refactorings problemlos anpassen.
  • Zentrale Dokumentation:
    Projektübergreifende Dokumentation kann im Root-Folder des Monorepos abgelegt werden.

Organisation:

  • Transparenz:
    Monorepos führen dazu, dass alle Entwickler Zugriff auf die gesamte Codebasis haben. In den meisten Fällen ist dies erwünscht, fördert die teamübergreifende Zusammenarbeit und erlaubt es Entwicklern an Code anderer Teams mitzuarbeiten.
  • Reduzierte Konfiguration:
    Monorepos reduzieren den Konfigurationsaufwand der Source Code Management Tools.

Was sind die Nachteile eines Monorepos?

Performance / Skalierbarkeit:

  • Performance-Probleme:
    Kritiker bemängeln, dass Monorepos zu Performance-Probleme führen. Ich konnte in Projekten der Größe, in der die meisten von uns sich bewegen, bisher keine Performance-Einbußen feststellen (s. Folgeabschnitt "Performance und Skalierbarkeit eines Git-Monorepos").
  • Erhöhter Speicherbedarf:
    Ebenso wird ein erhöhter Speicherbedarf kritisiert, da jeder Entwickler die gesamte Codebasis auschecken muss. Dies dürfte heutzutage in den meisten Projekten unerheblich sein: Der Quellcode des Linux-Kernels bspw. belegt etwa 3,5 GB. Die Repositories der meisten Unternehmen dürften kleiner sein.
  • Komplexere Builds:
    Monorepos machen vermeintlich die Build-Pipeline komplexer. Auch das entspricht nicht meiner Erfahrung. Mittels Sparse Checkouts und Polling Filtern kann man problemlos Teilprojekte bauen (s. Artikel "Jenkins-Konfiguration für Monorepos").
  • Komplizierte Versionierung:
    Angeblich wird die Versionierung komplizierter. Auch dies kann ich nicht nachvollziehen. Es ist sowohl möglich die komplette Codebasis zu versionieren (mit Tags wie "v1.0") als auch einzelne Teilprojekte (mit Tags wie "project-a-v1.0").

Architektur:

  • Schlechtere Modularisierung:
    Einige sind der Meinung, dass ein Monorepo einer sauberen und effektiven Modularisierung entgegen wirkt, da die Entwickler nicht gezwungen sind, den Code auf separate Repositories aufzuteilen. Ich sehe das als Vorteil, da Modulgrenzen durch Monorepos nicht in Stein gemeißelt werden (s. o. "Verbesserte Wartbarkeit").

Organisation:

  • Transparenz:
    Der unter den Vorteilen aufgeführte Aspekt, dass alle Entwickler Zugriff auf die gesamte Codebasis haben, ist nicht in allen Organisationen erwünscht. Beispielsweise sollten Projekte für unterschiedliche Kunden eines Auftragsentwicklers nicht in einem Monorepo abgelegt werden.
  • Open-Source-Libraries:
    Auch wenn Teilprojekte als Open Source-Komponenten freigegenen werden sollen, ist ein Monorepo hinderlich.

Performance und Skalierbarkeit eines Git-Monorepos

Ein weit verbreiteter Kritikpunkt an Monorepos ist deren schlechte Performance und Skalierbarkeit.

Aus meiner Erfahrung der letzten drei Jahre kann ich sagen, dass es in den meisten Teams keine Performance-Probleme geben sollte. In den meisten Unternehmen dürfte die gesamte Codebasis kleiner sein als der Linux-Kernel (17,9 Mio Codezeilen in 47.000 C/C++ Files laut cloc; und 5,7 Mio Deltas), welcher ebenfalls durch Git verwaltet wird (und für den Git ursprünglich entwickelt wurde).

Aktuell arbeite ich an einem Projekt bei IONOS. Das Monorepo des Java-Backends ist kleiner als der Linux-Kernel, liegt aber in einer vergleichbaren Größenordnung. Das Klonen des gesamten Repositories dauert weniger als eine Minute. Das Umschalten von Branches dauert je nach Alter des Branches zwischen 0,2 und 1,5 Sekunden.

Die meisten von uns arbeiten in Unternehmen dieser Größenordnungen; von daher sehe ich in naher Zukunft keine praktischen Probleme bei der Skalierbarkeit eines Monorepos.

Zusammenführen (Mergen) mehrerer Git-Repositories unter Beibehaltung der History

Nehmen wir an, wir haben zwei Projekte, "project-a" (beispielhaft angelegt unter https://github.com/SvenWoltmann/project-a) und "project-b" (https://github.com/SvenWoltmann/project-b), die wir zu einem Repository "sparse-checkout-demo" zusammenfassen wollen. Die zwei Projekte sollen dabei in Unterverzeichnissen mit den jeweiligen Projektnamen angeordnet werden.

Zunächst müssen wir beide Projekte auschecken (sofern diese nicht bereits vorhanden sind), das Verzeichnis für das Monorepo erstellen und dort ein Git-Repository initialisieren (alternativ eines via GitHub erstellen und klonen):

git clone [email protected]:SvenWoltmann/project-a.git git clone [email protected]:SvenWoltmann/project-b.git mkdir sparse-checkout-demo git init sparse-checkout-demo
Code-Sprache: Klartext (plaintext)

Hier die Ausgaben, die du sehen solltest:

Git-Repositories mergen – Schritt 1
Git-Repositories mergen – Schritt 1

Mit folgenden Kommandos mergen wir Projekt A in das Monorepo:

cd project-a git filter-branch -f --prune-empty --tag-name-filter cat --tree-filter ' mkdir -p project-a git ls-tree --name-only $GIT_COMMIT | xargs -I{} mv {} project-a ' cd ../sparse-checkout-demo git remote add project-a ../project-a git fetch project-a git merge --allow-unrelated-histories project-a/main git remote rm project-a
Code-Sprache: Klartext (plaintext)

Folgende Ausgabe solltest du sehen:

 Git-Repositories mergen – Schritt 2
Git-Repositories mergen – Schritt 2

Das gleiche wiederholen wir für Projekt B:

cd ../project-b git filter-branch -f --prune-empty --tag-name-filter cat --tree-filter ' mkdir -p project-b git ls-tree --name-only $GIT_COMMIT | xargs -I{} mv {} project-b ' cd ../sparse-checkout-demo git remote add project-b ../project-b git fetch project-b git merge --allow-unrelated-histories project-b/main git remote rm project-b
Code-Sprache: Klartext (plaintext)

Hier die entsprechenden Ausgaben dazu:

 Git-Repositories mergen – Schritt 3
Git-Repositories mergen – Schritt 3

Und das war es schon!

Mit dir und git log kannst du sehen, dass beide Teilprojekte und deren Commits im Monorepo enthalten sind:

 Git-Repositories mergen – Überprüfung
Git-Repositories mergen – Überprüfung

In diesem Beispiel ging das ziemlich schnell. Stell dich bei größeren Projekten darauf ein, dass der Prozess einige Stunden in Anspruch nehmen kann. Als wir bei AndroidPIT die Module, aus denen die Webseite besteht, zusammengefasst haben, hat dies etwa dreieinhalb Stunden gedauert.

Fazit

In diesem Artikel habe ich die Vor- und Nachteile von Git-Monorepos gegenübergestellt – meiner Meinung nach überwiegen die Vorteile deutlich, sodass Monorepos für die meisten Teams eine gute Wahl sind. Die häufig kritisierten Performance- und Skalierbarkeitsprobleme kommen bei den Teamgrößen der meisten Unternehmen nicht zum Tragen, und das einfachere Refactoring von Code über Modulgrenzen hinweg erhöht die Wartbarkeit des Codes.

Ich habe Schritt für Schritt gezeigt, wie existierende Repositories unter Beibehaltung ihrer History zusammengeführt werden können.

Wer nicht alle Projekte in einem einzigen Repository haben möchte, kann Monorepos auch auf diejenigen Projekte beschränken, die eng miteinander verwoben sind und bei denen gemeinsames Branchen und Mergen sinnvoll ist. Auch kannst du deine Projekte nach und nach in Monorepos überführen und – ebenso problemlos – Monorepos und Einzelprojekte nebeneinander laufen lassen.

Wenn dir der Artikel gefallen hat, teile ihn gerne über einen der Share-Buttons und hinterlasse mir einen Kommentar: Welche Erfahrungen hast du mit Monorepos gemacht? Teilst du meine Einschätzung der Vor- und Nachteile?

Möchtest du informiert werden, wenn neue Artikel auf HappyCoders.eu veröffentlicht werden? Dann klicke hier, um dich für den HappyCoders.eu-Newsletter anzumelden.