Strukturieren von Code-Repositorys: Multi, Mono oder Organic

Foto von Joren auf Unsplash

Die neueste Debatte in der Stadt ist, ob Sie Ihre Dienste in einem einzigen Repository oder in mehreren kleinen Repositorys aufbewahren sollten.

Bei mehreren kleinen Repositorys wird der Code für jeden Mikrodienst Ihrer App in einem eigenen Repository gespeichert. Mit einem Mono-Repo speichern Sie den gesamten Code in einem einzigen Repository und stellen den Code als Microservices bereit.

Also welche solltest du verwenden? Eine zu strenge Herangehensweise - ohne den Zweck und die Verwendung der einzelnen Herangehensweisen zu berücksichtigen - kann auf lange Sicht zu negativen Ergebnissen führen. Wenn Sie wissen, wann Sie welche verwenden müssen, kann dies Ihre Produktivität steigern und Ihr Projekt verbessern.

Um die Regeln zu biegen, müssen wir zuerst verstehen, warum sie existieren.

Es wird häufig empfohlen, für jede App / jeden Service ein unabhängiges Repository zu haben. Aber wieso? Da wir für jeden Mikrodienst ein Repository haben, erhalten wir:

  • Freiheit, Code anders und unabhängig von allen anderen Diensten zu schreiben.
  • Geschwindigkeit beim Vornehmen von Codeänderungen beim Beheben von Fehlern, beim Aktualisieren, Testen und Bereitstellen. Da Änderungen nur in einem einzigen Repository getestet werden müssen, ist die Bereitstellung des Codes schneller und zuverlässiger.
  • Trennung des Codes als unabhängige Einheiten, wodurch Fehlerlecks und Leistungsengpässe zwischen Diensten vermieden werden.
  • Klares Eigentum an jedem Repository und Service, was besonders für große Teams hilfreich ist.

Aber warum entstand der Bedarf an Mono-Repos?

Der Multi-Repo-Ansatz hat natürlich seine Vorteile. Es bringt aber auch seine eigenen Herausforderungen mit sich, insbesondere bei Projekten mit einer großen Anzahl von Microservices, die dieselben Frameworks, dieselbe Sprache, dieselben technischen Stacks usw. verwenden.

Einige dieser Herausforderungen sind:

  • Durchsetzung von Standards und Best Practices in allen Repositorys. Bei einem Multi-Repo müssen Änderungen an Codestandards und Best Practices in mehreren Repositorys repliziert werden. Mit einem Mono-Repo können alle Änderungen an einem Ort vorgenommen werden.
  • Der Aufwand für die Verwaltung gemeinsamer oder gemeinsamer Komponenten. Bei Sicherheitspatches, Versions-Upgrades und Fehlerkorrekturen muss sichergestellt werden, dass diese Änderungen in allen Repositorys vorgenommen werden und überall reibungslos funktionieren. (Nebenbei bemerkt, der sich wiederholende Code in jedem Dienst vergrößert sich ebenfalls.) In einem Mono-Repo können wir Aktualisierungen an einem Ort durchführen, was sowohl Zeit als auch Kopfschmerzen spart.
  • End-to-End-Tests in Verbindung mit eng verwandten oder abhängigen Diensten direkt vom Entwicklercomputer aus. Indem wir den gesamten Code an einem Ort haben, vereinfachen wir den Prozess des Startens aller zugehörigen Dienste und der Ausführung von End-to-End-Tests.
  • On-Premise-Bereitstellung von Code für andere Unternehmen. Durch die Bereitstellung eines Mono-Repos als Microservices sparen wir Zeit und reduzieren den redundanten Aufwand für das Bootstrapping der einzelnen Repositories.

Beide Ansätze haben natürlich Vor- und Nachteile, und jeder Ansatz hat unter verschiedenen Umständen seine eigenen Vorteile.

Aus diesem Grund haben wir den Ansatz gewählt, flexibel zu bleiben und sowohl Multi-Repos als auch Mono-Repos zu verwenden, jedoch erst, nachdem wir vollständig verstanden haben, warum wir uns für die jeweilige Dienstleistung entschieden haben. Dies hat dazu geführt, dass wir mehrere Repos mit mehreren Mikrodienstleistungen haben, die auf folgende Weise getrennt sind:

  • Wartung und Updates einfach und schnell.
  • Suchen Sie den Code zum Debuggen oder Ändern viel strukturierter.
  • Neue Teamkollegen leichter einbinden.

Wie wir entscheiden, welchen Repository-Typ wir verwenden

Die folgenden Überlegungen haben uns bei der Entscheidung geholfen, wann wir Mono-Repos im Vergleich zu Multi-Repos einsetzen.

1. Denken Sie an den Code, der als Grundlage für den Service dienen soll.

Beginnen Sie, indem Sie Gemeinsamkeiten bei Code, Wartung und Updates feststellen. Wenn mehrere Repositorys denselben Code haben, ist es besser, sie in einem einzigen Repository zusammenzufassen.

