Keine Angst vor Branching und Merging
Meist nutzen wir nur einen Bruchteil der Möglichkeiten des Team Foundation Servers (TFS) im Entwickleralltag. In diesem Beitrag möchte ich näher auf Branching und Merging eingehen. Als ich an meiner ersten Stelle an der Produktentwicklung beteiligt war und mit Releases und Patches gearbeitet habe, war das ein selbstverständlicher Begriff. Ich weiss noch gut, dass ich bei jedem Merging feuchte Hände bekam. Seither habe ich hauptsächlich auf Projektbasis gearbeitet und kam meist ohne Branching und Merging aus. Allenfalls wurde Multiple Checkout aktiviert, wenn mehrere Personen an derselben Codebasis arbeiteten.
So lange also alle am gleichen Projekt, für denselben Release und im gleichen Team arbeiten, mag ein Codestrang mit Single oder allenfalls Multiple Checkout ausreichen. Sobald aber mehrere Teams am gleichen Code arbeiten müssen und Releases ins Spiel kommen, sieht es etwas anders aus. Dies war vor über einem Jahr in meinem aktuellen Team der Fall. Es gab zwei Scrum Teams und ein Kanban Team. Die Scrum Teams lieferten jeden Sprint Code aus, der in eben diesem Sprint-Zyklus verteilt wurde. Beim Kanban Team verhielt es sich so, dass alles verteilt wurde, was zu diesem Zeitpunkt abgenommen war. Da es sich beim Kanban Team nicht abschätzen liess, wann eine Story abgeschlossen sein würde, konnte es passieren, dass es Codeanpassungen gab, die noch nicht live gehen durften. Wir haben uns daher überlegt, wie wir mit diesen unterschiedlichen Teams und Release-Zyklen umgehen wollen und eine Strategie des Branching und Mergings entwickelt. Über das letzte Jahr haben wir diese Strategie verfeinert und mehrfach erprobt, so dass ich sie gerne an dieser Stelle präsentiere.
Branches
Wir haben zwei Haupt-Branches ausgemacht, je einen für
- den aktuellen Release, der gerade live ist (CurrentRelease-Branch)
- die aktuelle Weiterentwicklung (NextRelease-Branch)
Wenn Patches für ältere Releases vonnöten sind, behält man die Branches älterer Releases auf, ansonsten löscht man sie, wenn ein neuer Release live geht, respektive der neue Release Branch erstellt wird.
Auf dem Release-Strang können jederzeit akute Fehler, die auf dem System auftreten, behoben werden, während am zweiten Strang weiterentwickelt wird für den nächsten Release. Der knifflige Branch ist klar der für den nächsten Release, weil an diesem alle Teams entwickeln. Wir haben daher beschlossen, zusätzlich für jede Story einen eigenen Branch zu erstellen. Der Story-Branch wird vom NextRelease-Branch aus erstellt und auf ihm die gesamte Story entwickelt. Wenn die Entwicklung abgeschlossen ist, wird der Story-Branch auf das Testsystem verteilt und abgenommen. Erst nach der Abnahme wird der Story-Branch in den NextRelease-Branch gemerged, damit sich in diesem wirklich nur Code wiederfindet, der auch tatsächlich live gehen wird beim nächsten Release und für alle weiteren Storys als Basis dient.
Der Lebenszyklus eines Story-Branchs sieht dann wie folgt aus:
Was, wenn jetzt aber eine Story grundlegende Elemente ändert, so dass weitere Branches auf ihren Codestand angewiesen sind? Dann gilt es die Storys zu gliedern. Die, nennen wir sie Basis-Story, muss vor allen folgenden abgenommen werden. Das heisst aber auch, dass sie als Basis für alle folgenden vorausgesetzt werden kann. Der jeweilige Codestand kann daher in die anderen Story-Branches gemerged werden. Aber noch nicht in den NextRelease-Branch!
Das Mergen von einem Story-Branch zum andern ist nicht so angenehm, da der TFS nicht erkennt, dass die beiden Branches dieselbe Basis haben, da sie nicht direkt voneinander abhängen. Er vergleicht daher alle Files, checkt auch alle aus und es treten meist mehr Konflikte auf. Damit das beim normalen Ablauf nicht passiert, sollte man ein Auge auf die Vererbung von Branches haben.
Vererbung
Welcher Branch wird von welchem erstellt? Der TFS unterstützt mich am besten beim Merging des Branches zurück zu demjenigen, von dem aus er erstellt wurde. Daher haben wir in unserem Prozess die häufigsten Merging Schritte ausgemacht und uns überlegt, von welchen Strängen jeweils ein Branch gezogen werden soll.
Story-Branches werden jeweils zurück zum NextRelease-Branch gemerged, Hotfixes für das Livesystem zurück in den CurrentRelease-Branch und den NextRelease-Branch. Ansonsten tritt der Bug nach dem nächsten Release wieder auf. Merging zwischen Story-Branches gibt es nur in Ausnahmefällen bei Abhängigkeiten. Damit das Merging also jeweils angenehm verläuft und der TFS nur die Files als geändert vermerkt, die wir in dem Branch auch geändert haben, sollten alle Branches vom NextRelease-Branch aus erstellt werden. Eine Ausnahme gibt es, die Hotfixes, auf die komme ich gleich noch zu sprechen. Dabei ist der Zeitpunkt der Erstellung eines Branches entscheidend! Der CurrentRelease-Branch wird zum Zeitpunkt des Codefreeze für den Release vom NextRelease-Branch erstellt. Story-Branches hingegen, wenn eine neue Story begonnen wird.
Das heisst aber auch, dass Branches für Hotfixes vom aktuellen Release vom CurrentRelease-Branch aus erstellt und in diesen gemerged werden. Denn sie dürfen nur die Codebasis verwenden, die derzeit live ist. Nun könnte man den Hotfix-Branch einmal in den CurrentRelease-Branch und einmal in den NextRelease-Branch mergen, das macht aber wegen der Vererbung der Branches wenig Sinn. Daher sollte der Hotfix immer zuerst in den CurrentRelease-Branch gemerged und dann der CurrentRelease-Branch in den NextRelease-Branch germerged werden. So haben wir die volle Unterstützung des TFS und merken erst noch, dass vielleicht sogar Code am CurrentRelease-Branch geändert wurde, der noch gar nicht in den NextRelease-Branch gemerged wurde.
Daher Vorsicht!
Feuchte Hände hin oder her, ein bisschen Konzentration ist durchaus vonnöten beim Merging. Denn gern geht vergessen, die Änderung für den Hotfix auch in den NextRelease-Branch zu mergen. Auch bei Konflikten ist Vorsicht geboten! Nicht dass eine Änderung plötzlich wieder rückgängig gemacht wird, die von einer anderen Story kam. Ich habe mir bei einem Kollegen abgeschaut, vor jedem Checkin nochmals alle Änderungen mit der letzten Version zu vergleichen. Dabei fallen mir Fehler eher auf. Gerade beim Merging habe ich mir so schon oft den Kopf aus der Schlinge gezogen.
Der TFS kann uns jedoch noch besser unterstützen. Dazu können bspw. Regeln erfasst werden, die uns zwingen in die richtigen Branches zu mergen. Die meisten Fehler beim Branching und Merging sind Benutzerfehler. Da wir eben alle Fehler machen und vergesslich sind, sollte man so viele Fälle wie möglich mit dem Tool abdecken. Wenn der Prozess von allen verinnerlicht wird, ist das unter Umständen nicht nötig.
Auch keine Angst vor Undo und Rollback
Heute habe ich keine feuchten Hände mehr beim Merging, ich bin jetzt ein alter Hase. Aber eigentlich gibt es auch keinen Grund dazu. Schliesslich checkt Visual Studio alle geänderten Files beim Merging erst einmal aus. Sollte irgendetwas schief gelaufen sein oder aber wir ein schlechtes Gefühl beim Auflösen eines Konflikts haben, dann können wir einfach nochmals Undo Checkout drücken und eben nochmals Mergen. Ist ja nicht weiter schlimm!
Und was, wenn schon eingecheckt wurde und doch etwas nicht stimmt? Dann gibt es immer noch die schöne Funktion Rollback Entire Changeset, die vermutlich auch nicht gerade oft angewendet wird und deswegen auch feuchte Hände auslöst.
Aufräumen nicht vergessen!
So. Jetzt entwickeln wir fleissig Storys und es gibt immer mehr und mehr Story-Branches und irgendwann wissen wir selbst nicht mehr, ob die Branches noch gebraucht werden oder nicht. Dann läuft es meist so wie mit auskommentiertem Code: man löscht ihn nicht, denn er könnte ja noch gebraucht werden. Falsch!
Die Zahl der Branches sollte klein gehalten werden. Wenn der neue CurrentRelease-Branch vom NextRelease-Branch gezogen wird, sollten alle alten Story-Branches gelöscht werden, denn diese sind ja jetzt live und werden bei Bedarf auf dem CurrentRelease-Branch gefixt. Dies ist im ersten Bild zum Lebenszyklus des Story-Branches in unserem Prozess so aufgeführt. Ich habe dazu immer die aktuellen Storys auf meinem Board aufgelistet. Dort steht, ob sie schon gemerged oder gelöscht sind und dort steht auch, welche Story derzeit auf welchem System verteilt ist. Dort hängt auch immer ein Ausdruck der beiden Bilder unseres Branching-Merging-Prozesses.
Jetzt kann eigentlich nichts mehr schief gehen. Zumindest nicht, wenn man sich für die Dauer des Merging konzentriert. Branching ist ja ein Kinderspiel, sofern man vom richtigen Branch aus den neuen erstellt und sich das Leben damit auch wirklich leichter macht. Und wenn man oft genug merged, gibt es irgendwann auch keine feuchten Hände mehr.