Dokumentation von Null auf Hundert

Wer fünf Entwickler*innen fragt, was Legacy Code genau ist, bekommt in der Regel zehn Antworten. Manche machen es am Alter des Codes fest, andere am Alter des Techstacks, wieder andere an der Qualität. Eine der interessantesten Antworten, die ich gehört habe, war “Code, der nicht dokumentiert ist und den keiner der Entwickler mehr versteht”. Ob das die beste Definition ist, ist streitbar; Klar ist aber, dass fehlende Dokumentation ein sehr verbreitetes Problem ist.

So ging es auch uns: Zwischen immer neuen Kundenanforderungen, Refactorings und Weiterentwicklungen ist die Dokumentation durchgängig vom Tisch gefallen. Schließlich standen wir also vor unserem 300kLoC Monolithen, bei dem die Hälfte des Codes von Entwicklern stammt, die inzwischen nicht mehr an dem Projekt beteiligt sind, und niemand hat mehr vollständig durchgeblickt. Über die Jahre haben wir verschiedene Anläufe gestartet, das System zu dokumentieren, und zeigen euch in diesem Artikel, wie es bei uns geklappt hat.

Messbarkeit

“Wir brauchen Dokumentation” ist schnell gesagt, aber fühlt sich oft wie eine riesige, unmögliche Aufgabe an. Unsere ersten Versuche, das Projekt durchzudokumentieren, sind alle an diesem Problem gescheitert: Wann weiß ich, dass alles ausreichend dokumentiert ist? Wo soll ich überhaupt anfangen?
Deshalb die erste Lektion: Stellt ein greif- und messbares Ziel auf. Das kann relativ arbiträr sein, wichtig ist, dass es grob “das ganze System” abdeckt, ohne allzu sehr ins Detail zu gehen. Zwei Beispiele aus unserer Arbeit:

- “Jede Tabelle in unserer Datenbank hat einen Tabellenkommentar”
- “Jeder Controller in unserer MVC-Architektur kann auf eine Handbuchseite verweisen, in der seine Funktionalität erklärt wird”

Plötzlich ist die Aufgabe dann nicht mehr “Dokumentier' mal dieses Riesensystem durch”, sondern “Hier haben wir 150 Tabellen/Controller/…, die aktuell undokumentiert sind”. Das macht den Anfang einfacher ("Ich fange mal mit den Tabellen von A bis D an") und gibt euch ein schönes Ziel, auf das ihr hinarbeiten könnt. Zusätzlich hat das den Vorteil, dass es die Zusammenarbeit erleichtert: Der*die Entwickler*in, der*die das Projekt anstößt, kann den Kolleg*innen klar abgegrenzte Aufgaben geben. "Kannst du bitte für die Tabellen A, B und C Kommentare schreiben? Du kennst dich damit besser aus" ist immer eine bessere Anforderung als “Kannst du bitte diesen Bereich dokumentieren?”

Style Guides

Ohne klare Anweisungen wird jede*r Entwickler*in andere Dokumentation schreiben.
Bevor ihr anfangt, zu schreiben, stellt euch die Fragen:

- Für wen ist die Dokumentation? (Für Benutzer*innen? Entwickler*innen? Manager?)
- Wie detailliert soll die Dokumentation sein? (Reicht ein grober Überblick über die Features? Muss dokumentiert werden, wie die Features funktionieren?)

Wenn die Dokumentation an Externe (bspw. Benutzer*innen) geht, kann es auch nicht schaden, einen konsistenten Schreibstil zu etablieren.

Es kann ausreichen, diesen Style Guide implizit zu machen: Der*die Entwickler*in, der*die die Dokumentation startet, macht sich vorher Gedanken dazu und schreibt die Dokumentation entsprechend, und alle anderen orientieren sich an diesem Beispiel. Je nachdem kann es aber auch hilfreich sein, einen explizit geschriebenen Style Guide zu haben; Erfahrungsgemäß ist dies insbesondere dann nützlich, wenn auch der Schreibstil einheitlich sein soll.

Der Big Bang

Jetzt, da das Ziel der Dokumentation klar ist, führt kein Weg am größten Schritt vorbei: Das Ganze muss einmal von null auf geschrieben werden. Das initiale Schreiben wird einiges an Entwicklerressourcen in Anspruch nehmen und sollte entsprechend so gelegt werden, dass alle zugehörigen Entwickler*innen die nötige Zeit haben, bspw. während dem Sommerloch.

