Enable javascript in your browser for better experience. Need to know to enable it? Go here.
Blogs Banner

Codeübernahme von externen Teams

Wie geht ein neu zusammengestelltes Software-Entwicklungsteam vor, das in einem definierten Zeitraum eine unbekannte Codebasis von einem anderen Team in einer anderen Stadt übernehmen soll?

In genau dieser Situation befand ich mich mit einem Team bei ImmobilienScout24. Dieser Artikel beschreibt, wie mein Team mit dieser Herausforderung umging und am Ende erfolgreich die bestehende Anwendung pflegte und erweiterte.

Die Ausgangslage

Wie genau man die Übergabe einer Codebasis von einem Team an ein anderes angeht, hängt natürlich immer von der konkreten Situation ab. In unserem Fall sah die Lage folgendermaßen aus:
  • Das bisherige Entwicklungsteam (damals vier EntwicklerInnen) saß im Ausland, befand sich aber in derselben Zeitzone.
  • Das neue Team sollte bei ImmobilienScout24 vor Ort in Berlin arbeiten. Sechs EntwicklerInnen waren geplant, mit einer ging es los. Während der Übergabe gab es im neuen Team noch viele personelle Veränderungen.
  • Es gab relativ wenig Dokumentation der bestehenden Anwendung (z. B. keine Readme-Dateien).
  • Das auswärtige Entwicklungsteam stand während der Informationsübergabe unter Lieferdruck und war daher für Fragen kaum verfügbar.
  • Zum damaligen Zeitpunkt war der Code ungefähr anderthalb Jahre alt.
  • Ein paar Worte zu Umfang und technischer Komplexität: Bei der Codebasis handelte es sich um eine Scala- und Play-Webanwendung mit rund 5.800 Zeilen JavaScript-Code und circa 8.300 Zeilen Scala-Code – also eine vergleichsweise kleine Codebasis. Die Testabdeckung war im Frontend niedrig, im Backend relativ hoch. In die Anwendung integriert waren drei Amazon-SQS-Queues, eine MySQL-Datenbank mit sieben Tabellen, sowie vier teamexterne, aber unternehmensinterne RESTful-APIs.

Zeitplan

Der abgebildete Zeitplan (Abb. 1) stellt beispielhaft dar, wie lange eine Code-Übergabe unter den beschriebenen Umständen dauern kann. Natürlich laufen nicht alle Übergaben nach genau diesem Muster ab, zumal man oft auch nicht die Gelegenheit hat, das Vorgehen in Ruhe zu planen, so wie es hier der Fall war. Dennoch ist es hoffentlich hilfreich zu sehen, wie unser endgültiger Zeitplan schließlich aussah. Wie im Zeitplan zu sehen ist, sind wir in vier Phasen vorgegangen.

Im folgenden betrachten wir, wie wir mit jedem Stadium der Übernahme umgegangen sind.

1. Einschätzung der Situation

 

Die Codebasis kennenlernen

Das Team, das den Code bisher pflegte, arbeitete gerade auf eine Deadline hin, und so waren wir zunächst auf uns allein gestellt.

Um einen Überblick über die Codebasis zu bekommen, beschäftigten wir uns als Erstes mit den Einstiegspunkten der Anwendung und arbeiteten uns dann tiefer in die Aufrufhierarchie vor. Als Einstiegspunkte dienten in diesem Fall die Web-Controller und die Message-Stream-Handler, die Nachrichten von den Queues verarbeiteten. Als Referenz machten wir uns Notizen und Skizzen der Aufrufhierarchien. Darauf aufbauend versuchten wir, Muster in diesen Hierarchien zu erkennen, u.a. anhand der Bezeichnungen und der Aufgaben der einzelnen Komponenten.

Mithilfe von Post-it-Zetteln auf einem Flipchart in unserem Teambereich sammelten wir mit simplen Mitteln Fragen an das bisherige Team. Außerdem hielten wir unser aktuelles Wissen in Form einer Basisdokumentation fest (Readme-Dateien, Diagramme usw.).

Anschließend wagten wir uns an unsere erste Story. Wir wählten dafür eine, die sich einmal quer durch die Schichten der Applikation zog, aber gleichzeitig hauptsächlich Änderungen an existierendem Code verlangte, anstatt völlig neuem Code. So gelangten wir von der theoretischen Arbeit (dem reinen Lesen des Codes) zur praktischen Anwendung.

