Wir kennen es alle: Du hast an vielen Änderungen gleichzeitig gearbeitet, von denen einige nichts miteinander zu tun hatten. Aufgrund der Bequemlichkeit hast du beschlossen, all diese Änderungen in einen einzigen Commit zu packen. Aber während dies verlockend erscheinen mag, könnte es tatsächlich mehr Probleme in der Zukunft bereiten.
Größere Commits können nämlich:
- Die Quelle von Fehlern oder Regressionen in der Zukunft verschleiern.
- Es schwer machen, unerwünschte Änderungen rückgängig zu machen, ohne auch gewünschte Änderungen rückgängig zu machen.
- Größere Tickets werden überwältigender und dadurch schwieriger zu verwalten.
In letzter Zeit habe ich die Gewohnheit entwickelt, atomare Commits zu machen, um meine Arbeit übersichtlich zu halten. Ich empfehle dir dies auszuprobieren, um zu sehen, ob es für dich funktioniert.
Atomare Commits und das Single Responsibility Principle
Wenn Entwickler über cleanen Code sprechen, erwähnen sie oft das Single Responsibility Principle, das besagt, dass eine Codeeinheit (wie eine Funktion oder ein „Komponente“) sich nur mit einer Aufgabe befassen sollte. Das macht diese Codeeinheit leichter testbar und wiederverwendbarer.
Wir können dieses Prinzip leicht auf Git übertragen und atomare Commits bevorzugen, die für die Dokumentation einer einzigen, vollständigen Arbeitseinheit verantwortlich sind. Das bedeutet nicht, dass jeder Commit auf eine einzelne Datei oder nur wenige Zeilen Code beschränkt sein muss (aber wenn das der Fall ist, dann sei es so). Vielmehr bedeutet es, dass du deine Änderungen mit einer einzigen, aussagekräftigen Nachricht beschreiben können solltest, ohne versuchen zu müssen, zusätzliche Informationen zu nicht verwandten Arbeiten hinzuzufügen.
Eine andere Möglichkeit, einen atomaren Commit zu definieren, ist ein Commit, der ohne unerwünschte Nebenwirkungen oder Regressionen rückgängig gemacht werden kann, abgesehen von dem, was du basierend auf seiner Nachricht erwartest. Wenn ein Commit aus deinem Git-Verlauf entfernt wird, dies aber auch andere legitime Änderungen entfernt, dann war dieser Commit nicht atomar.
Du solltest diese Praxis nicht einfach durchsetzen oder erwarten, dass sie für jeden in deinem Team effizient ist. Aber in jedem Fall ist das Schreiben von atomaren Commits eine gute Praxis, die sich in deinem Arbeitsablauf als nützlich erweisen könnte.
So etwas wie „zu viele Commits“ gibt es nicht
Entwickler zögern manchmal, viele kleine Commits zu machen, weil sie befürchten, dass dies unnötigen Unruhe in ihrem Commit-Verlauf erzeugt. Aber es gibt wirklich keine „zu vielen Commits“ – viele Unternehmen werden eine Anzahl von Commits im Tausenderbereich haben, wenn nicht sogar mehr.
Um dir eine Vorstellung zu geben: hier ein schneller Blick auf die Commit-Zahlen einiger der beliebtesten Repositories aller Zeiten auf GitHub (die Zahlen sind zum Zeitpunkt dieses Schreibens und werden zum Zeitpunkt deines Lesens viel höher sein):
- Visual Studio Code: 82k
- TensorFlow: 110k
- Kubernetes: 100k
- Rust: 140k
- Node.js: 33k
- TypeScript: 32k
Ungeachtet dessen, was du möglicherweise denkst, erschwert eine hohe Anzahl von Commits nicht das Durchführen von git logs oder das Zeitreisen in einer Codebasis. Tatsächlich kann dies diese Aufgaben erleichtern, indem die Änderungen voneinander isoliert werden. Das ermöglicht es dir, zu einem bestimmten Zeitpunkt zurückzureisen, als nur eine einzelne Änderung vorgenommen wurde. Du kannst klar zwischen den Commits X, X-1 und X+1 unterscheiden.
Squashen oder nicht squaschen?
Als Kompromiss folgen einige Teams einem Squash-und-Rebase-Workflow. Dabei können Entwickler auf ihrem Branch so viele Commits erstellen, wie sie möchten. Diese Commits werden jedoch vor dem Zusammenführen des Pull Requests in den Zielbranch durch einen interaktiven Rebase zu einem einzigen Commit zusammengefasst.
Obwohl dies deinen Commit-Verlauf kompakter gestaltet, habe ich festgestellt, dass dieser Workflow tatsächlich den Zweck der Verwendung atomarer Commits untergräbt. Zwar listet die erweiterte Git-Commit-Beschreibung immer noch die einzelnen Nachrichten aller zusammengefügten Commits auf, jedoch sind diese Commits eher wie Artefakte. Sie stellen keine gültigen Verweise in der Git-Geschichte dar, es sei denn, du stellst sie mit deinem lokalen Git reflog wieder her. Andere Entwickler können nicht zu diesen Commits in der Geschichte zurückkehren oder zu ihnen zeitreisen.
Beachte, dass das Squashen Sinn macht, wenn einige deiner Zwischencommits versehentlich erstellt wurden und eigentlich Teil eines anderen Commits hätten sein sollen. In diesem Fall wäre es nicht sinnvoll, diese Commits getrennt zu halten. Du könntest auch das Squashen verwenden, um lärmende oder bedeutungslose Commits aufzuräumen, wie zum Beispiel, wenn du versuchst, einen CI-Pipeline-Neustart auszulösen (es ist jedoch besser, den neuesten Commit zu ändern und erneut auf deinen Branch zu pushen, anstatt Commits wie „test“ oder „y u no work“ zu erstellen).
Die Vorteile des Schreibens von atomaren Commits in Git
Atomare Commits lösen all die Probleme, die ich zuvor erwähnt habe, und bieten eine Reihe von Vorteilen. Du wirst sie mehr schätzen, wenn du Änderungen in Git rückgängig machen oder mit vielen anderen Entwicklern zusammenarbeiten musst.
Atomare Commits erleichtern das Aufspüren von Regressionen
Es ist zwar wahr, dass du mehr Aufwand betreiben musst, um deine Commits in unabhängige Arbeitseinheiten zu trennen, aber diese Investition lohnt sich.
Zum einen erleichtern atomare Commits deine Arbeit sowohl für dich als auch für dein Team. Sie machen es einfacher, die Ursache von Regressionen in der Zukunft zu finden, insbesondere wenn du auf ein Problem stößt, das besonders knifflig zu debuggen ist.
Wenn du atomare Commits erstellst und einen git bisect
durchführst, wobei Git in der Lage ist, den schuldigen Commit zu identifizieren, wirst du zuversichtlicher sein, dass die Änderungen in diesem Commit tatsächlich für das Auftreten des Fehlers verantwortlich waren und dass sie nicht auch andere Bereiche des Codes beeinflusst haben. Dies kann den Umfang der Dateien erheblich einschränken, die du jetzt genauer untersuchen musst, um zu verstehen, was schief gelaufen ist. Noch besser ist es, wenn du einfach diesen Commit rückgängig machen kannst.
Wenn du stattdessen zusätzliche Änderungen als Teil dieses Commits hinzugefügt hättest, hättest du herausfinden müssen, welche dieser Änderungen für den Fehler verantwortlich waren. Dies kann zu einer ärgerlichen und unvorhersehbaren Entwicklererfahrung führen, da Fehler unbemerkt mit legitimen Änderungen in deinen Code einsickern können.
Atomare Commits sind einfacher rückgängig zu machen, zu verwerfen und zu ändern Angenommen, du arbeitest an einem großen Feature.
Es gibt viele bewegliche Teile, und du musst möglicherweise sogar etwas älteren Code refaktorisieren, um deine neuen Änderungen unterzubringen. Wenn du schließlich deinen Pull Request (PR) erstellst, sind einige der Reviewer mit bestimmten von dir vorgenommenen Änderungen nicht einverstanden und bitten dich, sie rückgängig zu machen. Klingt einfach, oder?
Leider hast du keine atomaren Commits gemacht. Du hast alle deine Änderungen nur in einen oder zwei Commits für die Bequemlichkeit gepackt, mit sich überlappenden Arbeitsbereichen. Einige deiner Commit-Nachrichten wurden auch überhastet verfasst, was es schwieriger macht, festzustellen, welcher dieser wenigen Commits die unerwünschten Codeänderungen eingeführt hat.
Deine einzige Option ist es, hineinzugehen und diese Änderungen manuell rückgängig zu machen. Und wenn die Änderungen besonders komplex waren, wird es noch schmerzhafter sein. Es stellt sich heraus, dass größere Commits doch nicht so bequem sind – du hast nicht die Zeit investiert, deine Commits im Voraus in unabhängige Arbeitseinheiten zu splitten, und jetzt erleidest du die Konsequenzen.
Wenn du stattdessen für all deine Arbeit atomare Commits verwendet hättest, könntest du die wenigen Commits, die sich auf die Änderungswünsche beziehen, rückgängig machen (oder sogar verwerfen), und du wärst innerhalb von Minuten fertig. Erneute Anfrage zur Überprüfung, und du bist fertig.
Dies gilt jedoch nicht nur für Änderungswünsche. Während du an einem Ticket arbeitest, könntest du feststellen, dass dein aktueller Ansatz nicht ideal ist, also musst du vielleicht zurückgehen. Aber wenn du eine Menge uncommitted Änderungen in Git hast – mit mehreren sich überlappenden Anliegen und mehreren verschiedenen Änderungen in einer einzigen Datei, von denen einige benötigt werden und andere verworfen werden müssen – musst du deine Arbeit manuell rückgängig machen. Wenn du jedoch atomare Commits geschrieben hättest, könntest du diejenigen rückgängig machen, die die Änderungen eingeführt haben, und fertig sein (oder, noch besser, diejenigen Commits ganz rebase und verwerfen).
Atomare Commits erleichtern das Abschließen größerer Aufgaben Stellen wir uns vor, du bist in der gleichen Situation. Du arbeitest an einem großen Feature-Ticket oder refaktorisierst viele Dateien.
Zunächst mag dies überwältigend erscheinen, und dein erster Instinkt könnte sein, einfach in einem Bereich deiner App zu arbeiten, um zu sehen, wie weit du kommst. Schließlich erkennst du, dass du viele Änderungen in vielen verschiedenen Dateien gemacht hast, um so schnell wie möglich eine funktionierende Lösung zu erhalten. Und wenn es an der Zeit ist, deine Arbeit zu reflektieren und zu commiten, scheint das selbst eine Herausforderung zu sein. Welche Nachricht schreibe ich? Wie fasse ich all diese Arbeit mit nur einem einzigen Commit zusammen?
Wenn du versuchst, mehrere Änderungen auf einmal anzugehen, wirst du diese nicht in sinnvolle Arbeitseinheiten aufteilen können. Daher wirst du versucht sein, einen einzigen Commit für alles zu machen, nur um damit fertig zu werden.
In diesem Szenario ist das Fehlen von atomaren Commits ein Symptom eines größeren Problems, nämlich dass du die ursprüngliche Arbeit nicht in mundgerechte, überschaubare Stücke unterteilt hast. Stattdessen hast du versucht, alles auf einmal zu erledigen. Wenn du deine Arbeit in kleinere Aufgaben aufgeteilt hättest, hättest du jede Arbeitsleistung isoliert commiten können, um einen sauberen und genauen Fortschrittsbericht bereitzustellen.
In diesem Zusammenhang sehen wir, dass atomare Commits zwei Vorteile bieten:
- Sie minimieren die kognitive Belastung bei der Arbeit an größeren Tickets.
- Sie erleichtern es dir, deinen Fortschritt zu verfolgen und zu dokumentieren.
Das Schreiben von atomaren Commits zwingt dich dazu, kleinere Änderungen vorzunehmen, wenn du an großen Aufgaben arbeitest. Dies kann als nützliche Erinnerung dienen, wo du am Vortag aufgehört hast, wenn du deine Arbeit fortsetzen oder häufig den Kontext wechseln musst. Aber am wichtigsten ist, dass es die mentale Belastung erleichtert. So viele Änderungen auf einmal vornehmen zu müssen, wird durch das schrittweise Vorgehen in kleinen Schritten auf dein Endziel hin vereinfacht.