Die Freiheit, Code in einem Dienst unterschiedlich und unabhängig zu schreiben, ist einer der Vorteile mehrerer kleiner Repositorys. Oftmals haben Services jedoch viele identische Scaffolds, wenn sie dieselbe Sprache, dasselbe Framework, dieselbe Protokollierung, dieselben Bootstrap-Skripte, dieselbe Middleware usw. verwenden. Die Wiederverwendung dieser freigegebenen Scaffolds spart Zeit.

Zum Beispiel verfügt Collect - unser primäres Datenerfassungstool - über mehrere Mikrodienste, die auf einem identischen Framework basieren. Diese Dienste basieren auf Node.js, Express und Parse Server. Sie teilen sich viele Bibliotheken wie Winston, Mongoose und andere Integrationen von Drittanbietern. Wenn früher jeder dieser Dienste ein eigenes Repository hatte, bedeutete das Aktualisieren oder Beheben eines Fehlers in einem dieser gemeinsam genutzten Module, dass jedes Repository separat aktualisiert und getestet wurde. Das war langsam und umständlich.

Als wir sie jedoch in einem Mono-Repo zusammengelegt haben, wurde das Testen und Aktualisieren der gemeinsam genutzten Module einfacher und schneller. Das Anwenden von Sicherheitspatches und das Durchsetzen von Standards wurde einfacher, da Entwickler alle Änderungen an einem Ort vornehmen können.

Das potenzielle Risiko eines Mono-Repos besteht darin, dass ein Entwickler Code wiederverwenden kann, der ursprünglich für ein nicht verwandtes Modul geschrieben wurde. Wenn sich die beiden Module den Code teilen, kann eine Änderung dieses gemeinsamen Codes zu Fehlern führen. Wenn diese Fehler nicht überprüft werden, können sie sich auf die CI / CD-Pipelines (Continuous Integration and Delivery) von nicht verwandten Mikrodiensten auswirken. Um solche Probleme zu vermeiden, ist es wichtig, eine starke Testsuite zu haben.

2. Prüfen Sie, ob Sie Module haben, die sich stark von den anderen unterscheiden.

Entwickeln Sie ein Modul, das eine ganz andere Technologie, Sprache, Framework oder Persistenz erfordert? Dann ist es besser, es in ein separates Repository aufzuteilen.

In Collect gibt es Dienste, die die Verarbeitung von Ereignissen in großen Mengen verwalten. Sie verwalten Warteschlangen, führen benutzerdefinierte Skripte aus und verfügen über einen völlig anderen Mechanismus zur Fehlerbehandlung. Diese Dienste sind in Python geschrieben und müssen häufig CPU-intensive Aufgaben ausführen.

Als wir uns überlegten, den Code für Collect neu zu strukturieren, war es für uns selbstverständlich, diese Services in einem separaten Repo zu belassen. Diese Services unterschieden sich stark vom oben beschriebenen Haupt-Repository von Collect. Während das Hauptrepository für benutzerbezogene Anfragen gedacht war, drehte sich in diesem Repository alles um Hintergrundaufgaben und -ausführungen. Auch das Änderungsmanagement in diesen Diensten sollte anders und vom Haupt-Repository isoliert sein.

Das Nachdenken über die Codewartung und die Entwicklung im Laufe der Zeit veranlasste uns, diese Services in einem separaten Repository zusammenzufassen. Auf diese Weise konnten wir ein völlig anderes Change-Management-System aufbauen, das sich als sehr hilfreich und produktiver herausstellte.

3. Berücksichtigen Sie die Unsicherheit und damit die Häufigkeit von Änderungen, die ein Dienst durchlaufen könnte.

Wenn Sie anfangen, an etwas zu arbeiten, das sehr ungewiss ist (entweder in Bezug auf den Umfang des Problems oder die Implementierung selbst), kann Ihnen ein anderes Repository die Geschwindigkeit und Freiheit geben, die Sie zum Testen der Dinge benötigen.

Angenommen, Sie möchten eine neue Art der Bildverarbeitung testen, um Objekte zu identifizieren. Sie möchten sich mit maschinellem Lernen beschäftigen, sind sich jedoch noch nicht sicher, wie es sich entwickeln wird oder ob sich die Problemstellung dramatisch ändern wird. In diesem Fall wäre es eindeutig besser, ein separates Repository zu haben und dann zu einem Punkt der Gewissheit zu gelangen. Wenn Sie hingegen der Meinung sind, dass die API Stabilität erreicht hat und für einen großen Zeitraum unverändert bleibt, können Sie einen Anruf entgegennehmen, um sie mit einem Ihrer Hauptrepositorys zusammenzuführen.

Das Blog wurde ursprünglich auf blog.socialcops.com veröffentlicht. Das Obige ist, wie wir mit unseren Repository-Entscheidungen umgehen. Ich hoffe, es kann Ihnen dabei helfen, nach ersten Grundsätzen zu denken, wenn Sie sich diesem Problem nähern. Abonnieren Sie unseren Newsletter, um weitere Updates vom SocialCops Engineering and Data Science Team zu erhalten.