Release Anxiety
Wer kennt es nicht: Der Release steht an, und plötzlich wird es still im Team. Welche Version vergeben wir? Was hat sich seit dem letzten Release geändert? Hat jemand das Changelog gepflegt? Und wer drückt den Knopf?
Manuelle Release-Prozesse erzeugen Reibung — und Reibung erzeugt Angst. Die Folge: Releases werden hinausgezögert, Änderungen stauen sich auf, und wenn dann doch released wird, ist der Umfang so groß, dass das Risiko entsprechend steigt. Ein Teufelskreis, den viele Teams kennen, aber nur wenige durchbrechen.
Dieser Artikel zeigt, wie sich mit Conventional Commits und Semantic Versioning ein vollständig automatisierter Release-Workflow aufbauen lässt — vom Commit bis zum veröffentlichten Artefakt.
Das Kernprinzip: Git-History als Single Source of Truth
Die zentrale Idee ist einfach: Die Git-History enthält bereits alle Informationen, die für ein Release relevant sind. Welche Features wurden hinzugefügt? Welche Bugs behoben? Gibt es Breaking Changes? All das steckt in den Commit Messages — wenn man sich auf ein Format einigt.
Was ist Semantic Versioning?
Semantic Versioning (kurz SemVer) definiert ein Versionierungsschema im Format MAJOR.MINOR.PATCH. Die Regeln sind einfach: PATCH wird bei Bugfixes erhöht, MINOR bei neuen Features ohne Breaking Changes, MAJOR bei inkompatiblen API-Änderungen. Die Konvention schafft Verlässlichkeit — Konsumenten einer Bibliothek oder eines Artefakts können anhand der Versionsnummer einschätzen, ob ein Update risikolos ist oder Migrationsaufwand bedeutet.
Statt Versionsnummern manuell zu vergeben und Changelogs von Hand zu pflegen, leiten wir beides automatisch aus den Commits ab. Das Ergebnis: deterministische, reproduzierbare Releases ohne manuellen Eingriff.
Die Grundlage: Conventional Commits
Die Conventional Commits Spezifikation definiert ein einheitliches Format für Commit Messages:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Die wichtigsten Typen:
- feat: Ein neues Feature. Erhöht die Minor-Version.
- fix: Ein Bugfix. Erhöht die Patch-Version.
- docs: Dokumentationsänderungen. Kein Release.
- chore: Maintenance-Aufgaben. Kein Release.
- refactor: Code-Umstrukturierung ohne Verhaltensänderung. Kein Release.
- BREAKING CHANGE: Im Footer oder als
!nach dem Typ. Erhöht die Major-Version.
Beispiele aus der Praxis:
feat(auth): add OAuth2 login flow
fix: resolve null pointer in user service
feat!: redesign API response format
BREAKING CHANGE: The response envelope has changed from
{ data, error } to { result, errors }.
chore(deps): bump Spring Boot to 3.4.1
Der Autopilot: semantic-release
semantic-release ist das Werkzeug, das Conventional Commits und Semantic Versioning zusammenbringt. Es analysiert die Commit Messages seit dem letzten Release, bestimmt die nächste Versionsnummer und führt den gesamten Release-Prozess automatisch durch:
- Analyse: Alle Commits seit dem letzten Git-Tag werden gelesen und nach Conventional Commits geparst.
- Versionierung: Aus den Commit-Typen wird die nächste Version abgeleitet (Patch, Minor oder Major).
- Changelog: Ein Changelog wird automatisch aus den Commits generiert.
- Release: Die Version wird als Git-Tag gesetzt, das Artefakt veröffentlicht und das Changelog committed.
Praktische Implementierung
Die Einrichtung erfolgt in drei Schritten. Das Beispiel zeigt die Konfiguration für ein Gradle-basiertes Java-Projekt mit GitHub Actions.
Schritt 1: Version aus Datei lesen
Statt die Version hart in der build.gradle.kts zu kodieren, lesen wir sie aus gradle.properties:
# gradle.properties
version=1.0.0
Die build.gradle.kts greift darauf zu:
// build.gradle.kts
version = project.findProperty("version") as String
Schritt 2: semantic-release konfigurieren
Die .releaserc.yml definiert, was semantic-release tun soll:
# .releaserc.yml
branches:
- main
plugins:
# 1. Commits analysieren
- '@semantic-release/commit-analyzer'
# 2. Release Notes generieren
- '@semantic-release/release-notes-generator'
# 3. CHANGELOG.md schreiben
- '@semantic-release/changelog'
# 4. Version in gradle.properties aktualisieren
- - '@semantic-release/exec'
- prepareCmd: |
sed -i 's/^version=.*/version=${nextRelease.version}/' gradle.properties
# 5. Änderungen committen
- - '@semantic-release/git'
- assets:
- 'gradle.properties'
- 'CHANGELOG.md'
message: 'chore(release): ${nextRelease.version} [skip ci]'
# 6. GitHub Release erstellen
- '@semantic-release/github'
Schritt 3: GitHub Actions Workflow
Der Workflow wird bei jedem Push auf main ausgeführt:
# .github/workflows/release.yml
name: Release
on:
push:
branches:
- main
permissions:
contents: write
issues: write
pull-requests: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Install semantic-release
run: |
npm install -g semantic-release \
@semantic-release/changelog \
@semantic-release/exec \
@semantic-release/git \
@semantic-release/github
- name: Build
run: ./gradlew build
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
Fazit
Conventional Commits und semantic-release eliminieren die manuelle Arbeit aus dem Release-Prozess. Kein Diskutieren über Versionsnummern, kein manuelles Changelog-Pflegen, kein Release-Anxiety mehr.
Der Aufwand für die Einrichtung liegt bei etwa einer Stunde. Der Ertrag: jeder Merge auf main kann automatisch ein sauberes, versioniertes Release erzeugen — mit Changelog, Git-Tag und GitHub Release. Teams, die Continuous Delivery ernst nehmen, kommen an automatisierter Versionierung kaum vorbei.
Senior Consultant
Daniel Schock ist als Senior Consultant bei atra consulting im Bereich Software Engineering tätig. Er begleitet Kunden bei der Modernisierung bestehender Softwarearchitekturen und der Einführung moderner Entwicklungspraktiken.