Zum Hauptinhalt springen
Software Engineering DevOps

Conventional Commits und Semantic Versioning

Daniel Schock
Abstrakte Versionierung

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
Workflow von Conventional Commits zu Semantic Versioning
Der automatisierte Workflow: Von Commits zu Releases

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:

  1. Analyse: Alle Commits seit dem letzten Git-Tag werden gelesen und nach Conventional Commits geparst.
  2. Versionierung: Aus den Commit-Typen wird die nächste Version abgeleitet (Patch, Minor oder Major).
  3. Changelog: Ein Changelog wird automatisch aus den Commits generiert.
  4. 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
Terminal-Ausgabe eines automatischen Semantic Release
Semantic Release in Aktion — automatisch generiertes Changelog

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.

Daniel Schock

Daniel Schock

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.

Artikel teilen