Hallo allerseits!
Also ein neuer Teil des versprochenen Holivars über Monorepositories. Im ersten Teil diskutierten wir die Übersetzung eines Artikels eines angesehenen Ingenieurs aus Lyft (und zuvor Twitter) über die Nachteile von Mono-Repositories und warum sie fast alle Vorteile dieses Ansatzes ausgleichen. Persönlich stimme ich den Argumenten des Originalartikels weitgehend zu. Um dieser Diskussion ein Ende zu setzen, möchte ich, wie versprochen, noch einige Punkte ansprechen, die meiner Meinung nach noch wichtiger und praktischer sind.
Ich erzähle Ihnen ein wenig über mich selbst - ich habe sowohl in kleinen als auch in relativ großen Projekten gearbeitet und in einem Projekt mit mehr als 100 Microservices (und SLA 99,999%) Polyrepositorys verwendet. Im Moment beschäftige ich mich mit der Übersetzung eines kleinen Mono-Repositorys (eigentlich nicht nur des vorderen js + java-Backends) von maven nach bazel. Funktionierte nicht bei Google, Facebook, Twitter, d.h. Ich hatte nicht das Vergnügen, ein ordnungsgemäß konfiguriertes und abgestimmtes Mono-Repository zu verwenden.
Was ist ein Monorepository für den Anfang? Kommentare zur Übersetzung des Originalartikels zeigten, dass viele glauben, dass ein Mono-Repository ist, wenn alle 5 Unternehmensentwickler an einem Repository arbeiten und das Frontend und das Backend zusammen darin speichern. Das ist natürlich nicht so. Ein Mono-Repository ist eine Möglichkeit, alle Unternehmensprojekte, Bibliotheken, Build-Tools, Plug-Ins für IDEs, Bereitstellungsskripte und alles andere in einem großen Repository zu speichern. Details hier finden Sie unter
trunkbaseddevelopment.com .
Wie heißt der Ansatz, wenn das Unternehmen klein ist und einfach nicht so viele Projekte, Module und Komponenten hat? Dies ist auch ein Monorepository, nur ein kleines.
Natürlich heißt es im Originalartikel, dass alle beschriebenen Probleme in einem bestimmten Ausmaß auftreten. Daher sind diejenigen, die schreiben, dass ihr Mono-Repository mit 1,5 Baggern perfekt funktioniert, mit Sicherheit absolut richtig.
Die erste Tatsache, die ich beheben möchte: Ein
Monorepository ist ein guter Anfang für Ihr neues Projekt . Wenn Sie den gesamten Code auf einen Haufen legen, erhalten Sie zunächst nur einen Vorteil, weil Die Unterstützung mehrerer Repositorys erhöht sicherlich den Aufwand.
Was ist dann das Problem? Und das Problem beginnt, wie im Originalartikel erwähnt, in einem bestimmten Ausmaß. Und vor allem sollten Sie den Moment nicht verpassen, in dem eine solche Skala bereits eingetroffen ist.
Daher bin ich geneigt zu behaupten, dass die auftretenden Probleme im Wesentlichen nicht die Probleme des Ansatzes "Setzen Sie Ihren gesamten Code auf einen Heap" sind, sondern Probleme einfach großer Quellcode-Repositorys. Das heißt, Unter der Annahme, dass Sie Polyrepositorys für verschiedene Dienste / Komponenten verwendet haben und einer dieser Dienste so groß geworden ist (wie groß wir später diskutieren werden), werden Sie höchstwahrscheinlich genau dieselben Probleme bekommen, aber auch ohne die Vorteile von Mono-Repositorys (falls vorhanden) , natürlich gibt es).
Wie groß sollte das Repository sein, um als problematisch eingestuft zu werden?
Es gibt definitiv zwei Indikatoren, von denen dies abhängt - die Menge an Code und die Anzahl der Entwickler, die mit diesem Code arbeiten. Wenn Ihr Projekt Terabyte an Code enthält, aber 1-2 Personen damit arbeiten, werden sie höchstwahrscheinlich fast keine Probleme bemerken (naja, oder zumindest ist es einfacher, nichts zu tun, selbst wenn sie es bemerken :)
Wie können Sie feststellen, dass es an der Zeit ist, über eine Verbesserung Ihres Repositorys nachzudenken? Dies ist natürlich ein subjektiver Indikator. Höchstwahrscheinlich werden sich Ihre Entwickler beschweren, dass etwas nicht zu ihnen passt. Das Problem ist jedoch, dass es möglicherweise zu spät ist, etwas zu ändern. Ich gebe Ihnen einige persönliche Zahlen: Wenn das Klonen Ihres Repositorys länger als 10 Minuten dauert, wenn das Erstellen eines Projekts länger als 20 bis 30 Minuten dauert, wenn die Anzahl der Entwickler 50 überschreitet, und so weiter.
Eine interessante Tatsache aus der persönlichen Praxis:Ich habe in einem Team von ungefähr 50 Entwicklern, aufgeteilt in mehrere kleine Teams, an einem ziemlich großen Monolithen gearbeitet. Die Entwicklung wurde in Feature-Brunchs durchgeführt, und die Zusammenführung erfolgte kurz vor dem Einfrieren der Features. Einmal verbrachte ich 3 Tage mit dem Zusammenschluss unserer Teamabteilung, nachdem 6 andere Teams vor mir gefroren waren.
Lassen Sie uns nun die Liste der Probleme durchgehen, die in großen Repositories auftreten (einige davon wurden im Originalartikel erwähnt, andere nicht).
1) Repository-Downloadzeit
Einerseits können wir sagen, dass dies eine einmalige Operation ist, die der Entwickler während der Ersteinrichtung seiner Workstation ausführt. Persönlich habe ich oft Situationen, in denen ich ein Projekt in einen benachbarten Ordner klonen, tiefer darin graben und es dann löschen möchte. Wenn das Klonen jedoch länger als 10 bis 20 Minuten dauert, ist dies nicht so praktisch.
Vergessen Sie jedoch nicht, dass Sie vor dem Erstellen des Projekts auf dem CI-Server das Repository für jeden Build-Agenten klonen müssen. Und hier beginnen Sie herauszufinden, wie Sie diese Zeit sparen können, denn wenn jede Baugruppe 10 bis 20 Minuten länger dauert und das Ergebnis der Baugruppe 10 bis 20 Minuten später angezeigt wird, ist dies für niemanden geeignet. Das Repository wird also in den Images der virtuellen Maschinen angezeigt, von denen aus die Agenten bereitgestellt werden. Zusätzliche Komplexität und zusätzliche Kosten für die Unterstützung dieser Lösung treten auf.
2) Bauzeit
Dies ist ein ziemlich offensichtlicher Punkt, der schon oft diskutiert wurde. Wenn Sie viele Quellcodes haben, wird die Montage in jedem Fall eine beträchtliche Zeit in Anspruch nehmen. Eine bekannte Situation ist, wenn Sie nach dem Ändern einer Codezeile eine halbe Stunde warten müssen, bis die Änderungen wieder zusammengesetzt und getestet wurden. Tatsächlich gibt es nur einen Ausweg: ein Build-System zu verwenden, das auf Caching-Ergebnissen und inkrementellen Builds basiert.
Hier gibt es nicht viele Optionen - trotz der Tatsache, dass Caching-Funktionen zum gleichen Gradle hinzugefügt wurden (leider habe ich sie in der Praxis nicht verwendet), bringen sie keinen praktischen Nutzen, da herkömmliche Build-Systeme keine wiederholbaren Ergebnisse erzielen (reproduzierbare Builds). Das heißt, Aufgrund der Nebenwirkungen des vorherigen Builds muss ohnehin irgendwann die Cache-Bereinigung
maven clean build
(der Standardansatz für die
maven clean build
). Daher bleibt nur die Möglichkeit, Bazel / Buck / Pants und ähnliche zu verwenden. Warum dies nicht sehr gut ist, werden wir etwas später besprechen.
3) Indizierung der IDE
Mein aktuelles Projekt ist 30 bis 40 Minuten in Intellij IDEA indiziert. Was ist mit deinem? Natürlich können Sie nur einen Teil des Projekts öffnen oder alle unnötigen Module von der Indizierung ausschließen, aber ... Das Problem ist, dass bei jedem Wechsel von einem Zweig zu einem anderen eine Neuindizierung erfolgt. Deshalb klone ich gerne ein Projekt in ein benachbartes Verzeichnis. Einige Leute fangen an, den IDE-Cache zwischenzuspeichern :)
<DiCaprio-Bild mit zusammengekniffenen Augen>
4) Erstellen Sie Protokolle
Welchen CI-Server verwenden Sie? Bietet es eine praktische Oberfläche zum Anzeigen und Navigieren in mehreren Gigabyte Build-Protokollen? Leider ist meins nicht :(
5) Geschichte der Commits
Möchten Sie die Commit-Geschichte sehen? Ich liebe, besonders in einem Werkzeug mit einer grafischen Oberfläche (ich nehme Informationen besser visuell wahr, schimpfe nicht :).
So sieht der Commit-Verlauf in meinem Repository aus Gefällt? Ist es bequem? Persönlich tue ich nicht!
6) Gebrochene Tests
Was passiert, wenn jemand fehlerhafte Tests / nicht kompilierten Code im Master ausführen konnte? Sie werden sicherlich sagen, dass Ihr CI dies nicht zulässt. Was ist mit den instabilen Tests, die der Autor besteht, und sonst niemand? Stellen Sie sich nun vor, dass sich dieser Code auf die Maschinen von 300 Entwicklern ausbreitet und keiner von ihnen ein Projekt zusammenstellen kann? Was tun in einer solchen Situation? Warten Sie, bis der Autor es bemerkt und korrigiert? Richtig für ihn? Änderungen rückgängig machen? Im Idealfall lohnt es sich natürlich, nur guten Code zu schreiben und sofort ohne Fehler zu schreiben. Dann wird ein solches Problem nicht auftreten.
(Für diejenigen, die die Hinweise im Tank nicht verstanden haben, ist die Diskussion über die negativen Auswirkungen, wenn dies im Repository mit 10 Entwicklern und im Repository mit 300 Entwicklern geschieht, etwas anders.)
7) Bot zusammenführen
Schon mal was davon gehört? Weißt du warum du es brauchst? Sie werden lachen, aber dies ist ein weiteres Tool, das es eigentlich nicht geben sollte :) Stellen Sie sich vor, die Erstellungszeit Ihres Projekts beträgt 30 Minuten. Und 100 Entwickler arbeiten an Ihrem Projekt. Angenommen, jeder von ihnen drückt 1 Commit pro Tag. Stellen Sie sich nun ein ehrliches CI vor, mit dem Sie Änderungen am Master erst zusammenführen können, nachdem sie auf das letzte Commit des Masters angewendet wurden (Rebase).
Achtung, die Frage ist: Wie viele Stunden sollte ein so ehrlicher CI-Server an einem Tag haben, um Änderungen von allen Entwicklern zu erdrosseln? Die richtige Antwort ist 50. Wer richtig geantwortet hat, kann eine Karotte aus einem Regal nehmen. Oder stellen Sie sich vor, Sie haben gerade Ihr Commit für das allerletzte Commit an den Master abgeschnitten, die Assembly gestartet und als sie fertig war, hat der Master bereits 20 Commits durchgeführt. Wieder von vorne?
Merge Bot oder Merge Queue ist also ein Dienst, der den Prozess des erneuten Basierens aller Zusammenführungsanforderungen für einen neuen Master, das Ausführen von Tests und das Zusammenführen selbst automatisiert und Commits auch zu Stapeln kombinieren und zusammen testen kann. Sehr handliche Sache. Siehe
urgify.io ,
k8s test-infra Prow von Google,
bors-ng usw. (Ich verspreche, in Zukunft mehr darüber zu schreiben)
Nun zu weniger technischen Problemen:
8) Verwenden eines einzelnen Build-Tools
Ehrlich gesagt ist es mir immer noch ein Rätsel, warum ich das gesamte Mono-Repository mit einem gemeinsamen Build-System zusammenstellen soll. Warum nicht Javascript mit Garn, Java mit Gradle, Scala mit sbt usw. erstellen? Wenn jemand die Antwort auf diese Frage kennt (nicht errät oder vorschlägt, nämlich weiß), schreibe in die Kommentare.
Natürlich scheint es offensichtlich, dass die Verwendung eines Build-Systems besser ist als mehrere verschiedene. Aber sie verstehen immer noch, dass jede universelle Sache offensichtlich schlimmer ist als eine spezialisierte, weil es hat höchstwahrscheinlich nur eine Teilmenge der Funktionen aller spezialisierten. Schlimmer noch, verschiedene Programmiersprachen können unterschiedliche Paradigmen in Bezug auf Assemblierung, Abhängigkeitsmanagement usw. haben, die sehr schwer in einen gemeinsamen Wrapper zu packen sind. Ich möchte nicht auf Details eingehen, ich werde ein Beispiel zu bazel geben (siehe Details in einem separaten Artikel) - wir haben 5 unabhängige Implementierungen von Javascript-Assemblierungsregeln für bazel von 5 verschiedenen Unternehmen auf GitHub gefunden, zusammen mit dem offiziellen von Google. Es lohnt sich, darüber nachzudenken.
9) Allgemeine Ansätze
Als Antwort auf den Originalartikel schrieb CTO vom Chef seine Antwort
Monorepo: Bitte tun! . In seiner Antwort argumentiert er, dass "die Hauptsache im Monorepo ist, dass es Sie zum Reden bringt und die Fehler sichtbar macht." Er bedeutet, dass Sie, wenn Sie Ihre API ändern möchten, alle Verwendungszwecke finden und Ihre Änderungen mit den Betreuern dieser Codeteile besprechen müssen.
Meine Erfahrung ist also genau das Gegenteil. Es ist klar, dass dies sehr stark von der Ingenieurkultur im Team abhängt, aber ich sehe solide Nachteile in diesem Ansatz. Stellen Sie sich vor, Sie verwenden einen bestimmten Ansatz, der Ihnen seit einiger Zeit treu dient. Und so haben Sie sich aus irgendeinem Grund entschieden, ein ähnliches Problem zu lösen und eine etwas andere Methode zu verwenden, möglicherweise eine modernere. Wie hoch ist die Wahrscheinlichkeit, dass das Hinzufügen eines neuen Ansatzes einer Überprüfung unterzogen wird?
In meiner jüngsten Vergangenheit erhielt ich mehrmals Kommentare wie „Wir haben bereits einen bewährten Pfad, verwenden Sie ihn“ und „Wenn Sie einen neuen Ansatz implementieren möchten, aktualisieren Sie den Code an allen 120 Stellen, an denen der alte Ansatz verwendet wird, und erhalten Sie das Update von allen Teams, die dafür verantwortlich sind diese Code-Teile. " In der Regel endet hier die Begeisterung des „Innovators“.
Und wie viel kostet es Ihrer Meinung nach, einen neuen Dienst in einer neuen Programmiersprache zu schreiben? Im Repository - überhaupt nicht. Sie erstellen ein neues Repository, schreiben und verwenden sogar das am besten geeignete Build-System. Und jetzt das gleiche im Monorepository?
Ich verstehe sehr gut, dass "Standardisierung, Wiederverwendung, Code-Sharing", aber das Projekt sollte entwickelt werden. Meiner subjektiven Meinung nach verhindert ein Monorepository dies eher.
10) Open Source
Kürzlich wurde ich gefragt: „
Gibt es Open Source-Tools für Mono-Repositorys? “ Ich antwortete: „Das Problem ist, dass Tools für Mono-Repositorys seltsamerweise im Mono-Repository selbst entwickelt werden. Daher ist es ziemlich schwierig, sie in Open Source zu integrieren! “
Schauen Sie sich als Beispiel ein Projekt auf Github mit einem
Bazel-Plugin für Intellij IDEA an . Google entwickelt es in seinem internen Repository und "spritzt" dann Teile davon auf Github mit einem Verlust des Commit-Verlaufs aus, ohne die Möglichkeit, eine Pull-Anfrage zu senden, und so weiter. Ich denke nicht, dass es Open Source ist (hier ist ein Beispiel für
meine kleine PR , die geschlossen wurde, anstatt zusammengeführt zu werden, und dann wurden die Änderungen in der nächsten Version angezeigt). Übrigens wurde diese Tatsache im Originalartikel erwähnt, dass Mono-Repositories verhindern, dass sie in Open Source veröffentlicht werden und eine Community rund um das Projekt schaffen. Ich denke, viele haben diesem Argument nicht viel Bedeutung beigemessen.
Alternativen
Nun, wenn wir darüber sprechen, was zu tun ist, um all diese Probleme zu vermeiden? Es gibt genau einen Rat: Bemühen Sie sich um ein möglichst kleines Repository.
Aber was hat das Monorepository damit zu tun? Und obwohl dieser Ansatz Ihnen die Möglichkeit nimmt, kleine, leichte und unabhängige Repositories zu haben.
Was sind die Nachteile des Polyrepository-Ansatzes? Ich sehe genau 1: die Unfähigkeit, den Überblick darüber zu behalten, wer der Verbraucher Ihrer API ist. Dies gilt insbesondere für den Ansatz in Microservices,
„nichts zu teilen“ , bei dem der Code nicht zwischen Microservices herumfummelt. (Glauben Sie übrigens, dass jemand diesen Ansatz in Mono-Repositorys verwendet?) Leider muss dieses Problem entweder organisatorisch gelöst werden oder es sollte versucht werden, Tools zum Durchsuchen von Code zu verwenden, die unabhängige Repositorys unterstützen (z. B.
https://sourcegraph.com) / ).
Was ist mit Kommentaren wie
"Wir haben Polyrepositorys ausprobiert, aber dann mussten wir ständig Funktionen in mehreren Repositorys gleichzeitig implementieren, was lästig war und wir haben alles in einem Boiler zusammengeführt" ? Die Antwort darauf ist sehr einfach:
"Verwechseln Sie die Probleme des Ansatzes nicht mit einer unsachgemäßen Zerlegung .
" Niemand behauptet, dass das Repository genau einen Microservice enthalten sollte, und das war's. Als ich Polyrepositorys verwendete, haben wir eine Familie eng verwandter Mikrodienste perfekt in einem Repository zusammengefasst. Angesichts der Tatsache, dass es mehr als 100 Dienste gab, gab es jedoch mehr als 20 solcher Repositorys. Das Wichtigste bei der Zerlegung ist, wie diese Dienste bereitgestellt werden.
Aber was ist mit dem Argument über die Version? Mit Mono-Repositorys können Sie schließlich keine Versionen haben und alles von einem Commit aus bereitstellen! Erstens ist die Versionierung das einfachste aller hier angesprochenen Probleme. Selbst in einem alten Ding wie Maven gibt es ein Maven-Versions-Plugin, mit dem Sie die Version mit nur einem Klick herunterstufen können. Und zweitens und vor allem: Verfügt Ihr Unternehmen über mobile Anwendungen? Wenn ja, dann haben Sie bereits Versionen, und Sie werden davon nichts bekommen!
Nun, es gibt immer noch das Hauptargument zur Unterstützung von Mono-Repositorys - es ermöglicht Ihnen, Refactoring in der gesamten Codebasis in einem Commit durchzuführen! In der Tat nein. Wie im ursprünglichen Artikel erwähnt, aufgrund der Einschränkungen, die die Bereitstellung auferlegt. Sie sollten immer bedenken, dass Sie für eine lange Zeit (die Dauer hängt davon ab, wie Ihr Prozess aufgebaut ist) zwei Versionen desselben Dienstes parallel haben. Bei meinem letzten Projekt befand sich unser System beispielsweise bei jeder Bereitstellung mehrere Stunden in diesem Zustand. Dies führt dazu, dass es unmöglich ist, globale Refactorings durchzuführen, die die Interaktionsschnittstellen in einem einzigen Commit beeinflussen, selbst in einem Mono-Repository.
Anstelle einer Schlussfolgerung:
Also, die angesehenen und wenigen Kollegen, die in Google, Facebook usw. arbeiten. und kommen Sie hierher, um ihre Mono-Repositories zu verteidigen. Ich möchte sagen: "Keine Sorge, Sie machen alles richtig, genießen Sie Ihre Abstimmung, die Hunderttausende oder Millionen menschlicher Stunden verbracht hat. Sie wurden bereits ausgegeben. Wenn du sie also nicht verwendest, wird es niemand tun. "
Und an alle anderen:
"Sie sind nicht Google, verwenden Sie keine Mono-Repositories!"P.S. Wie der angesehene Bobuk im
Radio-T- Podcast bei der Diskussion des Originalartikels feststellte: „Es gibt ~ 20 Unternehmen auf der Welt, die ein einziges Repository verwenden können.
Der Rest sollte es nicht einmal versuchen . “