Vergeßt nicht was war
Wenn man Programmcode nach einiger Zeit betrachtet, fragt man sich nicht nur, wie denn das jetzt funktionieren soll, sondern oft auch, warum es überhaupt da ist. Hier kann es helfen, wenn man einen Blick in die Geschichte des Codes wirft. Meist liegt der Code in einem Versionskontrollsystem und man bekommt leicht eine Übersicht über alle jemals dort abgelegten Versionen. Diese Versionen kann man dann vergleichen und feststellen, wann der betreffende Codeabschnitt erstmals auftauchte. Wenn man ganz viel Glück hat, gibt es zu jeder Version einen Kommentar, der besagt warum die Änderung durchgeführt worden ist. Man kann das Glück aber auch erzwingen, indem man es zur Pflicht macht, immer einen sinnvollen Kommentar einzufügen. Wer sich nicht daran hält, zahlt in die Kaffeekasse. Dies funktioniert übrigens nur auf diese Weise und nicht durch technische Lösungen. Wenn ein Kommentar erzwungen wird, lautet er bei vielen dann doch nur "x" oder ".".
Viele Systeme
Es gibt eine große Anzahl von Versionskontrollsystemen. Unter Unix gehörten schon immer rcs und sccs zum Lieferumfang. Später entstand dann das noch heute wohl meistgenutzte cvs daraus. Dieses wurde in letzter Zeit dann von subversion abgelöst. Warum ist subversion das bessere cvs? cvs geht davon aus, daß die Struktur der Ablage im Versionskontrollsystem der des Dateisystems entspricht. Wenn eine Datei verschoben wird, verlieft man bei cvs den Zusammenhang. Außerdem werden Löschvorgänge in cvs nicht gespeichert, während subversion Dateien, die obsolet geworden sind beim nächsten Update entfernt. Diese Eigenschaften von subversion sind extrem wichtig beim Umgang mit Java-Code, da hier die logische Struktur des Codes sich in der physikalischen Ablage im Dateisystem wiederspiegelt. Dateiverschiebungen gehören somit zum Entwicklungsalltag.
Hier sollte etwas auffallen. Wenn cvs benutzt wird, besteht immer die Gefahr, daß sich etwas lokal kompilieren läßt nur wegen Dateien, die noch an der alten Stelle herumliegen, obwohl sie eigentlich schon lange in ein anderes Paket verschoben wurden. Es ist also wichtig, immer wieder alles wegzukopieren und alle Dateien frisch auszuchecken, um nicht über solche veraltete Dateien zu stolpern (gilt auch für Microsofts VCS).
Es gibt auch eine Reihe von meist sehr teueren kommerziellen Systemen. Einige besitzen sicherlich wertvolle Möglichkeiten. Das Geld dafür lohnt aber nur, wenn gleichzeitig Geld bereit liegt, die Benutzer zu schulen, da diese Systeme in der Regel recht komplex sind. Vor allem braucht es erfahrene Administratoren.
Einfache Systeme wie cvs haben noch einen Vorteil: sie speichern in Klartext ab. Das heißt, man kann über ein Archiv mit find/grep suchen. Systeme die eine raffiniertere Ablage haben, müssen solche Suchmöglichkeiten selber implementieren.
Aus vielen Teilen wird ein Ganzes
Ein System besteht gewöhnlich aus vielen hundert Dateien. Um nicht nur die Geschichte der einzelnen Dateien verfolgen zu können, sondern des gesamten Systems, muß ein Zusammenhang hergestellt werden. Dies geschieht dadurch, daß immer dann, wenn das System gebaut wird, der verwendete Stand aller Komponenten markiert wird (Tag). Anhand dieser Markierung kann dann später immer rekonstruiert werden, wie die einzelnen Dateien aussahen, wenn eine Auslieferung des Systems erstellt wurde. Diese Markierung sollte immer über das gesamte System erfolgen, selbst wenn eine Teillieferung erfolgt. In diesem Fall müssen alle nicht auszuliefernden Komponenten in der Version ihrer letzten Auslieferung einbezogen werden. Dies klingt etwas kompliziert und ist es auch. Deshalb würde ich in der Regel empfehlen, immer nur das vollständige System zu liefern. Es ist tödlich, wenn in einer Produktionsumgebung Module laufen, die nicht jederzeit aus dem Code rekonstruiert werden können.
Verzweigungen
Ab der ersten Auslieferung geht die Entwicklung nicht mehr linear voran. Es müssen Bugs in der ausgelieferten Version behoben werden und gleichzeitig neue Anforderungen für zukünftige Versionen realisiert werden. Daher werden dann, wenn Änderungen an bereits ausgeliefertem Code durchgeführt werden müssen, Verzweigungen eingeführt. Das bedeutet, daß jetzt Änderungen nicht nur basierend auf dem aktuellen Stand gemacht werden, sondern auch aufbauend auf einem alten. Dies ist eine schwierig zu beherrschende Situation, bei der es immer wieder vorkommt, daß ein Entwickler in einem anderen Zweig arbeitet wie er meint. Daher sollten Änderungen in den Zweigen auf das nötigste begrenzt werden. Verzweigungen eignen sich überhaupt nicht dazu, die Entwicklung zu parallelisieren, indem man prophylaktisch mehrere Zweige anlegt, um sie unterschiedlichen Entwicklergruppen zur Verfügung zu stellen.
Zusammenführen von Zweigen
Änderungen in Zweigen werden meist auch in den anderen Zweigen gebraucht. Eine Zeitnahe Übernahme der Änderungen ist unbedingt zu empfehlen, denn es ist sehr zeitaufwendig, nachträglich festzustellen, welche Komponenten von einer Änderung betroffen waren. Wenn nicht nach jeder Änderung direkt die Übernahme in andere Zweige erfolgt, ist darüber penibel Buch zu führen. Hier ist sehr viel Disziplin erforderlich.
Es gibt Tools, die die Übernahme (Merge) von Code von einem in einen anderen Zweig unterstützen. Diese Tools haben alle ihre Grenzen, da die zugrundeliegenden Vergleichsalgorithmen alle auf einem Textvergleich aufbauen, bei dem meist nur ausgewählt werden kann, ob Leertext mitbetrachtet wird. Dies führt zu großen Schwierigkeiten z.B. bei Konfigurationsdateien, die die IDE im Hintergrund pflegt, aber nach dem Zufallsprinzip pflegt. Auch sind die Dateien in der Regel anders aufgebaut. Code von C-artigen Sprachen kümmert sich kaum um Zeilenumbrüche. Hier ist das Semikolon das trennende Zeichen. Bei Python wäre es aber schlecht, wenn Leertext unberücksichtigt bleibt. Hier wäre es sehr hilfreich, wenn es kontextspezifische Vergleichseditoren gäbe, die mit dem Inhalt genauso gut umgehen könnten, wie heutige Programmeditoren, die ja auch im Hintergrund die Syntax analysieren.
Erleichtern kann man sich den Codevergleich, wenn man den Code vor der Ablage immer automatisch formatiert. Leider erlauben die mir bekannten IDEs nicht, dies zu erzwingen. In der alten Unix-Zeit haben wir nie direkt die Werkzeuge aufgerufen, sondern Skripts verwendet, in denen soche Funktionen leicht eingebaut werden konnten.
Noch schwieriger wird es, wenn inzwischen ein Refactoring durchgeführt worden ist. Hier hilft es mal wieder, wenn Tests existieren. Diese sind meist wesentlich einfacher zusammenzuführen. Dann wird solange Code übernommen, bis die Tests in allen Zweigen erfolgreich sind.
Wer sich einmal bindet
Das Versionskontrollsystem wird einmal am Anfang des Projekts bestimmt. Es später zu wechseln ist nur schwer möglich, da dann meist die Historie verloren geht. Das gleiche gilt für den Umgang mit dem System. Wenn nicht von Anfang an diszipliniert gearbeitet wird, ist das später kaum zu korrigieren. Wenn es mal vergessen wurde, Zweige zusammenzuführen oder falsch verzweigt wurde, kann das kaum noch korrigiert werden. Fehlende Markierungen von Versionen sind auch nicht nachträglich zu rekonstruieren.
Es gibt wenige Dinge, die sich so schlecht später korrigieren lassen, wie die Versionsführung.
1 Kommentar:
In TFS 2008 hast Du die Chance ein Label nicht nur auf der latest version, sondern auf einem spezifischen Changeset zu definieren. Vorausgesetzt Du schaffst es, das richtige Changeset zu identifizieren, kannst Du immerhin noch nachträglich ein Label einschieben.
Wenn ein Entwickler in dieser Umgebung unbedingt Mumpitz in die Changeset-Kommentare eintragen muss, so muss zumindest stichprobenartig untersucht werden, wie diszipliniert die Entwickler sind. Das System erlaubt es dann noch, nachträglich Komentare eines Changeset zu modifizieren.
Kommentar veröffentlichen