1 Punkte von GN⁺ 3 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Chesterton’s fence ist der Rat, Code nicht leichtfertig zu ändern, wenn man den Grund dafür nicht kennt. Code und Commit-Historie ohne festgehaltene Begründung wälzen dieselbe Last jedoch auf nachfolgende Entwickler ab
  • Die Commit-Bodies der letzten 13 Jahre in diesem Repository umfassen, nach Kommando gezählt, nur 295 Zeilen; zieht man Bodies zu dependabot-, revert- und typo-bezogenen Commits ab, bleiben 167 Zeilen übrig
  • Commit-Titel wie „fix page A“ liefern selbst bei großen Änderungen kaum Kontext, und da es fast keine separate Dokumentation oder Code-Kommentare gibt, ist es schwierig, die Gründe für Änderungen nachzuverfolgen
  • Im Code bleiben unvollendete Refactorings, Überreste entfernter Funktionen, hinzugefügte, aber nicht verdrahtete oder ungenutzte Funktionen sowie offenbar von niemandem verwendete Funktionen zurück
  • Commit-Messages und Dokumentation sollten zumindest festhalten, „was geändert wird, warum es geändert wird und warum dies eine gute Lösung ist“; wenn gar nichts hinterlassen wird, steigen die Kosten für die Entwickler, die später kommen

Die Last von Code ohne Begründung

  • Chesterton’s fence ist eine Metapher dafür, etwas nicht vorschnell zu ändern, wenn man nicht versteht, warum es so ist
    • Auch beim Programmieren kann es passieren, dass man seltsam aussehenden Code „repariert“ und später feststellt, dass diese Seltsamkeit einen Grund hatte
  • In diesem Fall zeigt sich das gegenteilige Problem
    • Es gibt viel seltsamen Code und viele seltsame Entscheidungen, aber keine Aufzeichnungen darüber, warum es so geworden ist
    • Die früheren Entwickler sind alle weg, und es gibt niemanden mehr, den man fragen könnte
  • Die Commit-Historie des Repositories liefert praktisch keinen inhaltlichen Kontext
    • Die Commit-Bodies der letzten 13 Jahre umfassen insgesamt 295 Zeilen
    • Wenn man dependabot-, „revert commit“- und „fix typo“-Commit-Bodies manuell herausrechnet, bleiben 167 Zeilen
    • Das entspricht ungefähr einer Zeile pro Monat
  • Auch die Commit-Titel reichen meist nicht aus, um größere Änderungen zu erklären, etwa bei „fix page A“
  • Es gibt keine separate Dokumentation und fast keine Code-Kommentare
  • Diese Situation kommt eher einem Chesterton’s middle finger gleich: „Wir haben viele seltsame Dinge getan, sagen aber nicht, warum“

Das Minimum an Aufzeichnungen, das Entwickler hinterlassen sollten

  • In der Codebasis finden sich verschiedene Arten von unvollständigem oder zurückgebliebenem Code
    • unvollendete Refactorings
    • Überreste entfernter Funktionen
    • hinzugefügte, aber nicht verdrahtete oder ungenutzte Funktionen
    • Funktionen, die offenbar niemand benutzt
  • Insgesamt scheint auch das Problem von Chesterton’s gap stark ausgeprägt zu sein
    • Es ähnelt einer Situation nach dem Motto: „Hier gibt es noch keinen Zaun, also bauen wir einen“, ohne zu fragen, ob überhaupt ein Zaun nötig ist
  • Gut zu schreiben ist schwierig, aber eine grob ausreichende Erklärung zu hinterlassen, ist nicht schwer
  • Eine Änderungsdokumentation sollte grundsätzlich drei Fragen beantworten
    • Was wird geändert?
    • Warum wird es geändert?
    • Warum ist diese Lösung gut?
  • Manchmal reicht „Implement new feature X“ aus, aber meistens gibt es etwas dazu zu schreiben, warum oder wie eine Funktion auf diese Weise hinzugefügt wurde
  • Bei Bugfixes, Refactorings und substanziellen Änderungen kann man in der Regel zumindest ein oder zwei Absätze zum Inhalt der Änderung und zu ihren Gründen hinterlassen
  • Solche Aufzeichnungen sind keine optionale Zusatzarbeit, sondern Teil der Arbeit von Softwareentwicklern
    • Sie müssen nicht elegant sein
    • Es muss kein perfektes Englisch sein
    • Es muss kein großspuriger Essay sein
    • Selbst wenn etwas fehlt, ist es immer noch viel besser als gar nichts
  • Wenn man gar nichts hinterlässt, lädt man das Problem auf alle später Beteiligten ab

