Superstarke Continuous Delivery mit Git

Jetzt, da Git das mühevolle Mergen erleichtert, werden Branching-Workflows sehr viel attraktiver.

Sarah Goff-Dupont Sarah Goff-Dupont

Wir alle kennen den Ausspruch: "Vorsicht bei Code, den nur eine Person geschrieben hat." Genauso kennen wir die Vorteile von Teamwork bei der Softwareentwicklung: verschiedene Denkweisen, unterschiedliche Hintergründe und Erfahrungen ... und wenn du diese Unterschiede bei der Lösung von Problemen mit einbringst, hast du am Ende eine bessere Software. Sie ist einfacher zu verwalten, qualitativ hochwertiger und für die Benutzer letztendlich nützlicher.

Zusammenarbeit im Team | Atlassian CI/CD

Aber genauso wissen wir: Die Entwicklung im Team kann chaotisch sein.

Du versuchst zu verstehen, wer an welchem Codeabschnitt arbeitet, möchtest sicherstellen, dass Änderungen keinen Konflikt auslösen, bist bemüht, Fehler zu finden, bevor es die Kunden tun, und hast es dir zur Aufgabe gemacht, alle Projektbeteiligten stets auf dem Laufenden zu halten. Wie sich herausstellt, kann das Git-Branching oder die Continuous Delivery jedes dieser Probleme lösen.

Ich möchte dich gerne davon überzeugen, dass die Kombination aus beiden (vielleicht gemixt mit ein paar anderen Kick-Ass-Tools, nur zum Spaß) dein Zaubertrank-Rezept zum Erfolg ist.

Die Leistungsfähigkeit von Branch-basierten Workflows

Ehrlich gesagt ist Git an und für sich gar nicht so perfekt für die Continuous Delivery (CD). Aber Branching-Workflows sind ideal für CD und Git ist ideal für Branching-Workflows. Ein Branch-and-Merge-Workflow ist nicht nur der ideale Partner der CD – du kannst mit ihm auch lästige Fehler beheben, neue Technologien ausprobieren oder einfach eine neue Funktion von Grund auf neu programmieren, ganz ohne Risiko, dass deine Änderungen deinen Teamkollegen bei ihren eigenen Tasks in die Quere kommen.

Grundlegendes Workflow-Diagramm | Atlassian CI/CD

Klar, Branches kannst du auch mit Subversion und anderen herkömmlichen Versionskontrollsystemen erstellen. Aber machen wir doch einen kurzen Exkurs und lernen wir den bösen Zwilling von Branching kennen: den Merge.

Herkömmliche Versionskontrollsysteme wie Subversion sind einfach nicht so gut beim Nachverfolgen von Dateiversionen in verschiedenen Branches. Und wenn es Zeit für den Merge ist, muss Subversion sehr oft anhalten und nach dem Weg fragen. (Du weißt schon, dieses kleine Pop-up mit der Frage "Möchtest du diese oder jene Zeile in der Merge-Version?") Da beim Durchführen von Merges so viel menschliche Interaktion nötig ist, setzen Teams gerne Code-Freezes ein, damit die Person, die den Merge gerade durchführt, nicht von den Änderungen in einem der Branches unterbrochen wird. Code-Freezes kosten Zeit – und Zeit ist Geld.

Git dagegen ist wirklich gut darin, Änderungen an verschiedenen Dateiversionen in verschiedenen Branches nachzuverfolgen, und weiß immer, wie der gemeinsame Vorgänger dieser Datei aussah. Es hat quasi ein eingebautes GPS, mit dem es Merges durchführen kann, ohne ständig anzuhalten und dich nach dem Weg zu fragen.

Mit Git hast du die Freiheit, die Leistungsfähigkeit von Branching auf eine Art und Weise zu nutzen, die mit Subversion nicht umsetzbar wäre. Der geringe Aufwand für das Branching und Mergen macht Branches, die nur einen oder zwei Tage benötigt werden, nicht nur realisierbar, sondern auch vorteilhaft.

Okay, gut. Aber warum genau ist Branching überhaupt so gut für Continuous Delivery geeignet?

Branches halten den Main sauber und Release-bereit

Wir haben bereits festgestellt, dass kurzlebige Branches eine gute Möglichkeit für Entwickler sind, an einem Projekt oder einer Funktion zusammenzuarbeiten, ohne sich gegenseitig auf die Füße zu treten. Aber noch wichtiger für CD ist, dass das Isolieren laufender Arbeiten in Entwicklungs-Branches die Main- sowie die stabilen Versions-Branches sauber hält. So kannst du sie ausliefern, wann du möchtest.

