Vor kurzem habe ich einen Blogbeitrag über den Beginn unserer Untersuchungen zur Git-basierten ABAP-Entwicklung hier bei Basis Technologies veröffentlicht. Das war im Februar 2020. In diesem zweiten Teil möchte ich über Fortschritte berichten und Ihnen unsere neuesten Erkenntnisse mitteilen.
Beginnen wir mit dem Kontext. Wie entwickeln wir heute unsere ABAP-basierten Produkte (ohne Git)? Derzeit verwenden wir einen recht traditionellen transportbasierten Workflow im Rahmen einer Konfiguration der Softwarelandschaft, die den meisten SAP-Benutzern vertraut sein dürfte:

Die Fixes werden regelmäßig aus dem Produktionssystem in unsere Projekt-Entwicklungsbox integriert (wir verwenden unser ActiveControl-Tool, um diesen und viele andere Transportmanagementprozesse zu automatisieren).
Die Projektarbeit wird dagegen in der Wartungs-Entwicklungsbox zusammengeführt, sobald der Test abgeschlossen ist und ein Projekt-Release fertig ist.
Solange die Möglichkeit besteht, Konflikte zwischen eingehenden (d. h. Projekt-)Änderungen und solchen zu erkennen, die lokal im Wartungs-Entwicklungssystem vorgenommen wurden (was wir mit ActiveControl tun können), funktioniert das auch gut genug. Im Grunde genommen ist dies der standardmäßige Ansatz. Auch wenn dieses Setup aus vielen guten Gründen verwendet wird, gibt es eine Reihe von Aspekten, die nicht ideal sind:
- Mehrere Entwicklungen haben dieselbe Codebasis.
- Manchmal haben sie verdeckte Abhängigkeiten, die nachgelagerte Importfehler verursachen können.
- Manchmal wirken sich voneinander unabhängige Anforderungen auf denselben Code aus.
- In den Entwicklungssystemen häufen sich im Laufe der Zeit die abgelehnten Änderungen an.
- Wir müssen für jede Version, die wir regelmäßig patchen müssen, einen oder mehrere separate Server unterhalten.
Angesichts dieser Einschränkungen – die fast jeder kennen dürfte, der ABAP-Systeme entwickelt – haben wir uns schon seit einiger Zeit um Verbesserungen bemüht, und durch ein bevorstehendes Großprojekt sind Fortschritte dringend notwendig.
Was könnten wir also noch tun? Der Rest der Welt (d. h. außerhalb von SAP) arbeitet in der Regel nach folgendem Modell:

Zur Verwaltung von Releases wird wahrscheinlich so etwas wie Gitflow eingesetzt.
Mit abapGit haben wir zwar die Möglichkeit, eine verteilte Entwicklung in SAP durchzuführen – sofern man jedem Entwickler einen unabhängigen Server zur Verfügung stellen kann. Doch aus verschiedenen Gründen denke ich nicht, dass es ausreicht, denselben Ansatz zu implementieren:
- Es gibt kein „Build“ an sich, sondern nur einen Strom von Änderungen.
- Derzeit ist nur TMS zuverlässig genug, um an die Produktion ausliefern zu können.
- Wir müssen auch das Customizing transportieren (vielerorts ein großes Problem, wir bei Basis Technologies haben damit weniger Probleme).
- Wir haben ein beträchtliches Volumen an Work in Progress (WIP) in unserer TMS-Landschaft.
Unter diesen Umständen ist das folgende Modell vielleicht das Beste, was wir in einer SAP-Umgebung erreichen können:

Die Änderungen wären jedoch so umfangreich, dass wir entschieden haben, es wäre zu ehrgeizig, alles auf einmal anzugehen. Besser wäre es, mit ein paar kleinen Schritten anzufangen. Deshalb haben wir uns für eine Hybridlösung entschieden, bei der die Entwicklung in Git (über abapGit) einen Transport generiert, der dann in unsere TMS-Landschaft integriert wird:

Nachstehend schildere ich meinen ersten Versuch mit diesem Workflow. Wir planen, vor Beginn des großen Projekts einige weitere Iterationen mit unserer derzeitigen Landschaft durchzuführen. Das Projekt selbst möchten wir lieber mit Systemen beginnen, die besser abgestimmt sind (die TMS-Entwicklungsbox enthält viele WIP/aufgegebene Änderungen).
Erste Schwäche
Nach meiner ersten Entwicklung in meinem persönlichen System „dachte“ abapGit, ich hätte auch ein Include zu einer anderen Funktionsgruppe hinzugefügt:

Das war aber nicht der Fall.
Kein großes Problem: Ich könnte es beim Commit einfach ignorieren. Das wird jedoch wesentlich schwieriger, wenn ich Dutzende von Includes anstatt nur eines bearbeiten müsste. Außerdem möchte ich, dass alles so reibungslos wie möglich abläuft.
Erster Reparaturversuch
Ich habe dieses Objekt nicht angetastet und wollte nicht, dass es wegen eines Fehlers im System geändert wird. Um das Problem zu beheben, hatte ich die dumme Idee, abapGit in meinem persönlichen Entwicklungssystem auf die neueste Version zu aktualisieren. Aufgrund der Änderungen in den Serialisierungsalgorithmen ergaben sich viele zusätzliche Änderungen (d. h. ein Flag, das einem Objekttyp hinzugefügt wurde, erscheint wie eine Änderung an jedem Objekt dieses Typs).
Glücklicherweise kann Git so etwas sehr gut reparieren. Ich habe die zentrale Git-Entwicklungsbox aktualisiert, alle Phantomänderungen festgeschrieben (commited) und war wieder bei meiner einzelnen Änderung der Funktionsgruppe.
Zweiter Reparaturversuch
Jetzt hatte ich zwar die neueste Version von abapGit – mein Problem war aber immer noch vorhanden. Als nächstes habe ich versucht, den Funktionsgruppenindex in SE80 neu zu generieren. Das hat nicht funktioniert. Im Debugger fand ich dann heraus, dass es sich nicht um einen Fehler in abapGit, sondern um einen fehlerhaften Eintrag in einer Tabelle handelte. Abgesehen davon hatte das zur Folge, dass bei zwei identischen Systemen, die aus demselben übergeordneten System geklont wurden, das Include zwar auf dem einen, aber nicht auf dem anderen gefunden wurde.
Ich gab auf und schrieb nur die relevante Änderung fest, wobei ich die Funktionsgruppe ignorierte.
Ich schätze mal, ich werde meine Entwicklungsbox aus einem Backup aktualisieren, was normalerweise ungefähr eine Minute meiner eigenen Zeit und 15 Minuten Wartezeit in Anspruch nimmt (diese Server sind Nutztiere, keine Haustiere).
Zweite Schwäche
Als ich das Git-Hauptsystem mit meinem „Zweig“ (Branch) synchronisierte (im oben erwähnten Gitflow-Artikel erfahren Sie genauer, was man unter „Branch“ versteht), erstellte abapGit einen Transport mit der gesamten Funktionsgruppe, auch wenn die Änderung nur auf einem einzigen Include erfolgte.
Das Exportieren einer Funktionsgruppe in ein anderes System ist sicherer, macht aber auch Konflikte beim Einsatz mehrerer Tracks wahrscheinlicher. Genau deshalb benötige ich diesen Transport. Deshalb habe ich ihn durch ein TOC (Transport von Kopien) ersetzt, das nur das von mir geänderte Include enthält.
Dies ist allerdings keine Option für größere Änderungen und eine kontinuierliche Integration.
Konfliktlösung
Sogar das einzelne Include, das ich tatsächlich geändert habe, hatte einen Konflikt mit einer lokalen Änderung in der TMS-Entwicklungsbox. Dieser Screenshot zeigt, was passierte, als ich den Transport mit meiner Änderung analysierte, bevor er von der zentralen Git- zur TMS-Entwicklungsbox importiert wurde:

Ich hätte das manuell beheben können, wie ich es in der Vergangenheit immer getan hatte, entschied mich jedoch dafür, stattdessen die Funktion von Git zu nutzen.
Also habe ich das fehlerhafte Include in die andere Richtung aus der TMS-Entwicklungsbox in meine persönliche Git-Entwicklungsbox importiert und diese Änderung für den aktuellen Release-Zweig festgeschrieben (ich hätte einen anderen verwenden sollen, habe aber ausnahmsweise diese Abkürzung gewählt).

Dann habe ich die Situation in meinem bevorzugten Texteditor überprüft

… und die Änderung durch Rebasing übertragen. Das brachte mich hierher:

Im Nachhinein wäre eine Zusammenführung (Git Merge) in diesem Fall angemessener gewesen, da ich einen Push erzwingen musste.
Man sollte niemals einen Push in einem gemeinsam genutzten Git-Repository erzwingen, da es für andere schwierig ist, ihre persönlichen Repos zu synchronisieren. In einem Release-Zweig wie diesem ist das OK, wenn auch keine gute Praxis. Wir werden es wahrscheinlich unterbinden, wenn wir damit live gehen.
Aus den Blame Annotations (linker Teil der Abbildung, in der für jede Codezeile angegeben ist, wer sie zuletzt geändert hat, wann und warum) geht hervor, dass beide Änderungen im endgültigen Quellcode enthalten sind:

Dann habe ich die zentrale Git-Entwicklungsbox mit dieser Revision synchronisiert und den daraus resultierenden Transport in die TMS-Hauptentwicklungsbox importiert. Ab hier geht es weiter mit dem guten alten TMS.
Es zeigte den gleichen Konflikt, aber ich ignorierte ihn, da ich wusste, dass der gemeldete Konflikt diesmal bereits zusammengeführt war.
Schließlich habe ich den currentRelease-Zweig auf diese Version erweitert (Master wird für andere Zwecke verwendet). Das war möglich, weil keine gleichzeitige Änderung vorhanden war. In einem ordnungsgemäßen Workflow sollte eine bessere Lösung gewählt werden, möglicherweise durch Merge vor dem Erstellen des Transports. Oder vielleicht auch nach der Freigabe – wir haben uns noch nicht entschieden.

Gewonnene Erkenntnisse
Wie Sie gesehen haben, sind wir tatsächlich ein paar Schritte vorangekommen. Auf unserem Weg haben wir ein paar Dinge gelernt (was auch der eigentliche Sinn dieses Experiments war). Zum Beispiel:
- Es muss immer sichergestellt sein, dass in jedem relevanten System dieselbe Version von abapGit eingesetzt wird. Synchronisieren Sie sie gegebenenfalls mit einem Zweig Ihres eigenen Repositorys. Es ist gute Praxis, sie auf dem neuesten Stand zu halten. Aber Sie entscheiden selbst, wann der richtige Zeitpunkt dafür ist! Und warnen Sie alle, bevor Sie dies tun.
- Überprüfen Sie immer den Status des Repositorys, bevor Sie mit dem Codieren beginnen, auch wenn Sie der einzige im System sind.
Das ist Teil 2 über unsere Fortschritte auf dem Weg zu einem abapGit-Workflow. Es gibt noch viel zu entdecken, und ich werde Sie weiter über unsere Fortschritte auf dem Laufenden halten. Teil 3 der Serie wird in Kürze folgen.