Ergebnisse dieser Phase:

  • Verständnis von Einstiegspunkten und Datenfluss
  • Überblick über System und Abhängigkeiten (Kommunikation der Anwendung mit anderen Systemen, jeweils verantwortliche Teams der Abhängigkeiten)
  • Database Modell
  • Verständnis des Deployment-Prozesses und des bestehenden Automatisierungsgrads


Intermezzo: Checkpoint

Der frühe Start hatte uns einen Vorsprung verschafft. Nachdem wir ein erstes Gespür für die Codebasis entwickelt hatten, hielten wir kurz inne und überprüften den Zeitplan. Das bisherige Team war noch neun Wochen verfügbar. Wir überlegten: „Was haben wir bis jetzt herausgefunden? Welchen Eindruck haben wir von Code und Setup? Brauchen wir noch die vollen neun Wochen, um uns genug Wissen für die Übernahme anzueignen?“ Wir hatten das Gefühl, schon einen guten ersten Überblick zu haben, und beschlossen, eine Pause einzulegen und uns rund drei Wochen später wieder an die Arbeit zu machen.


Codebasis kennenlernen: Lessons Learned

  • Am Anfang kann es hilfreich sein, wenn man sich selbst zurechtfinden muss, anstatt von jemandem Schritt für Schritt durch das System geführt zu werden. Wenn man sich in einem begrenzten Zeitrahmen selbstständig einarbeitet, entdeckt man vielleicht etwas, das für das aktuelle Team selbstverständlich ist, und welches sie sonst nicht erwähnt hätten.
  • Hin- und Herspringen zwischen Abstraktionsebenen ist normal in dieser Phase.
  • Die folgenden Dinge waren hilfreich zum Verständnis des Codes und sind deshalb gute Tipps für EntwicklerInnen, um künftigen Codeverantwortlichen die Arbeit erleichtern:
    • Readme-Datei mit den mindestens erforderlichen Schritten zum Starten von Anwendung und Tests
    • Skript zum lokalen Starten der Anwendung in einem Schritt
    • Strukturierung des Codes nach Funktionsbausteinen, anstatt nach technischen Bausteinen (Package-Namen wie „listings“ oder „realtors“ anstatt Strukturen wie „services“, „metrics“ oder „repositories“). Das ist ohnehin eine bewährte Vorgehensweise bei der Modularisierung von Code und hilft der Leserin, durch die Codebasis zu navigieren und deren Funktionsumfang zu verstehen.
    • Überblick über alle Abhängigkeiten des Systems
    • Dokumentation der Kontrakte mit anderen Systemen durch Tests und Testdaten

2. Treffen der Teams

Bei einem Treffen mit dem aktuellen Team konnten wir im nächsten Schritt überprüfen, inwieweit unser Verständnis zu diesem Zeitpunkt der Realität entsprach. Dazu griffen wir auf unsere Dokumentation und unsere Fragensammlung zurück. Anhand unserer Diagramme und der Betrachtung des Codes versuchten wir bekannte und noch unbekannte Unwägbarkeiten aufzuspüren und zu verstehen.

Ein Schwerpunkt war hier die Historie der Codebase, nicht so sehr ihr aktueller Zustand: Warum sind die Dinge, wie sie sind? Es ist sehr schwer, den historischen Verlauf und Kontext aus dem bestehenden Code oder der heute vorhandenen Dokumentation abzulesen. Oft werden die Gründe für Entscheidungen von Teams nicht ausreichend dokumentiert. Deshalb muss man gerade diese Dinge ansprechen, solange noch Personen greifbar sind, die an der Entwicklung und an den Entscheidungen beteiligt waren.


Ergebnisse dieser Phase:

  • Korrektur des bis dahin gewonnenen Wissens, weitere Dokumentation
  • Verständnis der komplizierteren Teile des Systems, die nicht so leicht einzusehen sind
  • Liste der technischen Schulden: Welche Schulden gibt es, wie sind sie entstanden, was sind ihre Auswirkungen, mögliche Lösungen
  • Entscheidungshistorie, damit das neue Team informierter agieren kann:
    • Beispiel: „Nachdem wir dazu übergegangen waren, die Daten in diesem anderen System zu speichern, wollten wir alle Spuren der alten Vorgehensweise beseitigen. Unser Product Owner priorisierte diese Aufräumarbeiten aber herunter und setzte stattdessen die Funktion X als Priorität.“ Das neue Team kann nun verstehen, dass es zumindest keinen technischen Grund gab, diese Aufräumarbeiten nicht nachzuholen.
  • iste der bekannten Einschränkungen („Constraints“)
    • Beispiel: „Wir können für diese Sache nicht API X verwenden, weil sich die Schnittstelle in einer Weise verhält, die für unseren Anwendungsfall ungeeignet ist. Deshalb mussten wir diesen Teil des Codes so kompliziert machen und diese zusätzlichen Daten speichern.“
    • Beispiel: „Datenbankänderungen an Tabelle X können nicht automatisch implementiert werden, weil die Datenbank aktuell zu groß ist und Migrationen auf diese Tabelle eine halbe Stunde dauern.“


