1 Punkte von GN⁺ 5 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Conventional Commits versuchen, Commit-Messages im Format <type>[optional scope]: <description> mit Bedeutung zu versehen, stellen dabei aber den Änderungstyp in den Vordergrund und machen den Geltungsbereich optional, sodass die für die tatsächliche Navigation nötigen Informationen nach hinten rücken
  • Beitragende, Debugger und Incident-Responder suchen im Commit-Log nach den Codebereichen, die von einer Änderung betroffen sind; da Bugs bei jeder Art von Änderung entstehen können, ist der scope wichtiger als der Typ
  • Bei fix(compiler): prevent namespaced SVG <style> elements from being stripped erkennt man schon an der Beschreibung, dass es sich um einen Bugfix handelt, und refactor(core): Update webmcp support to use document.modelContext zeigt, dass ein einzelner Commit zugleich Bugfix, Refactoring und Feature-Erweiterung sein kann — der type ist also redundant und einschränkend
  • Die automatische Erzeugung von CHANGELOGs und die Ableitung semantischer Versionssprünge haben Grenzen, weil Commit-Logs und Changelogs unterschiedliche Leser haben und Reverts, versehentliche Breaking Changes oder später behobene Inkompatibilitäten das Ergebnis verzerren können
  • Commit-Messages mit scope-Präfix zeigen zuerst, was tatsächlich geändert wurde, und auch Build- und Deployment-Bedingungen sollten besser auf per git diff ermittelten geänderten Dateien basieren als auf dem Typ im Titel

Falsche Prioritäten

  • Conventional Commits verfolgen das Ziel, Commit-Messages mit Bedeutung zu versehen, damit Entwickler und Endnutzer Änderungen besser verstehen können
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
  • Die Betreffzeile besteht aus einem <type> wie fix, feat, chore, docs oder refactor, einem optionalen scope und einer Beschreibung
  • Der zentrale Mangel ist die Struktur, die den Änderungstyp über den scope stellt, also über den eigentlichen Gegenstand der Änderung
  • Dass der scope optional ist, erlaubt es, dass die wichtigste Information eines Commits fehlen kann, und dass der Typ ganz am Anfang steht, kehrt die Prioritäten um

Warum scope wichtiger ist als type

  • Beitragende lesen das Commit-Log, um Änderungen seit ihrem letzten Beitrag nachzuvollziehen, den Gesamtfluss eines Projekts zu verstehen und Commits zu finden, die beim Pullen oder Rebasen mit ihrer laufenden Arbeit kollidieren könnten
  • Debugger suchen nach Änderungen, die Bereiche berührt haben, die mit der fehlerhaften Komponente zusammenhängen; da Bugs bei jedem type von Änderung entstehen können, hilft die Typinformation dabei nicht
  • Incident-Responder überfliegen das Commit-Log rund um den Zeitpunkt einer Störung, um den betroffenen Bereich zu finden; wenn an der Stelle eines starken Anstiegs von Inbound-API-Fehlern ein Commit mit dem scope auth steht, ist das ein naheliegender Kandidat als Ursache
  • Für Menschen, die Commit-Logs lesen, ist nicht entscheidend, welcher Art eine Änderung war, sondern welchen Bereich sie berührt hat

Die Redundanz und Begrenztheit von type

  • fix(compiler): prevent namespaced SVG <style> elements from being stripped zeigt schon in der Beschreibung, dass es ein Bugfix ist; der type fix ist daher redundant
  • Der Platz in der Commit-Betreffzeile ist begrenzt, daher hilft es nicht, Zeichen für einen type zu verbrauchen, den man bereits aus der Beschreibung erkennen kann
  • refactor(core): Update webmcp support to use document.modelContext aktualisiert die webmcp-Funktionalität der core-Komponente so, dass sowohl document.modelContext als auch navigator.modelContext unterstützt werden
  • Diese Änderung lässt sich gleichzeitig als Bugfix, Refactoring und neues Feature verstehen, aber die tatsächlich wichtige Information ist, dass es sich um eine Änderung an der Komponente core/webmcp handelt

