Trunk-basierte Entwicklung

Erfahre, warum dieses Verfahren des Versionskontrollmanagements bei DevOps-Teams üblich ist.

Kev Zettler Kev Zettler

Zusammenfassung: Die Trunk-basierte Entwicklung ist eine Praktik der Versionskontrolle, bei der Entwickler kleine, häufige Änderungen in einem Kern- oder Haupt-Branch ("Trunk") zusammenführen. Dadurch werden die Zusammenführungs- und Integrationsphasen optimiert. Diese Vorgehensweise trägt zum Erreichen von CI/CD bei und optimiert die Softwareauslieferung und die organisatorische Effizienz.

In den Anfangszeiten der Softwareentwicklung hatten Programmierer nicht den Luxus moderner Versionskontrollsysteme. Vielmehr entwickelten sie gleichzeitig zwei Versionen ihrer Software, um Änderungen zu verfolgen und bei Bedarf rückgängig zu machen. Im Laufe der Zeit erwies sich dieser Prozess als arbeitsintensiv, kostspielig und ineffizient.

Als Versionskontrollsysteme reiften, entstanden verschiedene Entwicklungsstile, die es Programmierern ermöglichten, Fehler leichter zu finden, parallel zu ihren Kollegen zu programmieren und den Release-Rhythmus zu beschleunigen. Heute nutzen die meisten Programmierer eines von zwei Entwicklungsmodellen, um qualitativ hochwertige Software bereitzustellen – Gitflow und Trunk-basierte Entwicklung.

Gitflow, das zuerst erfolgreich angewandt wurde, ist ein strengeres Entwicklungsmodell, bei dem nur bestimmte Personen Änderungen am Hauptcode genehmigen können. Dies erhält die Codequalität und minimiert die Anzahl der Fehler. Die Trunk-basierte Entwicklung ist ein offeneres Modell, da alle Entwickler Zugriff auf den Hauptcode haben. Dies ermöglicht Teams schnelle Iterationen und die Implementierung von CI/CD.

Was ist Trunk-basierte Entwicklung?

Trunk-basierte Entwicklung ist eine Praktik der Versionskontrolle, bei der Entwickler kleine, häufige Änderungen in einem Kern- oder Haupt-Branch ("Trunk") zusammenführen. Diese Methode wird häufig von DevOps-Teams verwendet und ist Teil des DevOps-Lebenszyklus, da sie die Zusammenführungs- und Integrationsphasen optimiert. Trunk-basierte Entwicklung ist eine Voraussetzung für CI/CD. Entwickler können im Vergleich zu anderen langlebigen Branching-Strategien für Funktionen kurzlebige Branches mit wenigen kleinen Commits erstellen. Bei zunehmender Komplexität der Codebasis und wachsenden Teams trägt die Trunk-basierte Entwicklung dazu bei, einen kontinuierlichen Flow von Produktions-Releases sicherzustellen.

Gitflow vs. Trunk-basierte Entwicklung

Gitflow ist ein alternatives Git-Branching-Modell, das langlebige Feature-Branches und mehrere primäre Branches verwendet. Gitflow verfügt über mehr und langlebigere Branches sowie größere Commits als Trunk-basierte Entwicklung. Unter diesem Modell erstellen Entwickler einen Feature-Branch und verzögern den Merge mit dem Haupt-Trunk-Branch, bis das Feature vollständig ist. Diese langlebigen Feature-Branches erfordern mehr Zusammenarbeit bei Merges, da sie ein höheres Risiko haben, vom Trunk-Branch abzuweichen und konfliktbehaftete Updates einzuführen.

Gitflow hat auch separate primäre Branch-Zeilen für Entwicklung, Hotfixes, Funktionen und Releases. Es gibt verschiedene Strategien für den Merge von Commits dieser Branches. Da mit mehr Branches hantiert wird, nimmt die Komplexität zu und es werden zusätzliche Planungssitzungen und eine Überprüfung durch das Team erforderlich.

Die Trunk-basierte Entwicklung ist weitaus einfacher, da sie sich auf den Haupt-Branch als Quelle für Problembehebungen und Releases konzentriert. Bei der Trunk-basierten Entwicklung wird davon ausgegangen, dass der Haupt-Branch immer stabil, ohne Probleme und bereit für das Deployment ist.

Vorteile der Trunk-basierten Entwicklung

Trunk-basierte Entwicklung ist ein für Continuous Integration erforderliches Verfahren. Wenn Build- und Testprozesse automatisiert sind, Entwickler jedoch an isolierten, langwierigen Feature-Branches arbeiten, die selten in einen gemeinsamen Branch integriert werden, wird Continuous Integration ihr Potenzial nicht entfalten können.