Erfahrungsgemäß funktioniert der Big Bang am besten, wenn zuerst ein*e Entwickler*in (ideal der*diejenige, der*die am meisten Silowissen mit sich herumträgt) anfängt und möglichst viel schreibt, bevor die verbleibenden Bereiche an die anderen Entwickler*innen übergeben werden.

Die initiale Dokumentation ist fertig, wenn eure zuvor gesteckten Ziele erreicht sind; Damit habt ihr den schwersten Teil hinter euch.

Überalterung verhindern

Mit dieser einmaligen Arbeit ist es aber nicht getan: Solange eine Software aktiv entwickelt wird, muss auch die Dokumentation aktuell gehalten werden. Wenn Programm und Dokumentation nicht mehr auf dem gleichen Stand sind, wird letztere bestenfalls nutzlos und schlimmstenfalls kontraproduktiv. Die beiden nachträglich wieder zu synchronisieren erfordert eine zweite große Schreibaktion, die sogar noch anstrengender als die erste sein kann. Wir haben inzwischen einige Methoden, um zu erzwingen, dass die Dokumentation synchron gehalten wird:

Versionierung

Code und Dokumentation müssen in der gleichen Versionierung leben, also z.B. im gleichen Git-Repository liegen.

Wir hatten unsere Dokumentation ursprünglich in einem separaten Repo gehalten, mit der Erwartung, dass mit jeder Merge Request für das Hauptsystem eine Merge Request für die Doku einhergeht. Das kann funktionieren, wird aber in der Praxis häufig vergessen; Besonders bei kurzfristigen Änderungen fällt die Dokumentation schnell unter den Tisch. Zusätzlich müssen Reviewer*innen immer daran denken, beide Merge Requests parallel zu reviewen; d.h. wenn eine der beiden noch Änderungen erfordert, darf auch die andere noch nicht akzeptiert werden.

Auch führen separate Repos zu Problemen, sobald verschiedene Branches ins Spiel kommen; Entweder die Branch-Struktur des Hauptrepositories wird aufwändig im Doku-Repository nachgebaut oder es tritt regelmäßig die Frage auf, auf welchem Stand die Dokumentation denn jetzt ist.

Beides in einem Repository zu bündeln, vereinfacht diesen Prozess: Solange mit jeder Merge Request Code und Dokumentation beide aktualisiert werden, sind sämtliche Synchronisationsprobleme vom Tisch.

Automatisierung

Hier kann aber die nächste Stolperfalle auftreten: Damit das funktioniert, müssen Entwickler*in und/oder Reviewer*in trotzdem weiter selbst daran denken, diese Regelung einzuhalten. Dabei hilft die Automatisierung: Wir prüfen z.B. bei jeder Merge Request, ob auch das Handbuch mit angepasst wurde. Der zugehörige Job in unserem Gitlab-CI ist wirklich simpel:

 

check-updated-manual:
 stage: lint
 # Es kann auch legitime Änderungen geben, die keine Handbuch-Änderung brauchen; Daher soll das nicht direkt die ganze Pipeline aufhalten
 allow_failure: true
 rules:
   # Wenn im Handbuch etwas geändert wurde, wird der Job übersprungen
   - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
     changes:
       - manual/*
     when: never
   # Wenn es sich um eine Merge Request handelt und die obige Regel nicht gegriffen hat (d.h. im Manual wurde nichts geändert), soll der Job laufen
   - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
   # Wenn wir uns nicht in einer Merge Request befinden, greift keine Regel und der Job läuft nicht.
 script:
   - echo "Manual has not been updated!"
   - exit 1

 

Mit weiteren Tools kann auch abgeprüft werden, dass bspw. weiterhin alle Tabellen Datenbankkommentare haben. Ideal kontrolliert ihr so automatisch, dass das messbare Ziel aus Schritt 1 weiter eingehalten wird.

Fazit

Blicken wir zurück: Wir haben uns ein klares Ziel für die Dokumentation gesetzt, einmal in einem großen Schwung die Dokumentation auf diesen Stand gehoben und anschließend mithilfe des uns verfügbaren Toolings sichergestellt, dass die Doku inkrementell aktuell gehalten wird. Hoffentlich helfen diese Schritte und Methoden auch euch, eure Erfahrung als Entwickler zu verbessern.
 

Christina Reichel

11.02.2026

Christina Reichel