Sinn für die Versionsnummer

Semantic Versioning

Oliver Fischer
©shutterstock.com/Sashkin

Versionsnummern sind wohl noch eine der letzten Bastionen, in den sich Softwareentwickler ungehindert austoben können. Es gilt weithin: Höher ist besser. Wer wochenlang an einem Projekt gearbeitet hat, der sollte auch mit einer möglichst hohen Versionsnummer belohnt werden. Oder vielleicht doch nicht?

Noch bevor ich einen eigenen Computer hatte, besaß ich ein FORTRAN-Buch. Bücher über Computer waren in der DDR leichter zu bekommen, als Computer selber. Trotz des Buches habe ich nie auch nur eine Zeile FORTRAN geschrieben. In Erinnerung ist mir nur die Aussage geblieben, dass, wenn mehr als zehn Prozent des Quelltexts eines Programmes geändert wurden, eine neue Hauptversionsnummer gerechtfertigt ist. Muss also nur genügend Code geändert und hinzugefügt werden, um eine neue Versionsnummer zu rechtfertigen? Sind Versionsnummern nur ein quantitatives Maß? Sicherlich nicht, denn ein vollständiges Reformatieren von Code rechtfertigt sicherlich keine neue Version oder ein Release, selbst wenn einhundert Prozent des Codes geändert wurden. Ehrlich gesagt, denke ich, dass der Autor bei dieser Aussage auch eher an geänderte Anweisungen und damit an Funktionsänderungen gedacht hat, als nur an geänderten Text. Und so halten wir es ja auch. Ändern, Versionsnummer hochsetzen und veröffentlichen. In der Praxis hat sich dafür in den meisten Fällen eine Troika aus Major– und Minorversion sowie Patchlevel etabliert. Steht eine Software beispielsweise in den Versionen 1.1.3 und 1.1.7 zur Verfügung, dürften die meisten Entwickler eher die Version 1.1.7 wählen. Es gilt: Höher ist besser. Wir setzen automatisch voraus, dass die neue Version neue Funktionen und weniger Fehler beinhaltet, als die vorherige. Aber eigentlich bedeutet 1.1.7 nur, dass diese Version nach 1.1.3 kommt und vermutlich drei Versionen dazwischen liegen.

Versionsnummern sind damit eigentlich, wenn der berechtigte marketing-technische Aspekt außer Acht gelassen wird, sinnlos, auch wenn wir immer versuchen ihr Sinn zu geben. Patchlevel geändert? Ah, Fehler behoben. Minorversion erhöht? Oh, das kann mehr. Majorversion geändert? Diese Version löst alle Probleme!

Das Patchleveländerungen vorrangig für Fehlerbehebungen stehen, ist allgemein akzeptiert und praktiziert, doch enthalten solche Versionen meistens auch kleinere Änderungen und sei es nur, dass Abhängigkeiten aktualisiert wurden. Was jedoch eine neue Major- oder Minorversion rechtfertigt ist jedem frei gestellt selber zu entscheiden und damit im Grund willkürlich.

Sinn für die Versionsnummer

Versionsnummern gibt es aus zwei Gründen. Erstens, um festzustellen, welche Softwareversion vorliegt und damit die aktuelle Softwarekonfiguration bestimmen zu können. Dafür würde jedoch auch ein beliebiger eindeutiger Bezeichner ausreichen. Zweitens, und damit eng verbunden, um miteinander kompatible Software-Artefakte zusammenstellen zu können. Das dafür zuständige Softwarekonfigurationsmanagment ist ein mühsamer Prozess, der sowohl manuelles und automatisiertes Testen erfordert, denn woher weiß man, ob eine Version 2.3.1 kompatibel mit Version 1.3.1 ist? Und je mehr mögliche Kombinationsmöglichkeiten unterstützt werden sollen, umso höher ist der Aufwand.

An dieser Stelle setzt die Idee des Semantic Versionings von Tom Preston-Werner, einem der Gründer von GitHub, an, bei der sich die Vergabe von Versionsnummern nach der Kompatibilität mit vorherigen Versionen richtet. Semantic Versioning arbeitet ebenfalls mit einer dreiteiligen Versionsnummer, bestehend aus Major-, Minor- und Patchlevelversion, gibt aber für deren Vergaben genaue Regeln vor. Dabei lauten die drei Hauptregeln:

  1. Bei oder bei möglicherweise inkompatiblen Änderungen ist die Majorversion zu erhöhen.
  2. Bei rückwärtskompatiblen Änderungen ist nur die Minorversionsnummer zu erhöhen.
  3. Bei rückwärtskompatiblen Fehlerbehebungen ist nur das Patchlevel zu erhöhen.