Grenzen des Automatisierungsversprechens

  • Die Idee, mit Tools wie git-cliff oder conventional-changelog automatisch aus Commits ein CHANGELOG zu erzeugen, hat das Problem, dass Commit-Logs und Changelogs unterschiedliche Zielgruppen haben
  • Ein CHANGELOG richtet sich an Nutzer und fokussiert sich darauf, funktionale und geschäftliche Unterschiede zwischen Versionen verständlich zu machen
  • Ein Commit-Log richtet sich an Entwickler und dient dazu, nachzuvollziehen, wie sich die Codebasis im Zeitverlauf verändert hat und wie sich Änderungen aus Sicht des scope entwickelt haben
  • In Projekten mit mindestens mittlerer Komplexität wird ein sinnvolles Feature oft über mehrere Commits eingeführt; für Entwickler ist der Implementierungsprozess nützlich, für Endnutzer ist nur das fertige Feature relevant
  • Revert-Commits sind für Entwickler im Fluss des Commit-Logs wichtig, für Endnutzer ist eine zurückgenommene Änderung jedoch gleichbedeutend mit einer Änderung, die nie ausgeliefert wurde
  • Semantische Versionssprünge auf Basis von Commit-Typen können dazu führen, dass trotz eines revertierten Breaking Changes eine Major-Version erhöht wird, dass erst später erkannte Inkompatibilitäten fälschlich als Minor- oder Patch-Version veröffentlicht werden oder dass etwas als Breaking Change gilt, obwohl der Bruch durch Folge-Commits wieder verschwunden ist
  • Solche Situationen lassen sich zwar per Rebase aus der Historie herauskorrigieren, aber der Workflow kann das verhindern oder beschädigen und damit die Verlässlichkeit des im Commit-Log vermittelten Verlaufs mindern
  • Wenn Build- oder Deployment-Prozesse durch den type im Commit-Titel ausgelöst werden, können automatische Werkzeuge umgangen werden, etwa wenn ein Commit mit dem Titel docs: fix typos in Wirklichkeit eine Schwachstelle im Authentifizierungs-Subsystem einführt
  • Es ist besser, Build- und Deployment-Bedingungen anhand der mit git diff ermittelten geänderten Dateien festzulegen als anhand des Commit-Titels

Umsetzungsprobleme und Alternativen

  • Conventional Commits empfehlen, projektspezifische type-Sets zu definieren, doch viele Projekte übernehmen schlicht die Standardtypen von commitlint, die möglicherweise nicht gut zu den Besonderheiten des jeweiligen Projekts passen
  • Die Spezifikation von Conventional Commits definiert technisch gesehen nur fix und feat; weitere Typen bleiben dem jeweiligen Projekt überlassen
  • In Unternehmensumgebungen kann es aufgrund von Change-Management- und Audit-Anforderungen nötig sein, in jede Commit-Message eine Ticketnummer aufzunehmen; wenn <scope> dafür verwendet wird, geht nützliche Metadaten verloren
  • Linux, FreeBSD, Git, Go, NixOS und Node.js verwenden projektgerechte Commit-Messages mit scope-Präfix
  • Im Linux-Kernel ist das subsystem ein natürlicher scope, in Go-Projekten der Package-Pfad und in Microservice-Architekturen der Name des jeweiligen Microservice
  • scopedcommits.com plädiert für eine Rückkehr zu einem scope-zentrierten Format bei Commit-Messages und für die Trennung von CHANGELOG-Erzeugung und Commit-Log-Verwaltung
  • Die Vorteile von Conventional Commits haben sich in der Praxis nicht in echte Vorteile übersetzt, und ihre Popularität in Open-Source-Projekten sowie die Tendenz von KI, sie standardmäßig zu wählen, haben zur Verbreitung von Commit-Messages mit vermischten Antipatterns geführt

