Monorepositories für Webanwendungen: Vor- und Nachteile

Montag, 19.6.2023

Titelbild Monorepositories

Traditionell wird ein Projekt in einem Repository abgelegt. Je nach Anwendungsfall kann es Sinn machen, verwandte Projekte, etwa Backend und Frontend, in einem Repository abzulegen. Dieses Vorgehen wird als Monorepository bezeichnet.  

Ein Monorepository ist ein Quellcode-Repository, in dem mehrere Projekte gleichzeitig versioniert werden. Diese Projekte müssen untereinander keine offensichtliche Beziehung haben, wie etwa, dass sie für den Endkunden ein Produkt bilden. Sie können sogar völlig unabhängig voneinander sein und von unterschiedlichen Teams entwickelt werden.

In diesem Artikel erkläre ich einige der Vor- und Nachteile beim Einsatz von Monorepositories. Der Artikel geht auf Git-Repositories ein und an einigen Stellen gebe ich Beispiele zur Nutzung mit npm, diese dienen aber nur zur Illustration. Monorepositories können auch mit anderen Technologien wie etwa Java verwendet werden.

Vorteile eines Monorepositories

Welche Vorteile bietet nun ein Monorepository gegenüber dem klassischen Multi-Repository-Ansatz?

Atomare Commits

Änderungen an geteilten Bibliotheken, die auch Änderungen in konsumierenden Projekten nach sich ziehen, können in einem einzelnen Commit durchgeführt werden. In der Regel handelt es sich hierbei um einen Merge Commit. Dadurch, dass Anpassungen durch Breaking Changes in allen betroffenen Projekten direkt vorgenommen werden, ist der Entwicklungsbranch immer buildfähig.

Einfache Refactorings und Codeanalysen

Da alle Projekte, die eine API bereitstellen oder nutzen, in einem Repository liegen, kann in der Entwicklungsumgebung nachvollzogen werden wo und wie häufig eine Methode, Funktion, Klasse oder Ähnliches verwendet wird. Weiterhin können Werkzeuge genutzt werden, um Refactorings schnell und einfach an allen betroffenen Stellen umzusetzen.

Wiederverwendung von Code

Teilt man Source Code auf mehrere Repositories auf, so muss man, wenn Komponenten geteilt werden sollen, diese als Abhängigkeit über einen Paketmanager wie npm verfügbar machen. Hierfür muss ein Paket publiziert, versioniert und eingebunden werden. In einem Monorepository kann man beim Teilen von Komponenten auf die Auslagerung in ein Paket verzichten, da die Komponenten bereits im Repository vorliegen. Sie können also direkt referenziert werden.

Vereinfachtes Dependency Management

Abhängigkeiten lassen sich in einem Monorepository einfach definieren. Gemeinsam genutzte Abhängigkeiten können von verschiedenen Projekten geteilt werden.

In Node.JS können npm-Pakete gehoistet werden, d. h. sie werden in den darüberliegenden Ordner installiert, indem man dort eine package.json anlegt. Diese Abhängigkeiten können dann in allen darunterliegenden Ordnern verwendet werden.

Die gemeinsame Nutzung von Abhängigkeiten hat Vorteile für die Wartung: Es gibt nur eine Version einer Abhängigkeit, was zu geringerem Festplattenspeicherbedarf führt und damit (meistens auch) die Laufzeit von npm install verkürzt.

Welche Abhängigkeiten gehoistet werden können, kommt darauf an: Entwicklungsabhängigkeiten können gehoistet werden, Production-Dependencies sollten genauer betrachtet werden. So sollte z. B. eine Abhängigkeit bei einer Library, die über ein NPM-Repository ausgeliefert wird, kein Hoisting erfolgen. Sonst fehlt die Abhängigkeit bei der Installation der Library außerhalb des Repositories.

Nachteile eines Monorepositories

Natürlich hat der Einsatz eines Monorepositories nicht nur Vorteile. Auf die größten Nachteile gehe ich im Folgenden ein.

Versionierung

Man kann seine Projekte einzeln versionieren oder aber man hat eine gemeinsame Version für alle Projekte. Beide Ansätze haben Vor- und Nachteile, die weit über den Umfang dieses Artikels hinausgehen.

Eine zentrale Frage hierbei ist, wie signifikant die Versionsnummer ist: wird sie etwa für Bibliotheken nach außen kommuniziert? In diesem Fall ist eine eigene Versionsnummer für Projekte sinnvoll. Man stelle sich hier eine gemeinsame Versionsnummer vor eine neue Version des Repositories bedeutet nicht zwangsläufig auch, dass es Änderungen an den Bibliotheken in diesem Repository gab.

Die nächste Frage ist, wann man eine neue Versionsnummer nutzt: Bei jedem Merge in den Entwicklungsbranch? Bei jedem Merge in den Masterbranch? Zu jedem Releasetermin?

Berechtigungen

Wenn mehrere Teams im gleichen Repository arbeiten, wird häufig die Frage nach Berechtigungen laut. Kann jeder alles ändern oder gibt es Einschränkungen? Oft werden Monorepositories in den Köpfen der Verantwortlichen mit dem Wilden Westen verglichen, aber Berechtigungen können in einigen Git-Servern in der CODEOWNERS-Datei hinterlegt werden.

Performance und Skalierung

In einem Monorepository checkt man mit einem Repository alle Projekte aus, auch wenn man vielleicht gar nicht an allen Projekten interessiert ist. Dies kann je nach Größe des Repositories einen deutlichen Mehrbedarf an Speicherplatz nach sich ziehen und im schlimmsten Fall zu langsamen Git-Operationen führen. Es gibt einige Möglichkeiten, um diese Probleme zu umgehen, wie etwa ein Partial-Clone, bei dem nur der aktuelle Commit geklont wird. Man sollte sich jedoch im Klaren darüber sein, dass bei sehr großen Repositories die Performance ein Problem werden kann.

Fazit

Der Einsatz eines Monorepositories will wohlüberlegt sein und eignet sich definitiv nicht für jedes Setup. 

 


Mehr zum Thema Monorepositories

Wer sich für den Einsatz von Monorepositories entschieden hat, der steht schnell vor der Frage nach dem richtigen Tooling. Auf dieses Thema gehen wir in unserem zweiten Blogbeitrag zu diesem Thema ein: Monorepository-Tooling für Node.js-Projekte.

 


zurück zur Blogübersicht

Diese Beiträge könnten Sie ebenfalls interessieren

Keinen Beitrag verpassen – viadee Blog abonnieren

Jetzt Blog abonnieren!

Kommentare

Christian Siebmanns

Christian Siebmanns

Christian Siebmanns ist Berater bei der viadee IT-Unternehmensberatung. Schwerpunkt seiner Arbeit ist der Einsatz in verschiedenen Kundenprojekten im Webumfeld. Christian ist Experte für Java und TypeScript.