Auf den ersten Blick unterscheiden sich diese Grundregeln nicht von dem bisherigen Vorgehen und dennoch folgt Semantic Versioning anderen Regeln. Angenommen, einer Software wird häufig nur neue Funktionalität hinzugefügt, die immer rückwärtskompatibel ist, dann kann diese eine Versionsnummer mit einer sehr hohen Minornummer, wie beispielsweise 1.1098.4, haben, ohne, dass sich jemals die Majornummer erhöht. Demgegenüber führt jede nicht rückwärtskompatible Änderung oder Fehlerbehebung automatisch zu einer neuen Majorversion. So können viele nicht rückwärtskompatible Änderungen zu Versionsnummern wie 1098.1.4 führen, ohne das wirklich neue Funktionalität hinzugefügt wurde. Dass eine Version 1.1098.4 mehr bietet als Version 1098.1.4 ist gefühlt ein Wiederspruch, aber entspricht dem Gedanken des Semantic Versionings.

Praktisch damit leben

Wird Semantic Versioning konsequent gelebt, ist es ein Weg, das Überleben in der Dependency-Hölle zu erleichtern. Für eine einzelne Library ist es leicht umsetzbar. Als erstes größeres Projekt im Java-Umfeld ist Apache Wicket auf Semantik Versioning umgestiegen und ließ der Version 1.5.8 die Version 6.0 folgen. Alle folgenden Wicket-Releases stellten entweder Funktionserweiterungen oder Fehlerbehebungen dar, so dass die bei Schreiben dieses Artikels aktuelle Version 6.15.0 ist.

Das Prinzip des Semantic Versioning beschränkt sich jedoch nicht nur auf einzelne Librarys, sondern kann auch auf große und komplexere IT-Landschaften übertragen werden, die über viele externe Schnittstellen, beispielsweise REST-basiert, verfügen. Hier kann jede einzelne Schnittstelle über ihre eigenen Versionsinformationen verfügen, die entweder explizit abgefragt oder von ihr selber implizit mitgeteilt werden kann.

Clients können so selbst bestimmen, ob sie zu der jeweiligen Schnittstellen-Version kompatibel sind oder nicht. Aber auch Anwendungen können nach dem gleichen Prinzip versioniert werden. Damit geht das Versprechen einher, dass eine Version durch eine neuere ersetzt werden kann, solange sich nicht die Major-Versionsnummer ändert.

Bleibt die Frage, wie sichergestellt werden kann, dass die über die Versionsnummer implizierte Kompatibilität wirklich gegeben ist. Die für die jeweilige Software geschriebenen Unit-Tests sind hier nicht hilfreich, da sie genauso wie die eigentliche Software während der Entwicklung angepasst und damit zur geänderten Codebasis gehören und jede Änderung auch sie direkt beeinflusst. Sie sind in dem was sie testen nicht stabil. Unit-Tests testen immer nur ihre jeweilige Version, können aber nicht die Kompatibilität mit vorherigen Versionen sicherstellen. Daher sollten die Kompatibilitätstests separat als eigene und von der zu testenden Software unabhängig Technical Compatibility Kits (TCK), wie sie aus dem Java Specification Request (JSR) bekannt sind, ausgelagert und ebenfalls separat versioniert werden. Das heißt, dass es zu jeder Version einer Software oder Schnittstelle ein eigenes TCK gibt. Besteht eine neue Version alle Tests, durch die für die vorhergehenden Versionen entwickelten TCK, mit denen sie entsprechend Semantic Versioning kompatibel sein sollte, gilt sie als kompatibel.

Fazit

Semantic Versioning erlaubt durch eine sich nach der technischen Kompatibilität richtenden Vergabe von Versionsnummer eine besser Verwaltung von Abhängigkeiten zwischen Software-Artefakten und Systemen, da die verwendeten Versionsinformationen eine klare Aussage über ihre Rückwärtskompatibilität und damit auch mögliche Upgradepfade trifft. Richtig und konsequent umgesetzt, lassen sich Updates schneller und mit einer größeren Sicherheit durchführen, wovon gerade agil funktionierende Organisationen profitieren.

Aufmacherbild: Numbers von Shutterstock / Urheberrecht: Sashkin

Geschrieben von
Oliver Fischer
Oliver Fischer
Oliver B. Fischer ist Senior Software Engineer bei der E-Post Development GmbH und engagiert sich in der JUG Berlin-Brandenburg.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.