Git oder SVN? Wie sich Nuance Healthcare für ein Git-Branching-Modell entschied
Matt Shelton
Developer Advocate
Matt Shelton von Nuance Healthcare ist der Verfasser des heutigen Gastbeitrags. Dies ist der erste Beitrag einer Serie zur Umstellung von Subversion auf Git in seinem Team, zu den Gründen für den Wechsel und den Schwierigkeiten, auf die sie während der Migration gestoßen sind. Matt spricht auch auf dem Atlassian Summit 2015 über dieses Thema. In dieser Serie werden alle Aspekte behandelt, auf die in seinem 30-minütigen Vortrag nicht eingegangen werden konnte. Zudem erhalten Sie zusätzlichen Kontext.
Hintergrund
Mein Team gehört zur Medizinabteilung von Nuance. Wir sind über mehrere Standorte und Homeoffices an der US-amerikanischen Ostküste verteilt und haben auch ein Büro in Pune. Unsere Aufgabe ist die Entwicklung von Java-Webservices für NLP-Lösungen[1] im Bereich Gesundheitspflege.
Unsere Services werden größtenteils von anderen Unternehmen unserer Branche, wie z. B. Anbietern elektronischer Patientenakten und Gesundheitsdatenanalytikern (und natürlich von uns selbst) genutzt. Wir verkaufen Produkte direkt an Kliniken und die Endbenutzer der Anwendungen sind Ärzte oder auch Mitarbeiter der Klinikbuchhaltung. Otto Normalverbraucher kommt mit unserer Software nicht in Berührung.
Unser Team hat schon verschiedene Produkte zum Application Lifecycle Management in Kombination eingesetzt. Zuerst haben wir Rally Enterprise mit Seapine TestTrack Pro verwendet, uns 14 Monate lang mit Rational Team Concert schwergetan und sind schließlich ganz zu den Atlassian-Produkten migriert (Jira, Confluence, Bamboo, Crucible und Bitbucket). Früher haben wir als SCM-Tool Subversion 1.4/1.5 mit ganz gewöhnlicher Trunk-/Branches/Tags-Struktur genutzt. Wir haben unsere Build-Projekte und -Abhängigkeiten eine Ewigkeit lang mit Maven verwaltet und sind vor einiger Zeit von Jenkins zu Bamboo umgestiegen, um mithilfe von Continuous Integration (CI) engere Integrationen mit Jira und die damit verbundenen flexibleren Funktionen des Build- und Deploy-Agents nutzen zu können. Alles, was wir (jetzt) verwenden, liegt aus gutem Grund hinter der Firewall[2].
Zugehöriges Material
Verschieben eines vollständigen Git-Repositorys
Lösung anzeigen
Git kennenlernen mit Bitbucket Cloud
Git oder SVN?
Wir unterstützen rund zehn Einzelprodukte aus vier verschiedenen Produktreihen und alle Produktinhaber wollen immer bevorzugt werden und schnelle Hilfe erhalten. Über diese große Nachfrage freuen wir uns und wollen uns keineswegs beklagen. Doch so müssen wir Releases zu ungewöhnlichen Zeitpunkten einleiten und mitten im Sprint die Richtung wechseln[3].
Unser Entwicklungsprozess ist manchmal wirklich chaotisch. Gespräche im Team liefen regelmäßig in etwa folgendermaßen ab:
Ich: Wir müssen 1.8.0 jetzt zur Qualitätssicherung für Regressionstests freigeben, damit Kunden-Variable x nächste Woche in die Beta starten kann. Entwickler: Ich arbeite noch an ABC-123 im Trunk und brauche noch ein bisschen. Ich: ABC-123 ist für Variable x nicht so wichtig. Wir packen es einfach in den nächsten Release. Entwickler: Aber ich arbeite schon seit Wochen daran. Es gibt keine eindeutige Stelle, an der man einen neuen Branch erstellen könnte, um einen Release einzuleiten. Ich: Dann musst du eben alle deine Änderungen manuell rausziehen. Du hast zwei Stunden Zeit dafür, ansonsten wird die Qualitätssicherung nicht rechtzeitig fertig.
Das klingt idiotisch, ich weiß. Und natürlich übertreibe ich ein bisschen, um zu zeigen, worauf ich hinauswill. Aber es war wirklich wichtig für uns, herauszufinden, wie wir Code von einem Ort vorübergehend an einem anderen Ort ablegen konnten, damit wir den Release einleiten und den Code für den nächsten Release zurückverschieben konnten[4]. Vor diesem Problem standen wir ständig.
Einige von euch denken jetzt bestimmt: "Aber Subversion unterstützt doch Branches …". Ganz richtig, und wir haben sie hin und wieder auch bei SVN 1.4 und 1.5 genutzt. Während Branching in SVN gut funktioniert, ist das Merging eine Herausforderung. SVN hat sich weiterentwickelt und damit natürlich auch verbessert. Doch wir wussten, dass da draußen für uns bessere Lösungen warteten. Als dann die Frage "SCN oder Git?" aufkam, entschieden wir uns für Git.
Randbemerkung: Wir haben uns kurz die derzeit aktuelle Version von SVN (1.8) angesehen, um zu beurteilen, ob sie leistungsfähig genug wäre, unsere Probleme zu lösen, waren aber nicht hundertprozentig überzeugt. Ein Peer-Group-Unternehmen hat eine große Perforce-Installation, die über vieles verfügt, was wir uns wünschen, aber ich konnte die Lizenzkosten hierfür einfach nicht rechtfertigen. Außerdem haben wir einen Blick auf Mercurial geworfen, doch letztendlich war die bereits vorhandene Vertrautheit mit Git in unserem Team richtungsweisend.
Ich werde das nicht beschönigen: Die Tools von Atlassian begünstigen Teams, die Git verwenden, wirklich. Andere SCMs funktionieren einwandfrei. Unsere SVN-Integration war insofern ausreichend, als sie uns mit dem Ort verbunden hat, an dem die Änderungen an einer bestimmten User Story vorgenommen wurden. Die Integrationsmöglichkeiten für Teams, die stattdessen Bitbucket[5] verwenden, sind jedoch sowohl stärker als auch natürlicher in der Benutzeroberfläche und der Entwicklungserfahrung von Jira Software – das Gleiche gilt für Bamboo.
Da ich das wusste und einige herausragende Demos auf dem Summit 2013 gesehen hatte, wollte ich das Team davon überzeugen. Alle waren einverstanden, die Lizenzen waren besorgt und der Umstellung stand nichts mehr im Weg.
Die Auswahl des richtigen Git-Branching-Modells
Nachdem der Umstieg beschlossen war, mussten wir zunächst die schwierige Entscheidung treffen, welches Git-Branching-Modell wir für unser Team implementierten. Auf der Git-Microsite von Atlassian und dieser großartigen Präsentation vom Summit 2013 werden Branching-Modelle genauer erklärt. Kurz gesagt geht es darum, wie man Branches in Git einsetzt, um den Entwicklungs-Workflow zu unterstützen.
In SVN erstellten wir Branches dann, wenn wir feststellten, dass es höchste Zeit dafür war:
- Der aktuellste Code befindet sich im
Trunk
. Releases vom Trunk werden nach dem SchemaA.B.0-{build}
durchnummeriert. - Wenn ein Fix für einen Trunk-basierten Release benötigt wird (z. B. bei einem Bug in 1.2.0-64), wird ein Branch erstellt und von diesem Branch releasen wir
A.B.C-{build}
-Releases, wobei vonC
ausgehend nach jedem ausgelieferten Release hochgezählt wird. Diese Branches funktionieren nicht mitA.B
. Außerdem sind mehrere Branches denkbar. - Außerdem taggen wir jeden Release in einem Tag-Verzeichnis.
Kurze Randbemerkung zu Versionen: Als ich vor vielen Jahren meine ersten Erfahrungen im Managen eines Entwicklerteams sammelte, war das Versionierungssystem unseres Release Engineers … Wie soll ich sagen … wirklich nicht intuitiv. Im Grunde genommen war jeder Release ein Patch des vorherigen Releases (A.B.n). Dabei wurde nicht berücksichtigt, vorher der Patch eigentlich stammte. Man musste einen Blick in das SVN-Protokoll werfen, um diesen Ursprung herauszufinden und meist auch, um die Release-Reihenfolge
zu bestimmen. Den Baum haben wir zur Orientierung ausgedruckt und aufgehängt. Außerdem verfolgten unsere öffentlichen Versionsnummern ein Schema wie 3.0, 3.1, 3.5, 4.0 etc., das für den Kunden nachvollziehbar war. Und das, obwohl mein Team wohlbemerkt Webservices bereitstellt und kein eingetütetes Produkt. Unsere APIs sind fest vereinbart. Vor ein paar Jahren habe ich die Entscheidung getroffen, dass die Builds meines Teams, und damit auch die Releases, den Regeln der semantischen Versionierung unterliegen. Ich musste mich einige Male gegen die obere Führungsebene durchsetzen, aber jetzt sind wir uns über diese Regeln einig und haben nicht mehr daran gerüttelt. Partner schätzen solch klare Festlegungen.
Ich habe bereits über ein Problem bei einem Release (nennen wir es 1.2.0
) gesprochen, als wir noch bis kurz vor Schluss an einem Feature arbeiteten. Wir mussten diesen Code rausziehen, unseren Release einleiten, den Branch branches/1.2.1
erstellen, anschließend den Code zurückmergen und hoffen, dass wir in der Zwischenzeit keine Festplattenausfälle verursacht hatten[6].
Ein ganzes Feature selbständig aus einem gemeinsam genutzten Trunk zu entfernen, ist keine schöne Sache. Jeder hat sich damit schwergetan. svn blame
kann da hilfreich sein, denn es ist ein starkes Diff-Tool, aber die Arbeit damit ist lästig. Oft habe ich es persönlich genommen und hatte das Gefühl, dass meine schlechte Planung der Grund dafür war, dass wir nicht gut genug vorbereitet waren, um unsere Arbeit rechtzeitig abzuschließen[7]. Mein Team hatte lange genug damit zu kämpfen.
Manchmal waren wir übertrieben korrekt, um uns den Ärger zu ersparen, und haben die Entwickler gebeten, einige Tage lang untätig zu bleiben (gewissermaßen ein Code Freeze, wenn man so möchte), nur damit wir den Trunk vor einem Release nicht möglicherweise noch verunreinigten.
Wir wussten also, dass wir mindestens Feature Branches benötigten. Es gibt ein einfaches Git-Branching-Modell, das sich hier eignet: Ein Main-Branch für die Produktion und Feature Branches für die einzelnen Features, Bugs usw. Die Teams müssen die Merge-Reihenfolge verwalten, um sicherzustellen, dass wir im Main das ausliefern, was für den jeweiligen Release vorgesehen ist. So haben wir im Wesentlichen vorher auch gearbeitet, auch wenn hier die Features besser voneinander isoliert sind. Doch uns fehlte es hier noch an Freiheit.
In unserer Umgebung müssen wir oft einige Versionen in der Produktion behalten und müssen möglicherweise Fehler in einem Release beheben, der 2-3 kleinere Revisionen älter ist als das, woran wir gerade arbeiten. Zusätzlich zu den Feature-Branches brauchten wir also eine Art Release-Branch oder ähnliches, in dem wir Probleme aus früheren Releases beheben konnten. Korrekturen werden in Support-Branches mit langer Laufzeit vorgenommen und dann im Branch-Stream gemergt, sodass ein Fix in alle Support-Streams aufgenommen wird.
Dieses Model sah wirklich gut aus und wir haben ein paar Prototyp-Interaktionen mit diesem Modell durchgeführt, um herauszufinden, ob es zu unseren Anforderungen passt. Die "Killer App" des Atlassian-Teams war der fortlaufende Merge einer Fehlerkorrektur in den Entwicklungs-Branch. Uns gefiel dieses Konzept zwar, aber jedes Mal, wenn wir es ausprobierten, stießen wir auf das eine oder andere Problem mit unseren Maven-Abhängigkeiten. Außerdem waren wir uns nicht sicher, ob wir grundsätzlich einen direkten Merge unserer Arbeit von einer Version in eine andere wollten. In einigen Fällen müssen wir dieselbe Fehlerkorrektur in den verschiedenen Versionen auf leicht unterschiedliche Weise implementieren, sodass ein direkter Merge nicht möglich ist.
Einige aus dem Team bevorzugten eher eine Variante dieses Models, das als "Git-flow" bekannt ist. Unter Git-flow versteht man eine Reihe an Branch-Benennungskonventionen und Merge-Richtlinien von Vincent Driessen. Diese Lösung war für das Team sehr eingängig und die Struktur gefiel uns, denn dadurch haben sich uns viele Fragen wie "Was soll ich tun, wenn wir X tun müssen?" gar nicht erst gestellt. Meist lagen die Antworten auf der Hand. Doch ich versuche besser gar nicht erst zu erklären, was Git-flow ist. Lies es einfach selbst im Tutorial von Atlassian.
Der letzte Schritt mit Git-flow bestand darin, eine Vorgehensweise für Releases zu finden, die lange in Produktion sind. Da der Main immer weitergeführt wird, kam der Git-flow-Hotfix-Workflow, den wir für einen Bugfix bei einem früheren Release genutzt hatten, nicht infrage. Andererseits brauchten wir auch nicht immer einen Support Branch.
Meistens sollte ein Hotfix, der nur den letzte Release in die Produktion patcht, ausreichen. Ein Support Branch wird nur benötigt, wenn wir weiter zurückgehen müssen oder wenn wir aus irgendeinem Grund die Kompatibilität aufrechterhalten möchten. Letzteren Use Case haben wir noch weiter analysiert und Kriterien zur Entscheidung für einen Support Branch gegenüber einem Hotfix und kleineren Versions-Upgrade aufgestellt.
1. Der Code kann nicht einfach in den Develop Branch zurückgemergt werden.
2. Der Partner/Kunde kommt mit einer Änderung der Oberfläche im neuesten Release nicht zurecht.
3. Es gibt eine interne Abhängigkeit, die nicht geändert werden kann.[8]
Die beiden Git-flow-Erweiterungspakete[9] unterstützen das Support-Branch-Konzept, das für Git-Flow ursprünglich gar nicht vorgesehen war, aber dann so beliebt war, dass es miteinbezogen wurde.
Der Workflow von Git-flow gefiel uns und die von uns benötigten Tools wurden unterstützt. Im nächsten Blogpost erfährst du, was passierte, als wir in einem POC-Projekt, das unseren Entwicklungsprozess nachbilden sollte, Git-flow einsetzen wollten. Es war … eine lehrreiche Erfahrung!
[1]: Verarbeitung natürlicher Sprache. WIR KÖNNEN DEINE GEDANKEN LESEN. (Nein. Nicht wirklich.)
[2]: Die Cloud-Angebote von Atlassian sind in vielerlei Hinsicht attraktiv, aber wir müssen vorerst die Finger fest um unsere Server und Daten legen. Wir persönlich müssen zwar nicht viel mit PHI-Daten anfangen, aber unsere Software schon. Daher ist es wichtig, sie immer so sicher wie möglich zu machen.
[3]: Pst … Verratet das bloß nicht Ken Schwaber!
[4]: Wobei der nächste Release womöglich nur wenige Tage später anstand.
[5]: Früher als Stash bekannt. Das ist der neue Atlassian-Markenname.
[6]: Mir ist klar, dass wir das alles jederzeit aus dem vorherigen Commit ziehen konnten. Ich mache nur Spaß.
[7]: Normalerweise war das nicht so. Meistens lag es daran, dass jemand anderem ein engerer Zeitrahmen gesetzt wurde und wir schnell reagieren mussten.
[8]: Darauf kann ich in meinem eigenen Blog nicht näher eingehen. Vertrau mir einfach. Ich habe meine Gründe.
[9]: Das ursprüngliche Paket von Vincent Driessen wird nicht mehr gewartet. Es gibt jedoch einen neuen Fork, der laufend aktualisiert wird.
Footnotes
[1]: Natural Language Processing. WE CAN READ YOUR THOUGHTS. (No. Not really.)
[2]: There is a lot that is attractive about Atlassian's cloud offerings, but we need to keep our fingers wrapped tightly around our servers and data for the time being. While we don't personally need to do much with PHI data, our software does and it's important to keep it as secure as possible.
[3]: Shhhh... don't tell Ken Schwaber.
[4]: Which might have only been a few days later anyway.
[5]: Formerly known as Stash. Hello, Atlassian Fall Rebranding!
[6]: I know we could always pull it out of the previous commit. I was kidding.
[7]: This wasn't usually the case - generally it was because someone else's timeframe moved up and we had to react quickly.
[8]: This is one of those things I can't get into on my own blog. Just trust me. "Reasons".
[9]: The original package by Vincent Driessen isn't being maintained any longer. A new fork, however, is regularly updated.
Diesen Artikel teilen
Nächstes Thema
Lesenswert
Füge diese Ressourcen deinen Lesezeichen hinzu, um mehr über DevOps-Teams und fortlaufende Updates zu DevOps bei Atlassian zu erfahren.