Von Conventional Commits zum Changelog
5 Min. Lesezeit aktualisiert am
Conventional Commits geben einem Changelog drei Dinge geschenkt: den Typ jeder Änderung, den Teil des Systems, den sie betrifft, und ob sie etwas kaputt macht. Mehr geben sie nicht. Formulierung, Gruppierung und Auswahl, also der Changelog selbst, bleiben völlig offen, und eine Pipeline, die etwas anderes vorgibt, liefert ein formatiertes Git-Log aus.
feat(exports): add CSV column selection
fix(auth): reject expired refresh tokens
chore(deps): bump node-pg to 8.11
Drei Commits im Format Conventional Commits. Daraus kann eine Maschine ablesen, dass einer ein Feature ist, einer ein Fix, einer Hausputz, und welchen Teil des Systems jeder betroffen hat. Das ist echt nützlich, und es ist das ganze Versprechen der Konvention: eine Commit-Historie, die von etwas anderem als einem Menschen gelesen werden kann. Der Fehler ist zu denken, das ergäbe schon einen Changelog. Es ergibt das Rohmaterial.
Was gibt die Konvention vor?
Einen Typ, einen optionalen Scope und eine Beschreibung: type(scope): description. Übliche Typen
sind feat, fix, chore, docs, refactor, test, perf, build, ci. Zwei Dinge markieren
einen Breaking Change: ein ! vor dem Doppelpunkt, oder ein BREAKING CHANGE:-Footer. Werkzeuge
richten sich nach feat und fix für Minor- und Patch-Versionssprünge, und nach dem
Breaking-Marker für Major.
| Der Commit gibt | Der Changelog braucht | Wer die Lücke füllt |
|---|---|---|
feat / fix / chore | Added / Fixed / intern | Ein Mapping, automatisch |
(scope) | Eine Gruppierung, die die Leserin erkennt | Ein Mensch, einmal pro Scope |
! oder BREAKING CHANGE: | Wer bricht, bis wann, und was zu tun ist | Ein Mensch, jedes Mal |
| Die Beschreibung, für eine Reviewerin geschrieben | Das Ergebnis, für eine Kundin geschrieben | Ein Mensch, jeder Eintrag |
| Ein Commit | Eine Änderung, die viele Commits sein kann | Squash-Regeln, oder ein Mensch |
Der Marker sagt es dem Werkzeug; er sagt es nicht dem Aufrufer, was Thema von Wie man eine API abkündigt und Was ist ein Breaking Change ist. Es ist eine kleine Spezifikation, und sie zu befolgen lohnt sich auch, wenn man nie etwas daraus generiert, weil sie eine Entscheidung pro Commit erzwingt: Ist das eine Änderung, die Nutzer sehen, oder nicht.
Wo hören Conventional Commits auf?
Sie hören beim Satz auf. Alles, was die Konvention erfasst, ist Metadaten über eine Änderung; die Änderung selbst wird noch im Wortschatz einer Reviewerin beschrieben.
Commit-Nachrichten sind für Reviewerinnen geschrieben. fix(auth): reject expired refresh tokens ist korrekt und sagt einer Kundin nichts. Die Leserin eines Changelogs will “du wirst
abgemeldet, wenn eine Sitzung wirklich abgelaufen ist, statt sporadische 401er zu sehen”.
Scopes sind intern. exports, auth, ingest sind Modulnamen. Sie sind stabil, was sie gut
zum Gruppieren macht, und bedeutungslos für alle außerhalb der Codebasis.
Eine Änderung sind oft mehrere Commits. Ein über elf Commits gemergtes Feature erzeugt elf Einträge, zehn davon Rauschen, und das Zusammenquetschen, um das zu verbergen, verliert die Review-Historie.
chore ist ein Auffangbecken, keine Kategorie. Abhängigkeits-Updates, CI-Änderungen und
Umbenennungen landen alle dort, und manche davon zählen für Nutzer, die meisten nicht.
Also: Die Konvention gibt Typ, Scope und Breaking-Status geschenkt und lässt Formulierung, Gruppierung und Auswahl völlig offen. Diese drei sind der Changelog. Wem gehört ein Changelog-Eintrag wirklich behandelt, wer diese Formulierung, Gruppierung und Auswahl übernehmen sollte, da die Konvention selbst keine Meinung dazu hat.
Wie erzeugt man einen Changelog aus Conventional Commits?
In zwei Schichten, und die zweite muss verpflichtend sein.
Schicht eins, automatisch. Beim Merge einen Entwurfseintrag aus dem Commit ableiten: Typ auf
einen Changelog-Typ gemappt (feat auf Added, fix auf Fixed, ein Breaking-Marker auf Changed
plus Flag), Scope als Metadaten statt als Text behalten, Link zum PR. In den Unreleased-Abschnitt
einordnen, den Keep a Changelog verlangt.
Schicht zwei, menschlich, und verpflichtend. Bevor ein Release rausgeht, bekommt jeder Entwurfseintrag entweder eine einzeilige Umformulierung im Wortschatz der Nutzerin, oder wird als intern markiert und aus der öffentlichen Ansicht entfernt. Das ist der Schritt, den Leute überspringen wollen, und ihn zu überspringen erzeugt genau die Changelogs, die wie ein Diff lesen.
Das wichtige Design-Detail ist, dass Schicht zwei in der Pipeline nicht optional ist. Kann ein Release mit unbearbeiteten Entwürfen geschnitten werden, wird es das, in der Woche, in der alle beschäftigt sind. Welche Schritte der Maschine gehören und welche dem Menschen, ist der ganze Inhalt von Changelog-Automatisierung.
Das Release zu schneiden ist auch der Moment, in dem ein Git-Tag, ein Release und dieser Changelog-Eintrag entweder zusammenpassen oder anfangen auseinanderzudriften; Git-Tags, Releases und dein Changelog zeigt, wie man die drei synchron hält.
Drei Fallen
Squash-Merges fressen die Footer. Wenn eure Plattform mit dem PR-Titel als Nachricht
zusammenquetscht, verschwindet der BREAKING CHANGE:-Footer eines Commits aus diesem Branch, und
eure Werkzeuge sehen den Breaking Change still nicht mehr. Prüft, was eure Squash-Vorlage
tatsächlich behält.
Revert-Commits erzeugen Phantomeinträge. Ein fix, der am nächsten Tag zurückgenommen wird,
erzeugt einen Eintrag für etwas, das nie ausgeliefert wurde, es sei denn, die Ableitung gleicht
Reverts ab. Die meisten Werkzeuge tun das nicht.
Versionssprung und Changelog geraten außer Takt. Wird die Version aus Commits berechnet und der Changelog danach von Hand geschrieben, driften sie innerhalb von etwa zwei Releases auseinander. Berechnet beides im selben Durchgang oder akzeptiert, dass eines davon falsch ist.
Wenn du den mechanischen Teil ohne Pipeline willst
Unser Changelog-Generator macht den Ableitungsschritt im Browser: Commits einfügen, gruppierte, typisierte Einträge raus. Er ist bewusst deterministisch und komplett clientseitig, sodass die eingefügten Commits nie deinen Rechner verlassen, was zählt, wenn die Nachrichten aus einem privaten Repo stammen. Er macht die Sammel-Hälfte ehrlich und versucht sich nicht an Schicht zwei, weil Schicht zwei eine Ermessensfrage ist und ein Werkzeug, das das vortäuscht, genau den Changelog erzeugt, gegen den dieser Artikel argumentiert.
Für die Pipeline-Version deckt Changelog-Tools ab, was es gibt.
Die Zusammenfassung
Conventional Commits beantworten “was für eine Art Änderung ist das” zuverlässig und günstig. Sie beantworten nicht “was sollen wir den Leuten sagen”, und keine Menge Werkzeug oben auf der Commit-Nachricht wird das tun, weil die Information nie in der Commit-Nachricht war. Plant die Umformulierung ein.
FAQ
Erzeugen Conventional Commits automatisch einen Changelog? Sie erzeugen automatisch einen Entwurf: typisierte, mit Scope versehene, verlinkte Einträge. Die Formulierung für eine Kundin, die Gruppierung und die Entscheidung, was wegfällt, braucht immer noch einen Menschen, und eine Pipeline, die diesen Schritt überspringt, veröffentlicht Commit-Nachrichten.
Welche Conventional-Commit-Typen erscheinen in einem Changelog?
feat und fix immer, als Added und Fixed. perf meist, als Changed. chore, docs,
refactor, test, build und ci sind standardmäßig intern und erscheinen nur, wenn ein Mensch
einen davon befördert.
Wie markieren Conventional Commits einen Breaking Change?
Ein ! nach Typ oder Scope (feat(api)!: ...), oder ein BREAKING CHANGE:-Footer im Commit-Body.
Beides geht verloren, wenn ein Squash-Merge nur den PR-Titel behält.
Braucht man Conventional Commits, um einen Changelog zu automatisieren? Nein. PR-Labels, PR-Vorlagen und Issue-Links tragen dieselben Metadaten für Teams, die per Pull Request mergen. Conventional Commits sind die günstigste Option, wenn die Änderungseinheit der Commit ist.
Die technischen Aussagen in diesem Artikel wurden nicht unabhängig geprüft. Wenn etwas nicht stimmt, sagen Sie es uns, und wir korrigieren es.