Unser Weg zum Monorep
Lasst mich für diesen Blog-Eintrag ein wenig weiter ausholen und euch die gesamte Reise ins Monorep präsentieren. Eines vorweg: Es ist ein steiniger Weg, der den nicePeople alles abverlangt hat und für die meisten zu einer radikalen Änderung ihrer täglichen Arbeitsweise führte.
Gehen wir nun an den Anfang unserer Reise: Wir schreiben das Jahr 2021, die Corona-Pandemie hat uns voll im Griff und die tägliche Arbeit eines Developers beginnt damit, das Projekt von einem oder mehreren der über vierzig Shops zu öffnen. Hinter jedem dieser Shops steht ein eigenes Git Repository mit drei eingebundenen Submodulen.
Ein Modul beinhaltet die gesamte Konfiguration für alle Shops und Firmen und ein weiteres den Basis Source Code. Dieser sogenannte Library Source ist immer eine abstrakte Implementierung, denn instanziert wird immer die äquivalente, von der Library abgeleitete und meist leere Shop-Klasse. Hierdurch ist es möglich, den Programmfluss für einzelne Shops, durch Hinzufügen von Source Code in der Application Ebene, zu ändern. Das dritte Modul enthält alle geteilten Bilder, JavaScript und CSS Dateien.
Der Entwicklungsfluss sah meist so aus, dass man ausgehend vom teameigenen Sprint Branch einen Approval Branch über den Jenkins erstellte und diesen nach getaner Arbeit einfach zurück pushte. Gleichzeitig musste eine Code Review im Upsource angelegt und einem Teammitglied zugewiesen werden. Am Jenkins wurden anschließend über Nacht die Unit Tests ausgeführt und man bekam am nächsten Tag das Unheil präsentiert. Als wäre das nicht genug, kam gleichzeitig ein Quality Assurance Manager und zerlegte das neue Meisterwerk nach Strich und Faden. Hatte man all diese Schritte überstanden, konnte der Branch über den Jenkins zurück in den Team Branch gemerged werden und von dort aus wurden die Änderungen am Sprintende in den Develop zurück gemerged. Konflikte waren hier bei den zweiwöchigen Sprints schon so gut wie – im wahrsten Sinne des Wortes – vorprogrammiert.
So viel zur bisherigen Arbeitsweise – jetzt ist alles anders!
Vorhang auf für das Monorep. Nach ca. vier Monaten Vorbereitungszeit, wo wir begonnen haben das System für das Monorep durch Ändern der Einstiegspunkte, einem weitreichenden Cleanup und dem Ersetzen der Überschreibungen auf Applicationebene durch Dependency Injection fit zu machen, haben wir einen gesamten Sprint reserviert, um die Umstellung über alle Teams hinweg durchzuführen und etwaige Probleme zu beseitigen. Parallel zu dem Cleanup wurde ein Importer für den Source Code aus allen Shops geschrieben, damit die Portierung in das neue Repository automatisch durchgeführt werden kann. Dieser Updater hatte insgesamt elf Runden gezogen, bis alle Dateien am richtigen Platz waren. Das DevOps Team wurde in den letzten Monaten durch diese Umstellung auch richtig gefordert: In einem ersten Schritt wurde schon früh eine Umstellung auf Gitlab zur Source Code Verwaltung vollzogen. Danach wurden Schritt für Schritt die Prozesse von Jenkins in Gitlab Runner umgezogen und ein Großteil des bestehenden Source Codes zum Bauen der Shops für die Staging Server und das Live System neu geschrieben und direkt als CLI Modul in das Hauptprojekt integriert. Durch die Gitlab Runner und das Umstellen auf Merge Requests wurde erstmals eine sinnvolle QA Pipeline ermöglicht mit phpstan, phpcs, phplint und Unit Tests. Das System wurde so konzipiert, dass im Merge Request nachgesehen wird, welche Dateien geändert wurden und über das Setzen von Labels dann die richtigen Pipelines getriggert werden. Wird zum Beispiel in einem Shop eine PHP Datei hinzugefügt, erkennt das die Pipeline sofort und führt für diesen Shop die Unittests aus.
Als wäre das noch nicht genug, haben wir auch die Art und Weise wie wir entwickeln – von Git Flow weg hin zum Trunk Based Modell – gewechselt.
Hierdurch sind die Teambranches weggefallen und der zentrale Ausgangspunkt für jeden Branch ist jetzt immer der Main. Ziel soll es sein, dass kein Merge Request einen Tag überdauert und somit der Source Code jeden Abend direkt in den Main zurück gemerged wird. Wichtige Begriffe sind jetzt Dark Launching und Feature Flags, d.h. man stellt den Code bereits im Hintergrund online und schaltet ihn zu einem gegebenen Zeitpunkt frei. Dies alles läuft automatisch über Merge Requests in Gitlab, d.h. pusht man eine Änderung, wird automatisch ein Merge Request erstellt und die QA Pipeline beginnt zu arbeiten. Im gleichen Zug muss nur noch ein weiteres Teammitglied für den Code Review zugewiesen werden. Die maximale QA Stufe beinhaltet sogar den Review durch einen Software Quality Manager. Hierfür wird automatisch der Shop auf dem Staging Server gebaut und somit für die Tests bereitgestellt. Haben nun alle drei Parteien, die automatische QA Pipeline, der Code Reviewer und der Software Tester den Merge akzeptiert, wird der Source in den Main zurück gemerged.
Jetzt fragt ihr euch sicher, warum wir diesen Weg auf uns genommen haben. Ganz einfach: Das tägliche Arbeiten wird hierdurch viel einfacher. Statt 51 Projekte braucht ein Developer nur noch ein einziges, die verschiedenen Shops werden einfach per CLI gestartet. Die Konflikte zwischen den Teams beschränken sich auf maximal einen Tag und jedes Team kann sofort von neuem Source der anderen Teams profitieren. Als wichtigster Punkt kann aber die Einführung der verpflichtenden QA Pipeline gesehen werden, durch welche kein ungetesteter Code mehr in den Main gemerged werden kann.
Alles in Allem haben wir den Code von 362.400 Dateien in über 50 Repositories auf 43.500 Dateien in einem einzigen Projekt reduziert und die Entwicklung von neuen Features extrem beschleunigt. Darüber hinaus ist die Projektstruktur viel einfacher geworden und das System ist nun vorbereitet für ein weiteres Wachsen des Nice Valleys mit neuen Teams und Shops.
Stay Nice!