Das bedeutet, du solltest alle deine automatisierten Tests über Entwicklungs-Branches ausführen. Entwickler erhalten eindeutige Signale bezüglich der Qualität ihres Codes und können dann zuversichtlich entscheiden, wann der Merge für ihre Änderungen durchgeführt wird. (Wenn du bei den automatisierten Tests noch nicht ganz durchsteigst, dann schau dir diesen Beitrag von RebelLabs an. Dort findest du eine lockere Einführung und Rezepte dafür, wie du deine ersten Unit-Tests schreibst.)

Es bedeutet auch, Pull-Requests von Git als eine Art Code-Review zu nutzen, damit sich dein gesamtes Team auf die Wartungsfreundlichkeit des Codes und die Interoperabilität mit anderen Bereichen der Codebasis verlassen kann. Ja, das ist mehr Arbeit im Vorfeld als bei herkömmlichen Bereitstellungsmodellen. Und ja, es lohnt sich.

Erfolgreiche Continuous Delivery ist davon abhängig, dass du deine Release-Branches blitzsauber hältst.

Als Beispiel: Bei Atlassian gibt es zwischen allen Entwicklungsteams die Vereinbarung, dass niemals etwas direkt auf den Main- oder den stabilen Branches committet wird. Alle arbeiten an Branches. Tatsächlich haben wir ein so gutes Gefühl beim Branching, dass wir für jeden Jira-Vorgang, den wir angehen, einen separaten Branch erstellt haben – mehr dazu in Kürze.

Die Nutzer können also so viele Tests kaputt machen und in ihren Branches so viel Schaden anrichten, wie sie möchten! Der Main bleibt in einem Release-bereiten Zustand, wo du neue Branches ganz ohne vererbten, fehlerhaften Code erstellen kannst. Das ist ein Vorteil für CD und die allgemeine Entwicklerproduktivität (ganz zu schweigen von der Moral).

Branches helfen dir dabei, Beiträge von außerhalb des Teams anzunehmen

Da in Git Branching möglich ist – und insbesondere auch das Abspalten ganzer Repositorys – wird es einfach, Beiträge von Personen außerhalb des unmittelbaren Teams anzunehmen: von Auftragnehmern, Entwicklern aus Partnerunternehmen, Entwicklern aus anderen Geschäftsbereichen usw. Du musst dir nicht mehr den Kopf darüber zerbrechen, dass andere, die mit deiner Codebasis nicht vertraut sind, einfach so Änderungen an wichtigen Branches vornehmen und dadurch verhindern, dass du neuen Code ausliefern kannst.

Gründliches, automatisiertes Testen der von ihnen erstellten Branches oder abgespaltenen Repositorys ist auch hier wieder der Schlüssel zu gelungener Zusammenarbeit. Du solltest ihre Builds und Testergebnisse überprüfen, bevor du Merges in deiner Hauptcodezeile genehmigst.

Profitipp: Repository-Manager wie Bitbucket ermöglichen es dir, Git-Hooks zu nutzen, um Qualitätsstandards durchzusetzen. Du kannst zum Beispiel eine Regel festlegen, dass alle Branch-Builds bestehen müssen, bevor ein Pull-Request angenommen und zusammengeführt werden kann.

Korrektes Branching = Übersichtliche Projektverfolgung

Der aktuelle Trend: Erstelle einen Entwicklungs-Branch für jede Story, jeden Bugfix oder jede Task (d. h. jeden Jira-Vorgang), die du ausführst. Bei Atlassian haben wir dieses Branch-pro-Vorgang-Modell vor einigen Jahren eingeführt und wir haben es nicht bereut! Es erfreut sich auch bei unseren Kunden großer Beliebtheit.

Einen Branch für jeden Vorgang zu erstellen macht es einfach, die Änderungen auszuwählen, die zur Produktion ausgeliefert oder in ein Release-Bundle gepackt werden sollen. Da du auf dem Main nicht massig Änderungen anhäufst, kannst du auswählen, was in den Main kommt und was nicht. Du kannst das MVP (Minimum Viable Product) eines Epics plus ein nettes Extra ausliefern, anstatt zu warten, bis alle Extras fertig sind. Oder du lieferst einen einzigen Bugfix aus, und das im Rahmen eines regulären Releases. Selbst wenn der Fix dringend ist, musst du dich nicht mit dem Zurücksetzen anderer Änderungen stressen, die noch nicht bereit zur Auslieferung sind, nur um diese eine Änderung herauszugeben.

Und diese bequeme Auslieferung einer einzigen Codeänderung ist die Quintessenz der Continuous Delivery.