1 Kommentare

 
GN⁺ 3 시간 전
Lobste.rs-Meinungen
  • Manchmal ist im jeweiligen Moment nicht klar, was später wichtig werden wird. Es hilft sehr, wenn der gesamte Weg bis zu einem Commit öffentlich dokumentiert ist, aber weil alle Beteiligten bereits viel Kontext haben, wird manches als „zu selbstverständlich“ angesehen und weggelassen.
    Wenn die Diskussionen nicht festgehalten werden, wird es viel schwieriger, den Entscheidungsprozess wie digitale Archäologie zu rekonstruieren. Am Ende muss man dann mit einem Zustand umgehen, in dem niemand mehr weiß, warum dieser Zaun existiert, und ihn anhand des aktuellen Kontexts und des heutigen Systemwissens der Beteiligten bewerten.
    Es ist ein großer Vorteil, wenn jemand lange im Projekt geblieben ist und ein Verständnis sowie ein Gespür für das Gesamtsystem entwickelt hat. Organisationen, die Entwickler als austauschbare Zahnräder betrachten, erleben, dass niemand lange genug bleibt, und wiederholen deshalb dieselben Fehler und erfinden das Rad neu.

    • Der größte Vorteil von Code-Reviews ist, dass eine andere Person den Code liest und dadurch an allen Stellen Kommentare erzwingt, an denen der Grund nicht klar ist. Was für mich unklar ist, kommentiere ich ohnehin bereits; dadurch bleiben nun an allen Stellen Kommentare zurück, an denen wenigstens eine von zwei Personen das Gefühl hatte, dass eine Erklärung nötig ist.
      Die nächste Person, die sich den Code ansieht, hat dann zumindest keine Null-Chance mehr zu verstehen, warum es so gemacht wurde.
    • Das ist ähnlich wie bei alten Kochrezepten, in denen Zutaten nicht aufgeschrieben wurden, „von denen doch jeder weiß, wo man sie findet“, oder bei mittelalterlicher Musik, bei der Rhythmus und Takt als allgemein bekannt vorausgesetzt wurden. Heute weiß man nicht mehr, was darin ursprünglich enthalten war.
  • Ich habe nie Entwickler verstanden, die in Commit-Messages einfach nur „fix“ oder „WIP commit“ schreiben. Wahrscheinlich haben sie nie ernsthafte Code-Archäologie betrieben oder gar nicht daran gedacht, dass man so etwas tun kann.
    Ich versuche immer, eher in Richtung zu vieler Informationen zu irren. Dann besteht zumindest eine Chance, dass mein zukünftiges Ich, mein Nachfolger oder die arme Person, die bei einem Ausfall alarmiert wird, irgendwann noch nachvollziehen kann, warum etwas kaputtgegangen ist.

    • Das kann vielleicht ein Fall von einer falsch gewählten Größe der Arbeitseinheit sein. Für manche Entwickler ist ein einzelner Commit nur einer von Hunderten kleiner Schritte, die zusammen ein „verkaufbares“ Feature ergeben.
      In so einem Fall ist es schwer, nach jedem Checkpoint gedanklich nachzuverfolgen, was sich geändert hat, oder eine sinnvolle Erklärung hinzuzufügen; man behandelt Commits dann eher wie den „Speichern“-Button in einem Videospiel statt als Mittel, sauber definierte Arbeitseinheiten zu trennen.
      Wenn man umgekehrt als Ergebnis eines großen API-Refactorings einen großen Commit erstellt, reicht wahrscheinlich alles unterhalb einer Erklärung auf dem Niveau eines Designdokuments nicht aus, und wenn man das ohnehin nicht schreiben wird, ist auch der Sinn einer langen Message fraglich. Manche tragen das dann allerdings wie einen Orden vor sich her und fangen an, in Release Notes nur noch Dinge wie „Bugfixes und Feature-Verbesserungen“ zu schreiben, was ganz offensichtlich die falsche Schlussfolgerung ist.
  • Viele Entwickler vergessen oft, dass die „andere Person“, die den Hintergrund einer Änderung lesen und verstehen muss, auch das eigene zukünftige Ich sein kann. Die Situation, in der man sich an einem Code-Block den Kopf kratzt, git blame ausführt und dann der eigene Name zurückstarrt, dürfte allen vertraut sein.
    Gute Commit-Messages haben mir unzählige Male mehrere Stunden, im schlimmsten Fall sogar Tage erspart, die ich sonst mit dem Durchsuchen von Issues, E-Mails und Chat-Logs nach dem Warum verbracht hätte. Das galt sogar in Fällen, in denen ich die Antwort doch eigentlich sofort hätte wissen müssen.
    Man sollte freundlich zu seinem zukünftigen Ich sein. Es ist besser, alles, was man wusste, dachte und besprochen hat, in den git-Log zu kippen. Es ist niemals klar, was in fünf Jahren immer noch selbstverständlich sein wird.

    • Natürlich gehört ein Teil dieses Wissens eher in Kommentare oder Designdokumente als in den Commit-Log.
    • Der Teil mit dem Erschrecken über den eigenen Namen bei git blame passiert mir zumindest bei Dingen, für die es einen Grund gab, nicht so oft. Zufällige Dinge, die wie seltsames Verhalten anderer wirken, lassen sich schwer sinnvoll erklären, wenn man deren Prozess nicht sehen kann; aber wenn es etwas ist, das für mich einmal Sinn ergab, verstehe ich es beim erneuten Lesen meist wieder.
      Meine Art zu lernen ist wohl eher wie sich aufschichtende Lavaschichten. Seit der Schulzeit hat sie sich nicht grundlegend verändert; statt dessen habe ich einfach immer weiter ergänzt, was ich weiß und wie ich es nutzen kann.
  • Kürzlich hat jemand behauptet, Commit-Messages seien meist Zeitverschwendung, sobald sie länger als ein Satz sind; ich wollte dem entschieden widersprechen, war aber überraschend schlecht darin, das Gegenteil zu belegen
    Eine Frage dabei ist, welche Informationen in eine Commit-Message gehören und welche in Inline-Kommentare, ADRs oder andere längere Dokumentationsformate
    Ich versuche weiterhin, gute Commit-Messages zu schreiben, aber bei der Arbeit ist die Lage schon hoffnungslos, und selbst in meinen privaten Spielzeugprojekten bin ich nicht konsequent

    • Für mich sind Commit-Messages für den Reviewer gedacht. Sie erklären, warum diese Änderung nötig ist, was sie behebt oder warum man ein neues Feature will, und liefern einen Rahmen, um die Änderung zu verstehen
      Der endgültige Code sollte aber auch ohne Lesen der Commit-Message verständlich sein. Wenn es in neuem Code Stellen gibt, die eine Begründung brauchen, gehört das in Kommentare
      Anders gesagt erklärt die Commit-Message, warum man diese Änderung jetzt vorgenommen hat, und Kommentare erklären, warum der Code nach Abschluss der Änderung so aussieht, wie er aussieht
      Größere Änderungen, besonders neue Features, sollten irgendwo ein Designdokument haben. Wenn Review nötig ist, kann das ein echtes Dokument im Repository sein oder auch im Issue-Tracker liegen
      Die Commit-Message sollte auf dieses Dokument verweisen und möglicherweise auch Details erklären, die beim Übergang vom Design in den Code entstanden sind. Wenn möglich, ist es gut, auch eine kurze Zusammenfassung des Designdokuments aufzunehmen. Das ist besonders wichtig, um bei mehreren möglichen Reviewern zu signalisieren, wer sich am stärksten dafür interessieren sollte, und um Fälle zu erkennen, in denen jemand fehlt, der schon in der Designphase Feedback hätte geben sollen
    • Mein Kriterium dafür, was in eine Commit-Message gehört, ist, dass ich Commit-Messages normalerweise nicht in git log oder jj log anschaue, sondern fast immer über die Blame-Anzeige
      Die Betreffzeile ist allgemein sehr nützlich, um zu entscheiden, ob man tiefer einsteigen muss. Wenn im Text steht, warum die Änderung gemacht wurde, hilft das, wenn der Grund nicht intuitiv ist
      Zum Beispiel reicht oft schon „admin: add impersonation“, selbst wenn ziemlich viel Code dazugehört. Aber bei „auth: shorten JWT timeouts“ würde ich gern ein oder zwei Sätze dazu sehen, warum die Timeouts verkürzt werden mussten
      Wirklich lange Commit-Messages halte ich in der Praxis eher für ziemlich nutzlos. Das deckt sich weitgehend mit den im Artikel genannten Gründen. Solche Formate scheinen aus Arbeitsabläufen zu stammen, in denen die Commit-Message zugleich die PR-Beschreibung ist, etwa bei E-Mail-basierten Workflows oder Gerrit. In solchen Fällen sind sie nicht schädlich, aber ich finde auch nicht, dass sie zwangsläufig Mehrwert liefern
    • Ich habe früher einmal einen Artikel genau als Antwort auf diese Frage geschrieben. Darin ordne ich verschiedene Orte für Dokumentation hierarchisch und formuliere Faustregeln dafür, wo welche Informationen hingehören
      Bei mir ist es ähnlich. In der größeren Gruppe bei der Arbeit schreiben nur ich und noch eine weitere Person ausführliche Commit-Messages
  • Selbst wenn man weiß, warum ein Zaun gebaut wurde, weiß man nicht unbedingt, warum er jetzt noch dort steht. Selbst wenn man die Person ist, die Chestertons Zaun errichtet hat, weiß man nicht unbedingt, ob man ihn einreißen kann
    Der bei der Entstehung eines Systems beabsichtigte Baum der Abhängigkeiten ist zu jedem Zeitpunkt nur eine Teilmenge des tatsächlich existierenden Abhängigkeitsbaums. Selbst wenn dir also der perfekte Entwickler vollständig erklären könnte, warum der Zaun gebaut wurde, ist der Nutzen begrenzt
    Was diese Person weiß, ist, warum er gebaut wurde — nicht die Antwort auf „Was geht kaputt, wenn ich diesen Zaun entferne?“. Siehe https://xkcd.com/1172/. Unter den lustigen Use Cases mag es welche geben, die man als unwichtig abtun kann, aber es gibt immer legitime Nutzungen, die selbst dem ursprünglichen Entwickler nicht bekannt waren
    Zu wissen, was der ursprüngliche Entwickler gedacht oder geraucht hat, ist nett, aber diese Information liegt irgendwo zwischen unvollständig und irrelevant
    Als erfundenes Beispiel: Angenommen, Chestertons Zaun wurde ursprünglich errichtet, um Kinder von einer Pfütze fernzuhalten, als auf beiden Seiten noch Bauernhöfe lagen. Inzwischen gibt es dort eine Autobahn, und der Zaun könnte zufällig die einzige Barriere sein, die Massensterben von Tieren und Menschen durch Kollisionen zwischen Hirschen und Autos verhindert
    Niemand weiß das. Die Kombination aus Autobahn und fehlendem Zaun wurde nicht nur nie getestet, sie hat nie existiert, und weder die Autobahnbauer noch die Naturschutzbehörde wissen, warum es auf dieser Straße so wenig Wildunfälle gibt. In ein paar Jahren könnten alle Bauernhöfe in Wohnsiedlungen umgewandelt sein und das Gebiet kein wichtiger Tierkorridor mehr sein — dann ist der Zaun vielleicht nutzlos, vielleicht aber auch nicht
    Wenn dir das konstruiert vorkommt oder du meinst, dass es auf dich nicht zutrifft, beneide ich dich. Meiner Erfahrung nach läuft es in den meisten Firmen so, außer vielleicht in Unternehmen mit einer gewissen Größe, die zudem noch nicht besonders alt sind
    Die Wahrheit ist: Alles, was wir tun, ist Teil eines Ökosystems, hängt von Dingen ab, mit denen wir nie ausdrücklich eine Interaktion vereinbart haben, und wird zugleich von ihnen benutzt. Man kann die API-Oberfläche verkleinern und verhindern, dass alle Implementierungsdetails zur Angelegenheit der Nachbarn werden, aber unbeabsichtigte Kopplung ist eher ein Naturgesetz des Universums — so unvermeidlich wie die Zunahme der Entropie
    Für manche klingt das nihilistisch und defätistisch. Man könnte sagen, wir müssten doch gegen die Entropie kämpfen. Aber ich glaube, die beste Nutzung von Zeit und Investition beginnt damit anzuerkennen, dass dies im Kern nichts ist, gegen das man kämpft, sondern etwas, das man managt
    So zu tun, als könne man den Zustand der Welt jederzeit vollständig kennen, ist eine Einladung zu Scheitern und Selbstvorwürfen. So wie 100% Uptime nicht existiert und für die meisten Dinge ohnehin das falsche Ziel ist
    Wenn man anerkennt, dass man einen Prozess mit einer gewissen heisenbergschen Unschärfe managt, kann man die begrenzte Zeit eines Tages wirksamer einsetzen und bessere Wege wählen, um zu besseren Ergebnissen zu kommen. Insbesondere wird dann ein kluger Kompromiss zwischen Prävention und Reaktion möglich, und man versteht, dass sich Reaktion nicht auf null reduzieren lässt und dass es manchmal keinen Sinn ergibt, ein Jahr Prävention zu betreiben, nur um einen Tag Reaktion zu vermeiden
    Wie viel Dokumentation sollte man also in einen Commit schreiben? Wie viele Design-Dokumente oder Testpläne sollte es geben? Ich weiß es auch nicht. Ein möglicher Ansatz wäre: Jede Dokumentation wird für Leser geschrieben
    Wenn du eine Codebasis veränderst, sollten die aktuellen Teammitglieder ebenso wie neu dazukommende Leute durch Nachforschungen verstehen können, was die Änderung getan hat und warum sie gemacht wurde, und sie sollten ein paar Warnhinweise auf riskante Trittstellen oder lasttragende Bugs bekommen
    Das sollte meist keine ausufernde Prosa sein, sondern eher in Form von Verweisen auf zusätzlichen Kontext, der die Bühne bereitet. Zum Beispiel: „Authentifizierung ist in diesem Schritt erforderlich, da dies Teil der Richtlinie ist, für alle Änderungen eine Mehrparteien-Freigabe zu verlangen. see: go/multiparty“

    • Dass die Entropie zunimmt und ständig neue unbeabsichtigte Kopplungen entstehen, ist in gewisser Weise gar kein Fehler, sondern kann sogar als nützliche Eigenschaft gesehen werden, weil sie verhindert, dass Systeme in einem Dauerzustand erstarren
      Systeme, die im Umgang mit Menschen auf Perfektion abzielen, sind wirklich unerquicklich. Dinge wie DRM, Trusted Computing, Remote Attestation, Faro Plague und Smart Contracts
      Viel besser sind Systeme, die man im Service-Modus neu starten und reparieren kann. Denn wir können nicht vorhersagen, in welche Richtung sich Software entwickeln muss, um Menschen künftig tatsächlich zu helfen. Es ist besser, sie leicht veränderbar zu machen, als sie zu 100% hart abzuriegeln
  • Wir schreiben fast nie Commit-Bodies, aber die Titel sind meist ziemlich gut. Wenn das die Messlatte ist, weiß ich nur nicht, was genau damit gemessen werden soll
    In großen Codebasen sind klarer Code und ausreichende Test Coverage oft deutlich nützlicher als Dokumentation

    • Stimme ich nicht zu. Es gibt Fälle, in denen sowohl die eigentliche Änderung als auch der Grund dafür subtil sind. Wenn man herausfinden will, warum merkwürdiger Code so ist, wie er ist, ist es ein zusätzlicher Schritt, die in diesem Commit geänderten Tests anzusehen, und sie verraten auch nicht zwingend, warum die Änderung vorgenommen wurde
      Es gibt völlig legitime Änderungen, bei denen die Tests entsprechend aktualisiert wurden, bei denen aber später überhaupt nicht mehr klar ist, warum die Änderung nötig war. Besonders dann, wenn die geänderten Zeilen in der Produktionsumgebung unerwartetes oder zusätzliches Verhalten auslösen
      Ein einfaches Revert ist womöglich nicht wünschenswert, und genau dann hilft eine vollständige Historie darüber, warum die Änderung vorgenommen wurde, enorm
      Ich habe viele Fälle gesehen, in denen die Idee richtig war, aber unerwartete Folgen hatte. Wenn man die Absicht kennt, kann man eine tatsächlich richtige Änderung ableiten, die sowohl das neue Problem behebt als auch den ursprünglichen Zweck der Änderung bewahrt
      Wenn man auf einer Ein-Zeilen-Commit-Message besteht, sollte man zumindest eine Ticketnummer angeben, damit man dort die Historie nachlesen kann
  • Ich habe fünf Jahre lang recht gut verdient, indem ich in exotische Gegenden gereist bin, um solche Codebasen zu retten. @arp242, erhöhe immer deine Preise und schlafe mit https://archive.org/details/working-effectively-with-legacy-code unter dem Kopfkissen

  • Zum Glück schreiben die AI-Gerümpelgeneratoren riesige Commit-Messages. Sie haben oft zumindest irgendeinen Bezug zur tatsächlichen Änderung, also ist dieses Problem damit wohl wenigstens gelöst

    • Wirklich? Zum Zusammenfassen dessen, was geändert wurde, taugt das vielleicht, aber beim Warum dürften sie ziemlich schlecht sein