Trunk-basierte Entwicklung verringert die Reibung der Code-Integration. Wenn Entwickler neue Aufgaben abgeschlossen haben, müssen sie den neuen Code in den Haupt-Branch mergen. Dennoch sollten sie Merges der Änderungen im Trunk erst durchführen, wenn sie überprüft haben, dass Builds erfolgreich sind. In dieser Phase können Konflikte auftreten, wenn seit Beginn der neuen Aufgaben Änderungen vorgenommen wurden. Diese Konflikte werden insbesondere deshalb immer komplexer, da die Entwicklerteams wachsen und die Codebasis skaliert wird. Dies geschieht, wenn Entwickler separate Branches erstellen, die vom Quell-Branch abweichen, und andere Entwickler gleichzeitig für überlappenden Code einen Merge durchführen. Glücklicherweise reduziert das Trunk-basierte Entwicklungsmodell diese Konflikte.

Möglichkeit zur kontinuierlichen Code-Integration

Im Trunk-basierten Entwicklungsmodell gibt es ein Repository mit einem stetigen Strom von Commits, die in den Haupt-Branch fließen. Automatisierte Tests und Überwachung der Codeabdeckung für diese Commits ermöglicht Continuous Integration (CI). Sobald neuer Code im Trunk zusammengeführt wird, werden Integration und Codeabdeckung zur Validierung der Codequalität automatisch getestet.

Sicherstellen einer kontinuierlichen Code-Prüfung

Die schnellen, kleinen Commits der Trunk-basierten Entwicklung machen den Code-Prüfungsprozess effizienter. In kleinen Branches können Entwickler kleine Änderungen schnell sehen und überprüfen. Dies ist viel einfacher als bei einem langlebigen Feature-Branch, in dem ein Prüfer seitenweise Code liest oder eine große Oberfläche manuell auf Codeänderungen überprüft.

Möglichkeit zu aufeinander folgenden Produktionscode-Releases

Teams sollten häufige, tägliche Merges in den Haupt-Branch durchführen. Die Trunk-basierte Entwicklung zielt darauf ab, den Trunk-Branch im "grünen" Bereich zu halten, was bedeutet, dass er nach jedem Commit bereit für das Deployment ist. Automatisierte Tests, Codeabdeckung und Code-Reviews liefern die notwendige Sicherheit, dass ein Trunk-basiertes Entwicklungsprojekt jederzeit in der Produktion bereitgestellt werden kann. Teams erhalten so die Flexibilität, Code häufig in der Produktionsumgebung bereitzustellen und sich weitere Ziele bei den täglichen Produktions-Releases zu setzen.

Trunk-basierte Entwicklung und CI/CD

Mit steigender Popularität von CI/CD wurden Branching-Modelle präzisiert und optimiert, was zu einem Anstieg der Trunk-basierten Entwicklung führte. Heute ist die Trunk-basierte Entwicklung eine Voraussetzung für Continuous Integration. Bei Continuous Integration führen Entwickler eine Trunk-basierte Entwicklung in Verbindung mit automatisierten Tests durch, die nach jedem Commit in einen Trunk ausgeführt werden. Dadurch wird sichergestellt, dass das Projekt jederzeit funktioniert.

Best Practices für Trunk-basierte Entwicklung

Die Trunk-basierte Entwicklung stellt sicher, dass Teams Code schnell und konsistent veröffentlichen. Im Folgenden findest du eine Liste von Übungen und Verfahren, mit denen du den Rhythmus deines Teams verfeinern und einen optimierten Release-Plan entwickeln kannst.

Entwicklung in kleinen Batches

Bei der Trunk-basierten Entwicklung wird Code in einem schnellen Rhythmus an die Produktion ausgeliefert. Wäre die Trunk-basierte Entwicklung ein Musikstück, wäre es in einem schnellen Staccato – kurze, prägnante Noten in schneller Folge, wobei die Repository-Commits die Noten darstellen. Kleine Commits und Branches ermöglichen ein schnelleres Tempo von Merges und Deployments.

Kleine Änderungen einiger Commits oder die Änderung einiger Codezeilen minimieren den kognitiven Overhead. Es ist viel einfacher für Teams, sinnvolle Gespräche zu führen und schnelle Entscheidungen zu treffen, wenn sie einen begrenzten Codebereich anstatt einen weitläufigen Satz von Änderungen überprüfen.

Feature-Flags

Feature-Flags sind eine hervorragende Ergänzung der Trunk-basierten Entwicklung. Entwickler können neue Änderungen in einen inaktiven Codepfad einbinden und zu einem späteren Zeitpunkt aktivieren. Sie müssen also keinen separaten Feature-Branch im Repository erstellen, sondern können stattdessen neuen Feature-Code innerhalb eines Feature-Flag-Pfads direkt in den Haupt-Branch committen.