Dieser Ansatz hält nicht nur ungeprüften Code vom Main fern. Wenn der Name des Branches den entsprechenden Jira-Vorgangsschlüssel und den Namen oder die Initialen des Entwicklers enthält, ist der Entwicklungsstand von jedem aktuell bearbeiteten Vorgang absolut eindeutig.

Bitbucket Commits Git Repository Screenshot | Atlassian CI/CD

Beachte die im obigen Bild verwendete Namenskonvention: der eindeutige Schlüssel für den ausgeführten Jira-Vorgang plus eine kurze, für Menschen lesbare Beschreibung, worum es bei diesem Vorgang geht. Als Release-Manager oder Stakeholder kannst du beim Betrachten des oben gezeigten Repositorys auf den ersten Blick erkennen, dass die User Story, verfolgt von AMKT-13952, bereit für den Release ist, da du siehst, dass der Merge zum Main hier bereits durchgeführt wurde. Das ist Nachvollziehbarkeit ohne den ganzen manuellen Aufwand.

Also wie funktioniert der Workflow mit Git + Continuous Delivery?

Gut, dass du fragst! Ich werde hier nur kurz darauf eingehen, denn in anderen Artikeln auf dieser Seite werden die einzelnen Phasen ausführlicher beschrieben.

  • Erstelle einen Branch für jeden Vorgang, an dem du arbeiten möchtest. Füge den Jira-Vorgangsschlüssel in den Namen des Branches ein, damit klar ist, wofür der Branch gedacht ist. Und wenn du andere Atlassian-Tools wie Bitbucket oder Bamboo verwendest, werden sie diesen Vorgangsschlüssel aufgreifen und Verknüpfungen zwischen dem Vorgang, deinem Branch, all deinen Commits, deinen Builds, deinen Pull-Requests und deinen Bereitstellungen erstellen, die mit diesem Vorgang zusammenhängen. Anders ausgedrückt: Vorgangschlüssel sind fantastisch.
  • Nimm Änderungen an den Branches vor. Du bist hier in deiner eigenen kleinen Welt, also tobe dich aus. Probiere Neues aus. Mach Dinge kaputt. Es spielt keine Rolle, denn du wirst auch ...
  • Deinen Branch unter CI stellen. (Bamboo macht das übrigens automatisch für dich). Du und dein Team entscheiden, ob hier spezielle Tests wie Belastungstests oder UI-basierte End-to-End-Tests durchgeführt werden, und ob jedes Mal, wenn Änderungen in deinen Branch gepusht werden, automatisch ein Testlauf durchgeführt wird. Die Teams bei Atlassian führen im Allgemeinen Tests auf Unit- und Integrationsebene für Entwicklungs-Branches durch. Sie lassen die Entwickler entscheiden, wie oft diese ausgeführt werden sollen, um Build-Ressourcen zu sparen und sicherzustellen, dass die Warteschlange nicht unnötig verstopft ist.
  • Aktualisiere deinen Branch regelmäßig mit der neuesten Version des Main. Du kannst dafür "rebase" oder "branch-commit" verwenden. Das ist ganz dir überlassen. (Aber denk daran, einen Branch, den du mit anderen Entwicklern teilst, nicht zu rebasen – davon kriegen sie schlechte Laune.) So oder so, du wirst vor dem Mergen Integrationskonflikte finden, was dir wiederum hilft, den Main sauber zu halten.
  • Erstelle einen Pull-Request, wenn du bereit für den Merge bist. Das bedeutet, die Implementierung ist abgeschlossen, du hast Änderungen von deinen Teamkollegen miteinbezogen sowie alle entstandenen Konflikte gelöst und dein Branch besteht alle Tests.
  • Führe den Merge durch und stelle nach Belieben bereit. Einige Teams ziehen es vor, jede Änderung automatisch auszuliefern, sobald der Merge mit dem Main durchgeführt wurde und alle Tests dort bestanden wurden – das Modell von Continuous Deployment. Andere Teams ziehen es vor, selbst zu entscheiden, welche Änderungen wann ausgeliefert werden sollen. Das liegt ganz an dir und deinem Team.

Git CI-Automatisierung | Atlassian CI/CD

Also: Branching-Workflows machen Continuous Delivery einfacher und Git macht Branching unkomplizierter. Lies weiter, um mehr darüber zu erfahren, wie du dein Git-Repository CD-freundlich einrichtest und all diese Ideen mithilfe der Tools von Atlassian in die Praxis umsetzen kannst. Wir sehen uns auf der nächsten Seite!