1 Kommentare

 
GN⁺ 5 시간 전
Lobste.rs-Meinungen
  • Es freut mich, einen logisch formulierten Einwand gegen conventional commits zu sehen und nicht bloß eine instinktive Abneigung
    Ich hatte nie wirklich gründlich darüber nachgedacht, warum ich sie nicht mag, und dachte, vielleicht liegt es daran, dass ich sie mit von LLMs erzeugtem Code verbinde. Vor allem chore: kann ich überhaupt nicht ausstehen; bitte erfindet nicht noch einmal die ungarische Notation. Das hätte von Anfang an nie entstehen sollen

    • Besonders chore: gibt es inzwischen nicht einmal mehr im Angular Commit Style Guide; offenbar hat man erkannt, wie vage es ist, und es in build: aufgehen lassen
      Selbst als es noch im Angular-Stil enthalten war, beschrieb chore: recht konkrete Anwendungsfälle, aber in manchen Open-Source-Projekten scheint es eher aus dem Bauch heraus für Arbeiten vergeben zu werden, die sich wortwörtlich wie lästige Pflichten anfühlen
  • Ich mag conventional commits nicht besonders, aber die vorgeschlagene Alternative scheint zu übersehen, warum scope optional ist
    In kleinen Projekten ohne viele klar abgegrenzte Module ist das Konzept eines „scope“ kaum nützlich. Eine hilfreiche Praxis, die beide Seiten auslassen, ist es, im Commit-Titel Issue- oder Ticketnummern unterzubringen; das erleichtert es, zusätzlichen Kontext einer Änderung zu erfassen, und hilft besonders beim Code-Review. Wenn Ticketnummern aber verpflichtend werden, entstehen bei kleinen Änderungen nur nutzlose Tickets, und das mag ich nicht; wenn eine Änderung jedoch einen bestimmten Bug oder eine bestimmte Aufgabe behandelt, sollte sie mit diesem Bug oder dieser Aufgabe verknüpft sein

    • Wenn kein scope nötig ist, lässt man ihn einfach weg
      Das ist immer noch besser als ein redundanter Commit-„type“, der schon in der Betreffzeile selbst ersichtlich sein sollte
    • Im Idealfall gäbe es überhaupt keinen vorgeschriebenen Commit-Stil, und man würde jeweils die Formulierung verwenden, die zum konkreten Commit passt
      Wenn eine Änderung klar einem Ticket entspricht, nutzt man einen „Ticketnummer“-Commit, andernfalls eine andere Form. Manche Änderungen passen gut zu einem type, aber weniger zu einem scope, und umgekehrt, daher kann man scoped commits und conventional commits auch mischen
  • Ich würde gern sagen: „Verwendet in Fließtexten keine Monospace-Schrift
    Trotzdem stimme ich der Grundannahme des Textes weitgehend zu

  • Auch wenn Commit-Messages nicht besonders gut sind, empfehle ich, um ein Gefühl für den Umfang von Änderungen zu bekommen, häufiger git log --name-only oder git log --stat zu verwenden
    Wenn man die Dateinamen sieht, hilft das ziemlich dabei zu verstehen, was sich geändert hat, ohne jeden einzelnen Commit öffnen zu müssen

  • Eine Vorgehensweise, die mir wirklich gefällt, ist es, den conventional-commit-Stil für PR-Titel zu erzwingen
    PR-Titel können auch nach dem Merge noch von Maintainern geändert werden, man muss die Commit-Historie nicht neu schreiben, und zusammen mit Tools wie release-drafter lassen sich aussagekräftige Changelogs in GitHub-Releases automatisieren. Damit bekommt man genau die passende Granularität für die vom Autor genannten Stakeholder, also eine Trennung nach Features, Fixes und Breaking Changes, und zugleich wird für den nächsten GitHub-Release-Entwurf eine sinnvolle semver-Version automatisch abgeleitet
    Der Einwand des Autors, dass eine Komponente wie parse-lib nicht optional sein sollte, ist richtig, und ich stimme auch zu, dass das Erzwingen von conventional commits neue Beiträge abschrecken kann. Aber die Alternativen sind auch nicht wirklich besser
    Trotzdem vermittelt ein Breaking-Change-Bezeichner wie fix!(parse-lib): Don't leave sparse holes when parsing JSON arrays ziemlich viele Informationen. Es ist ein Bugfix für eine bestimmte Komponente, mit einem unvermeidlichen Breaking Change als Folge, und trägt eine Bedeutung wie eine semver-Erhöhung. So etwas kann man gut im PR-Titel verwenden

  • Ich gebe zu, dass ich conventional commits als Methode zur Förderung von Commit-Disziplin zu sehr verinnerlicht habe, und am Ende wurde es einfach zur Gewohnheit
    Inzwischen empfinde ich sie oft als einschränkend und willkürlich. In manchen Projekten weiß ich nicht einmal, ob das dort tatsächlich Konvention ist, und ich bin eher näher an Linux-/Go-/Node-Stilen gelandet; in Monorepos mit vielen unterschiedlichen Setups fühlte es sich natürlicher an, [service]: [what changed] zu schreiben, statt einen type zu erzwingen. Künftig möchte ich bei meinem persönlichen Commit-Stil mehr experimentieren und mich eher daran orientieren, was nützlich erscheint, statt mich an eine strikte Konvention anzupassen; scoped commits wirken dabei wie ein guter Ausgangspunkt

  • chore(lobsters): add my 2 cents on conventionals commits [JIRA-69420]
    Ich stimme fast vollständig zu, sehe aber einen Punkt anders: den Teil über „revisionistische Aufzeichnungen, die den Beitragenden gezeigt werden und die Zuverlässigkeit der Geschichte verringern, die das Commit-Log erzählt“. Der Autor scheint hauptsächlich von öffentlichen Branches zu sprechen, und für öffentliche Branches ist das ein vernünftiger Rat. Auf private Branches sollte das aber nicht angewendet werden. Es reicht, die Historie so zu gestalten, dass die Person, die die endgültige Änderung prüft – also ein Maintainer oder ich in zehn Jahren – sie leicht verstehen kann; man muss keinen inkonsistenten Gedankengang oder, schlimmer noch, eine Ansammlung von address review-Commits zurücklassen

  • Die Antwort auf „Warum ist scope optional?“ lautet bei kleinen Projekten schlicht, dass das gesamte Projekt der scope ist
    Ich stimme zu, dass der „type“ eines Commits nicht besonders nützlich ist, aber ich bin mir auch nicht sicher, ob es zwischen scoped commits und conventional commits überhaupt einen großen Unterschied gibt. Scoped ist im Grunde nur conventional ohne „type“, und eine Unterscheidung zwischen fix, feat, refactor und chore ist durchaus in Ordnung
    Wenn ohnehin alle einfach die Standardwerte von commitlint übernehmen, müsste man die Leute dann nicht einfach dazu bringen, besser damit umzugehen?