Feature-Flags ermutigen direkt zu kleinen Batch-Updates. Anstatt einen Feature-Branch zu erstellen und darauf zu warten, die vollständige Spezifikation zu erstellen, können Entwickler stattdessen einen Trunk-Commit erstellen, der das Feature-Flag einführt und neue Trunk-Commits pusht, die die Feature-Spezifikation innerhalb des Flag ausbauen.

Implementierung umfassender automatisierter Tests

Automatisierte Tests sind für jedes moderne Softwareprojekt erforderlich, das CI/CD erreichen möchte. Es gibt mehrere Arten von automatisierten Tests, die in verschiedenen Phasen der Release-Pipeline durchgeführt werden. Unit- und Integrationstests mit kurzer Laufzeit werden während der Entwicklung und beim Merge des Codes ausgeführt. End-to-End-Tests des gesamten Stacks mit längerer Laufzeit werden in späteren Pipeline-Phasen für eine vollständige Staging- oder Produktionsumgebung ausgeführt.

Automatisierte Tests unterstützen die Trunk-basierte Entwicklung, indem sie den Rhythmus kleiner Batches aufrechterhalten, während die Entwickler Merges neuer Commits durchführen. Die automatisierte Testsuite überprüft den Code auf etwaige Probleme und genehmigt oder verweigert ihn automatisch. Dies hilft Entwicklern, schnell Commits zu erstellen und für diese automatisierte Tests auszuführen, um festzustellen, ob die Commits neue Probleme verursachen.

Asynchrone Code-Prüfungen

In der Trunk-basierten Entwicklung sollte die Code-Prüfung sofort durchgeführt und nicht erst in ein asynchrones System eingefügt und später überprüft werden. Automatisierte Tests bieten eine Ebene präventiver Code-Prüfungen Wenn Entwickler bereit sind, die Pull-Anfrage eines Teammitglieds zu überprüfen, können sie zunächst überprüfen, ob die automatisierten Tests bestanden wurden und die Codeabdeckung zugenommen hat. Dies gibt dem Prüfer sofort die Gewissheit, dass der neue Code bestimmte Spezifikationen erfüllt. Der Prüfer kann sich dann auf Optimierungen konzentrieren.

Maximal drei aktive Branches im Code-Repository der Anwendung

Sobald ein Branch gemergt wurde, empfiehlt es sich, ihn zu löschen. Ein Repository mit einer großen Anzahl aktiver Branches bringt einige unangenehme Nebenwirkungen mit sich. Es kann für Teams von Vorteil sein, zu sehen, welche Aufgaben in Arbeit sind, indem sie aktive Branches untersuchen. Dieser Nutzen geht jedoch verloren, wenn auch noch alte, inaktive Branches vorhanden sind. Einige Entwickler verwenden Git-Benutzeroberflächen, die beim Laden einer großen Anzahl von Remote-Branches möglicherweise unübersichtlich werden.

Führe Branches mindestens einmal am Tag im Trunk zusammen

Leistungsstarke, Trunk-basierte Entwicklerteams sollten alle offenen Branches sowie Branches, die zum Mergen bereit sind, mindestens einmal täglich schließen und zusammenführen. Dadurch wird eine bestimmte Regelmäßigkeit geschaffen und die Häufigkeit für die Releaseverfolgung festgelegt. Das Team kann dann den Haupt-Trunk am Ende des Tages als Release-Commit markieren. Dies hat den zusätzlichen Effekt, dass auf diese Weise ein tägliches agiles Release-Inkrement entsteht.

Weniger Code-Freezes und Integrationsphasen

Agile CI/CD-Teams sollten keine geplanten Code-Freezes oder Unterbrechungen für Integrationsphasen benötigen – es gibt natürlich trotzdem andere Gründe, die dies erfordern. "Continuous" in CI/CD impliziert, dass ständig Updates im Fluss sind. Trunk-basierte Entwicklerteams sollten versuchen, Code-Freezes zu vermeiden, und entsprechend planen, damit die Release-Pipeline nicht zum Stillstand kommt.

Schnelle Builds und sofortige Ausführung

Um einen schnellen Release-Rhythmus aufrechtzuerhalten, sollten die Build- und Testausführungszeiten optimiert werden. CI/CD-Build-Tools sollten gegebenenfalls Caching-Layer verwenden, um teure Berechnungen für statische Assets zu vermeiden. Tests sollten optimiert werden, um geeignete Stubs für Services von Drittanbietern zu verwenden.

Fazit

Die Trunk-basierte Entwicklung ist derzeit der Standard für leistungsstarke Entwicklerteams, da sie mithilfe einer vereinfachten Git-Branching-Strategie einen Rhythmus für Software-Releases vorgibt und aufrechterhält. Darüber hinaus bietet die Trunk-basierte Entwicklung den Entwicklerteams mehr Flexibilität und Kontrolle darüber, wie sie Software an den Endbenutzer liefern.