Treffen der Teams: Lessons Learned

  • Man kann es nicht oft genug betonen: Kontext, Historie und Entscheidungen lassen sich nicht einfach aus dem aktuellen Code ablesen. In dieser Phase sollten also Kontext und Historie im Fokus stehen.
  • Wenn sich ein Team bereits teilweise von einer Codebasis zurückgezogen hat, ist es vielleicht nicht ganz so motiviert, an der Übergabe mitzuarbeiten. Hier war das zwar nicht der Fall, es ist nicht ungewöhnlich und menschlich. Auch wenn es in dieser Situation manchmal schwierig ist, Informationen zu bekommen: Scheuen Sie sich nicht, vermeintlich „dumme“ Fragen zu stellen. Es muss Ihnen auch nicht peinlich sein, wenn Sie am System vielleicht etwas missverstanden haben. Mit „dummen“ Fragen lassen sich oft die interessantesten Teile der Code-Historie aufdecken.

3. Kick-off

Nach den ersten Vorbereitungen, an denen nur zwei EntwicklerInnen des neuen Teams beteiligt waren, organisierten wir einen eintägigen Kick-off-Workshop, bei dem auch die übrigen Teammitglieder ins Boot geholt wurden.

Anwesend waren der Product Owner, die EntwicklerInnen des neuen Teams, und in unserem Fall auch ein besonderer Gast aus einem Team, das sich um unser wichtigstes Abhängigkeitssystem kümmerte. Mitglieder des bisherigen Teams konnten aus logistischen Gründen leider nicht teilnehmen – aber das wäre natürlich ideal gewesen.

Auf der Tagesordnung standen Themen rund um das Produkt, die Technologie und den Aufbau des Teams.

Wir sprachen über die Vision und die Ziele der Anwendung, die Historie aus Produktperspektive sowie über die KPIs zur Messung des Erfolgs. Des Weiteren erhielten wir eine Demonstration der aktuellen Funktionalitäten und beschäftigten uns mit dem unmittelbar anstehenden Produkt-Backlog.

Auf technologischer Seite besprachen wir eine Übersicht auf Grundlage der zuvor verfassten Dokumentation, lernten das wichtigste Abhängigkeitssystem näher kennen und betrachteten gemeinsam Teile des Codes. 

Anschließend ging es um die nächsten Schritte und um unsere Zusammenarbeit als Team.


Kick-off: Lessons Learned

  • Es ist wichtig, die richtige Detailtiefe zu finden. Dabei ist es besser, auf einer höheren Ebene zu bleiben und lieber ein paar Dinge auszulassen als innerhalb kurzer Zeit zu sehr ins Detail zu gehen. Für Einzelheiten ist später im Tagesgeschäft noch Zeit.
  • Solch ein Workshop ist vollgepackt mit vielen Informationen in kurzer Zeit, erwarten Sie deshalb nicht, dass die Teilnehmer sich jede Kleinigkeit direkt merken können. Überlegen Sie, wie Sie in den Wochen danach Dinge wiederholen und in die tägliche Arbeit einbinden können. Eine Möglichkeit ist, für den Workshop Plakate mit Übersichten vorzubereiten, die Sie dann im Teambereich aufhängen können. So kann das Team sich immer wieder in Erinnerung rufen, was beim Kick-off-Workshop vermittelt wurde.

4. Parallele Entwicklung

In der vierten und letzten Phase arbeiteten das neue und das bisherige Team eine Zeit lang parallel an Funktionalitäten.

