Warum jeder Build eine eindeutige Identität braucht
Du hast gerade einen Build ausgeführt. Eine JAR-Datei erscheint im Ausgabeverzeichnis. Oder vielleicht ein ZIP-Archiv, oder eine kompilierte Binärdatei. Sie sieht aus wie jeder andere Build, den du diese Woche gemacht hast. Du kopierst sie auf einen Server, deployst sie und machst weiter.
Drei Tage später meldet jemand einen Bug in der Produktion. Du musst herausfinden, welches Artefakt tatsächlich läuft. Du checkst den Server, findest eine Datei namens app-1.0.0.jar und stellst fest, dass du keine Ahnung hast, ob das der Build von Dienstag oder der von Donnerstag ist. Beide waren als 1.0.0 gekennzeichnet. Beide stammen aus demselben Repository. Aber irgendetwas stimmt nicht, und du kannst das Problem nicht bis zum Quellcode zurückverfolgen.
Dies ist der Moment, in dem eine fehlende Artefakt-Identität zu einem echten operativen Kopfschmerz wird.
Das Problem mit nur einer Versionsnummer
Versionsnummern sind die einfachste Form der Identifikation. Sie geben dir ein grobes Gefühl dafür, womit du es zu tun hast. 1.0.0, 2.3.1, 3.0.0-beta – diese Bezeichnungen helfen Menschen, Fortschritt und Kompatibilität zu verstehen.
Aber Versionsnummern allein versagen in der Praxis schnell.
Stell dir vor, du baust heute Version 1.0.0. Morgen führst du denselben Build aus demselben Quellcode aus. Du erhältst eine weitere 1.0.0. Jetzt hast du zwei verschiedene Dateien mit derselben Bezeichnung. Welche ist in der Produktion? Welche wurde getestet? Wenn du einen Bug reproduzieren musst, welche 1.0.0 solltest du verwenden?
Die unangenehme Wahrheit ist, dass zwei Builds aus identischem Quellcode unterschiedliche Artefakte produzieren können. Eine Abhängigkeitsversion könnte sich in deinem Paketmanager geändert haben. Das Build-Tool selbst könnte aktualisiert worden sein. Die Build-Umgebung – Betriebssystem-Patches, Bibliotheksversionen, sogar die Zeitzone – kann subtile Unterschiede einführen. Gleiche Versionsnummer, anderes Artefakt.
Was eine gute Artefakt-Identität ausmacht
Eine nützliche Artefakt-Identität muss eindeutig, nachvollziehbar und dauerhaft sein. Sie sollte drei Fragen beantworten:
- Welcher Code wurde verwendet?
- Wann wurde das gebaut?
- Welcher Build-Durchlauf hat es produziert?
Der gebräuchlichste Ansatz kombiniert drei Informationen zu einer einzigen Kennung.
Build-ID
Jeder Pipeline-Durchlauf erhält eine fortlaufende Nummer. Jenkins nennt es die Build-Nummer. GitLab CI nennt es die Pipeline-ID. GitHub Actions nennt es die Run-Nummer. Wie auch immer der Name, es ist eine monoton steigende ganze Zahl, die innerhalb eines Projekts eindeutig ist. Build 142 ist immer anders als Build 143.
Aber eine Build-ID allein sagt dir nicht, welcher Code gebaut wurde. Du brauchst mehr.
Commit-Hash
Jeder Commit in Git hat einen SHA-Hash – einen langen hexadezimalen String, der den exakten Zustand des Quellcodes zu diesem Zeitpunkt eindeutig identifiziert. Wenn du die Build-ID mit dem Commit-Hash kombinierst, erhältst du etwas Mächtiges: „Dieses Artefakt stammt aus Build 142, der den Commit a3f2c9e verwendet hat.“
Wenn etwas schiefgeht, kannst du genau diesen Commit auschecken und den Code sehen, der kompiliert wurde. Kein Rätselraten, kein „Ich glaube, das war die Version, die wir verwendet haben.“
Zeitstempel
Einige Teams fügen der Identität einen Zeitstempel hinzu. Das hilft, wenn du genau wissen musst, wann der Build stattfand, besonders beim Vergleich von Artefakten über verschiedene Umgebungen hinweg. Aber Zeitstempel allein sind nicht eindeutig – zwei Builds in verschiedenen Pipelines könnten zur selben Sekunde starten.
Die Kombination aus Build-ID, Commit-Hash und Zeitstempel ergibt eine robuste, menschenlesbare Identität. So etwas wie 142-a3f2c9e-20250321T143022. Es ist nicht hübsch, aber es ist eindeutig.
So könntest du diese Identität in einer CI-Pipeline konstruieren:
BUILD_ID="${CI_PIPELINE_ID:-142}"
COMMIT_HASH="${CI_COMMIT_SHA:-a3f2c9e}"
TIMESTAMP=$(date +%Y%m%d%H%M%S)
ARTIFACT_NAME="myapp-${BUILD_ID}-${COMMIT_HASH}-${TIMESTAMP}.jar"
echo "Building ${ARTIFACT_NAME}"
# ... build steps ...
cp target/app.jar "dist/${ARTIFACT_NAME}"
Die Regel der Unveränderlichkeit von Artefakten
Sobald ein Artefakt seine Identität erhalten hat, darf sich diese Identität niemals ändern. Dies ist das Prinzip der Unveränderlichkeit (Immutability).
Ein unveränderliches Artefakt bedeutet:
- Du überschreibst niemals ein vorhandenes Artefakt.
- Du verwendest niemals eine Identität für eine andere Datei wieder.
- Du modifizierst niemals ein Artefakt, nachdem es gebaut wurde.
Wenn du neu bauen musst, erhältst du eine neue Identität. Das alte Artefakt bleibt dort, wo es ist, mit seiner ursprünglichen Bezeichnung. Das mag verschwenderisch erscheinen, aber es ist die Grundlage für Nachvollziehbarkeit.
Ohne Unveränderlichkeit wird deine Artefakt-Ablage zu einem Chaos aus überschriebenen Dateien und verlorener Historie. Du kannst nicht mit Sicherheit sagen „das ist das Artefakt, das im Staging getestet wurde“, weil jemand es möglicherweise unter demselben Namen durch eine neuere Version ersetzt hat.
Mit Unveränderlichkeit kannst du jedes Artefakt durch seinen Lebenszyklus verfolgen. Du weißt genau, welches Artefakt ins Staging ging, welches in die Produktion und welches noch auf Tests wartet. Du kannst Artefakte über Umgebungen hinweg vergleichen und bestätigen, dass sie identisch sind.
Wo leben Artefakte?
Die Identität allein reicht nicht. Du brauchst auch einen Ort, um Artefakte zu speichern, damit sie über die Build-Maschine hinaus Bestand haben.
Wenn deine Artefakte in einem Ordner auf deinem CI-Server oder deinem Laptop liegen, verschwinden sie, wenn die Festplatte bereinigt wird, die Maschine ersetzt wird oder dir der Speicherplatz ausgeht. Du brauchst ein zentrales Speichersystem, das für alle zugänglich ist, die es brauchen – Entwickler, Tester, Betriebsteams und Deployment-Pipelines.
Dieser Speicher wird als Registry bezeichnet. Es kann ein einfacher Dateiserver sein, ein dediziertes Artefakt-Repository wie Nexus oder Artifactory oder eine cloud-native Lösung wie Container-Registries für Docker-Images. Wichtig ist, dass es eine einzige Quelle der Wahrheit für alle gebauten Artefakte ist.
Wenn du eine eindeutige, unveränderliche Identität mit einer zuverlässigen Registry kombinierst, schaffst du eine lückenlose Nachweiskette für jedes von dir produzierte Softwarestück. Du kannst jede laufende Instanz zurückverfolgen zu ihrem genauen Build, ihrem Quellcode und den Bedingungen, unter denen sie erstellt wurde.
Praktische Checkliste
- Jeder Build erzeugt eine eindeutige Kennung, die Build-ID, Commit-Hash und Zeitstempel kombiniert.
- Artefakte werden niemals überschrieben oder mit derselben Identität ersetzt.
- Die Artefakt-Ablage ist zentralisiert und für alle Teams zugänglich, die sie benötigen.
- Du kannst jedes deployte Artefakt auf seinen genauen Quellcode-Commit zurückverfolgen.
- Dein Deployment-Prozess zeichnet auf, welche Artefakt-Identität in jeder Umgebung läuft.
Das Fazit
Ein Build ohne eindeutige Identität ist ein Risiko. Du kannst Produktionsprobleme nicht effektiv debuggen, du kannst nicht überprüfen, was wo läuft, und du kannst deinem Deployment-Prozess nicht vertrauen. Die Kombination aus Build-ID, Commit-Hash und Zeitstempel gibt dir eine einfache, zuverlässige Möglichkeit, jedes von dir produzierte Artefakt zu identifizieren. Mach es unveränderlich, speichere es in einer Registry, und du wirst nie wieder raten müssen, welche Version deiner Software tatsächlich läuft.