Wir vereinbarten, für die Dauer der parallelen Zusammenarbeit die Entwicklungsverfahren und Prozesse des bisherigen Teams genauso beizubehalten, um unnötige Konflikte zu vermeiden und uns ganz auf eine reibungslose Übergabe zu konzentrieren. Beispielsweise bevorzugte das bisherige Team die Arbeit mit Feature Branches. Das neue Team verzichtete später zwar darauf, verwendete sie aber während der parallelen Entwicklung. Einige Änderungen, die wir im Sinn hatten, sprachen wir aber zumindest kurz an. So konnten wir herausfinden, wieso manches bisher nicht oder anders gemacht wurde, teilweise mit gutem Grund.

Während wir uns bis dahin überwiegend auf den Code und seine Funktionsweise konzentriert hatten, ging es in dieser Phase viel um Releases und Deployment. Wir dachten uns kleine „Initiierungen“ für das neue Team aus, mit denen sich jeder mit dem Betrieb der Anwendung vertraut machen konnte. Zum Beispiel haben wir dafür gesorgt, dass jeder im neuen Team für mindestens ein Deployment in die Produktionsumgebung verantwortlich war. Wir mussten auch versuchen alleine ein paar Probleme in der produktiven Anwendung zu lösen, wobei wir das bisherige Team als Notfallunterstützung hinter uns hatten.

In der letzten Woche trafen sich die beiden Teams noch einmal persönlich. So konnten wir noch ein paar unbekannte Faktoren, sogenannte „unknown unknowns“ entdecken, größere technische Schulden noch ein letztes Mal miteinander durchgehen, verbleibende Fragen beantworten, und sichergehen, dass in allen Systemen die Kontaktdaten des neuen Teams aktualisiert waren (z. B. im Alerting-System).

Wiederverwendbarkeit dieses Ansatzes

Welche der genannten Techniken in einem speziellen Fall sinnvoll sind, hängt von der jeweiligen Situation und der Codebasis ab. Aber hoffentlich zeigt Ihnen dieses Beispiel eine hilfreiche Liste an Optionen auf.

DIE eine Checkliste für eine Übergabe gibt es nicht, aber die folgenden Punkte sollten bei der Planung einbezogen werden, weil sie sich auf Zeitplan und Herangehensweise auswirken können:
  • Größe der Codebasis
  • Stil der Architektur: Eine Event-getriebene, sehr asynchrone Architektur lässt sich wesentlich schwieriger durchschauen und übernehmen als eine unkomplizierte Standard-Webanwendung.
  • Geschäftskritikalität der Anwendung: Diese bestimmt, wie hoch das Risiko bei der Übergabe sein darf und wie viele Wissenslücken des neuen Teams akzeptiert werden können.
  • Größe und Stabilität des neuen Teams.
  • Verfügbarkeit des bisherigen Teams zur direkten Anweisung, Erklärung und Beantwortung von Fragen.
  • Lernstile und Erfahrung der EntwicklerInnen, die die Codebasis in der ersten Phase analysieren.
  • Fähigkeit der zuerst eingesetzten Teammitglieder, ihr Wissen an das restliche Team weiterzugeben.

Fazit

Obwohl die Zusammensetzung des neuen Teams während der Übergabe fluktuierte, hatten wir insgesamt genügend Zeit, parallel zum bisherigen Team zu arbeiten und zu lernen. Die Maßnahmen, die wir trafen, ermöglichten eine reibungslose Übergabe. Das neue Team verursachte keine außergewöhnlichen Incidents im Produktivsystem, nachdem es die Codebasis übernommen hatte.

Dass ich mich zunächst alleine mit der Anwendung auseinandersetzen musste, lag daran, dass die bisherigen Teammitglieder nicht verfügbar waren. Im Nachhinein muss ich sagen, dass dadurch einige knifflige Punkte aufgedeckt werden konnten und wir bessere Fragen stellen konnten. Ich würde allerdings nicht behaupten, dass das deshalb immer die beste Lösung ist, denn die Anfangsphase wäre mit Unterstützung durch das bisherige Team sicherlich effizienter verlaufen.

Alles in allem würde ich beim nächsten Mal wieder eine ähnliche Struktur und Herangehensweise anwenden. Ich würde aber den Zeitplan und den tatsächlichen Inhalt danach ausrichten, wie die Anwendung beschaffen und wie geschäftskritisch sie ist.

Hinweis: Die in diesem Artikel geäußerten Aussagen und Meinungen sind die der Autor:innen und spiegeln nicht zwingend die Position von Thoughtworks wider.

Bleiben Sie auf dem Laufenden mit Hilfe den neuesten Insights