„Der grug-brained Entwickler“ (2022)
(grugbrain.dev)- Die wichtigste Lektion aus langer Entwicklungserfahrung ist, dass Komplexität der „ewige Feind“ einer Codebase ist und einen Zustand schafft, in dem kleine Änderungen an unerwarteten Stellen etwas kaputtmachen
- Das stärkste Werkzeug gegen Komplexität ist, zu Features und Abstraktionen „no“ zu sagen; wenn pragmatische Kompromisse nötig sind, liefert eine 80/20-Lösung den Großteil des Werts mit wenig Code
- Design und Tests sollten weniger auf zu frühe Abstraktion setzen, sondern darauf, gute Grenzen zu finden, sobald die Form des Systems sichtbar wird; wichtiger als Unit Tests sind langlebige Integrationstests
- Tools, Typsysteme, Logging, Debugger und API-Design sollten die kognitive Last für Entwickler senken; der große praktische Wert von Typsystemen liegt in der IDE-Autovervollständigung
- Auch Methoden mit gutem Namen wie DRY, Separation of Concerns, Microservices, SPA, Generics und übertriebenes Refactoring werden zu Einfallstoren für Komplexität, wenn sie das Verstehen und Warten von Code erschweren
Komplexität ist das größte Risiko einer Codebase
- Komplexität macht ein System, das anfangs verständlich war, plötzlich schwer handhabbar und sorgt dafür, dass eine Änderung an einer Stelle an einer scheinbar unbeteiligten anderen Stelle etwas kaputtmacht
- Komplexität kann durch Entwickler oder Projektmanager mit guten Absichten hineingelangen; manchmal ist auch die Komplexität, die man selbst eingebracht hat, die Ursache des Problems
- Es gibt keinen universellen „Knüppel“, der Komplexität löst; die beste Reaktion ist, sie gar nicht erst hereinzulassen
„no“ und „ok“: Ablehnung und pragmatischer Kompromiss
- Die beste Waffe gegen Komplexität ist „no“
- „Dieses Feature bauen wir nicht“
- „Diese Abstraktion bauen wir nicht“
- Allerdings ist „no“ zwar guter Engineering-Rat, kann aber für höhere Vergütung oder Beförderungen nachteilig sein
- Wenn man realistisch kompromissbereit sein muss, antwortet man mit „ok“ und sucht nach einer 80/20 solution
- Also 80 % des gewünschten Werts mit 20 % des Codes umzusetzen
- Vielleicht hat sie nicht alle schmückenden Features und sieht etwas unschön aus, liefert aber den größten Teil des Werts und hält die Komplexität niedrig
- Da Projektmanager Detailanforderungen auch vergessen, wechseln oder das Unternehmen verlassen können, ist der 80/20-Ansatz manchmal die bessere Wahl
Design und Zerlegung von Code sind gefährlich, wenn sie zu früh kommen
- Zu Beginn eines Projekts ist die Form des Systems noch unklar wie „Wasser“, daher sollte man zu frühe Zerlegung vermeiden
- Mit der Zeit werden gute Grenzpunkte sichtbar
- Die Schnittstelle zum restlichen System ist schmal
- Interne Komplexität wird hinter wenigen Funktionen oder Abstraktionen verborgen
- Wenn man Abstraktionen zu früh schafft, entstehen leicht die falschen Abstraktionen; deshalb ist Abwarten oft vorzuziehen
- Bei Entwicklern, die am Anfang große Abstraktionen bauen wollen, kann es helfen, ihnen wegwerfbare Artefakte wie UML zu geben oder für morgen eine lauffähige Demo zu verlangen
- Ein früher Prototyp zeigt lauffähigen Code und reale Einschränkungen schneller
Tests nicht „später auf einmal“, sondern „während die Form entsteht“
- Tests verhindern viele Probleme, aber er lehnt es ab, test first zu erzwingen, bevor man überhaupt die Domäne verstanden hat
- Er bevorzugt es, die meisten Tests zu schreiben, nachdem der Prototyp steht und der Code anfängt, stabiler zu werden
- Die Haltung, Tests mit „läuft auf meiner Maschine“ zu überspringen, ist sehr schlecht
- Die Vorlieben nach Testart sind klar
- unit tests: Am Projektanfang nützlich, brechen aber leicht bei Implementierungsänderungen und können Refactoring erschweren
- end to end: Zeigen das Verhalten des Gesamtsystems, aber wenn sie brechen, ist die Ursache schwer zu verstehen; brechen sie häufig, werden sie leicht ignoriert
- integration tests: Hoch genug, um die Korrektheit des Systems zu sehen, und niedrig genug, um Ursachen mit dem Debugger zu finden; sie liegen nahe am besten Punkt
- Wenn die Grenzpunkt-API stabil ist, sollte man stark in Integrationstests rund um sie investieren
- End-to-End-Tests sollten sich auf die häufigsten UI-Funktionen und einige wichtige Edge Cases konzentrieren und als kleine, gut gepflegte Sammlung erhalten bleiben
- mocking nur dann einsetzen, wenn es wirklich nötig ist, und möglichst nur auf Ebene großer Grenzen
- Wird ein Bug gefunden, bevorzugt er ausnahmsweise, ihn zuerst mit einem Regressionstest zu reproduzieren und danach zu beheben
Agile, Refactoring, Chesterton's Fence
- Agile selbst ist nicht das Schlimmste und kann als eine Form der Entwicklungsorganisation dienen; vor „agile shamans“, die es wie ein Entlastungswerkzeug für Scheitern verwenden, sollte man sich jedoch hüten
- Prototypen, gute Tools und die Einstellung guter Entwickler sind für Softwareerfolg wichtiger; kein Prozess ist eine silver bullet, die alle Probleme löst
- Refactoring ist besonders nützlich, wenn der Code in späten Projektphasen stabiler geworden ist, doch je größer das Refactoring, desto höher die Wahrscheinlichkeit des Scheiterns
- In kleine Schritte aufteilen
- Das System möglichst durchgehend lauffähig halten
- Nach jedem Schritt zum nächsten übergehen
- Übertriebene Abstraktion kann zu gescheitertem Refactoring und Systemversagen führen
- Chesterton's Fence steht für die Haltung, hässlichen Code nicht zu entfernen, bevor man verstanden hat, warum er existiert
- Laufender Code verdient Respekt, auch wenn er nicht perfekt ist; je größer das System, desto mehr Zeit braucht man zuerst zum Verstehen
Microservices und Tools
- Microservices fügen dem ohnehin schwierigen Problem, ein System richtig zu schneiden, zusätzlich Netzwerkaufrufe hinzu
- Tools erhöhen Produktivität und Verständnisfähigkeit von Entwicklern erheblich
- In einer neuen Umgebung sollte man Zeit investieren, um die Tools zu lernen
- Wenn man zwei Wochen Tools lernt, kann sich die Entwicklungsgeschwindigkeit verdoppeln
- Wenn es keine Dokumentation gibt, muss man andere Entwickler fragen und tiefer nachforschen
- Code Completion in IDEs sorgt dafür, dass man sich nicht jede API merken muss, und ist für Java-Entwicklung nahezu unverzichtbar
- Ein guter Debugger ist sehr wichtig
- Bedingte Breakpoints
- Auswertung von Ausdrücken
- Stack-Navigation
- Neue Entwickler sollten den verfügbaren Debugger gründlich lernen; das kann ihnen mehr über Computer beibringen als Universitätskurse
Typsysteme, Ausdrücke, DRY
- Der größte Wert eines Typsystems liegt in der Tool-Unterstützung, bei der nach dem Punkt eine Liste möglicher Aktionen erscheint
- Typsicherheit ist ebenfalls gut, aber Code Completion und Navigation haben den größeren praktischen Wert
- Entwickler, die Typsysteme zu abstrakt einsetzen, sind ein Warnsignal
- Generics können besonders gefährlich sein
- In den meisten Fällen bevorzugt er, sie auf Container-Klassen zu beschränken
- Statt kurze Bedingungen in eine Zeile zu quetschen, ist es für Debugging und Verständnis besser, sie in Zwischenvariablen mit aussagekräftigen Namen aufzuteilen
- DRY ist guter Rat, braucht aber Balance
- Einfacher und klarer duplizierter Code ist manchmal besser als komplexe Callbacks, Closures oder Objektmodelle
- Wenn das Entfernen von Duplikaten selbst die Komplexität erhöht, kann es zum Nachteil werden
Separation of Concerns, Closures, Logging
- Separation of Concern ist eine mächtige Idee, kann in der Praxis aber dazu führen, dass man zwischen vielen Dateien hin- und herwechseln muss, um Verhalten zu verstehen, und dadurch Zeit verschwendet
- Als Alternative bevorzugt er locality of behavior
- Wenn Code nahe bei dem liegt, „was funktioniert“, erkennt man beim Betrachten dieses Gegenstands sofort, was er tut
- Closures eignen sich gut, um Collection-Operationen zu abstrahieren, aber wie bei Salz reicht eine kleine Menge
- JavaScripts „callback hell“ ist ein Beispiel für Komplexität durch übermäßigen Einsatz von Closures
- Logging ist besonders in Cloud-Deployments sehr wichtig
- Bei jeder wichtigen logischen Verzweigung loggen
- Bei Requests über mehrere Maschinen hinweg eine Request-ID aufnehmen, damit Logs zusammengeführt werden können
- Wenn möglich, Log-Level dynamisch anpassen
- Wenn möglich, Log-Level pro Nutzer anpassen, um Probleme bestimmter Nutzer zu debuggen
- Logging-Bibliotheken können komplex sein, aber Investitionen in eine ordentliche Logging-Infrastruktur zahlen sich später stark aus
Nebenläufigkeit, Optimierung, API
- Nebenläufigkeit ist ein Bereich, vor dem man Respekt haben sollte; bevorzugt wird ein möglichst einfaches Modell
- Zustandslose Web-Request-Handler
- Remote-Work-Queues, die nicht voneinander abhängen
- Einfache APIs
- Im Web kann optimistic concurrency gut passen
- Auch Nebenläufigkeits-Datenstrukturen wie Javas ConcurrentHashMap sollte man vorsichtig verwenden
- Optimierung sollte erst beginnen, nachdem ein konkretes Problem durch ein reales Performance-Profil bestätigt wurde
- Nicht nur auf die CPU schauen
- Netzwerkzugriffe entsprechen vielen CPU-Zyklen und sollten möglichst reduziert werden
- Wenn man bei verschachtelten Schleifen sofort damit beginnt,
O(n^2)zu entfernen, kann die Komplexität wachsen
- Eine gute API sollte Entwickler möglichst wenig zum Nachdenken zwingen
- Wenn API-Autoren in Implementierungs- oder Domänensicht gefangen sind, wird die API schwer benutzbar
- Für einfache Fälle sollte eine einfache API bereitstehen, für komplexe Fälle separat eine komplexere API; diese Schichtung ist gut
- Bei objektorientiertem Design sollte Verhalten möglichst auf dem betroffenen Objekt liegen
- Javas stream/collector-Beispiel wird als API kritisiert, die häufige Aufgaben unnötig umständlich macht
Parsing, Visitor Pattern, Frontend
- Parser im Stil von recursive descent hält er für unterhaltsam und schön
- Parser Generatoren erschweren das Verstehen und Debuggen des generierten Codes und verbergen die rekursive Natur der Grammatik
- Reale Produktions-Parser sind meist recursive descent; Bob Nystroms Crafting Interpreters wird empfohlen
- Das Visitor pattern wird knapp als „bad“ zusammengefasst
- Frontend und Backend zu trennen und SPA-Bibliothek sowie GraphQL-JSON-API per HTTP zu verbinden, kommt dem Bau zweier Komplexitätsnester nahe
- Er warnt vor dem Trend, selbst für einfache Websites oder Formulare, die Daten in eine Datenbank schreiben, große Frontend-Bibliotheken zu verwenden
- Um Komplexität zu senken, wurden htmx und hyperscript entwickelt
- Einfaches HTML bleibt erhalten
- Viel JavaScript wird vermieden
- React ist für Jobs und einige Anwendungstypen besser, wird aber als Entscheidung gesehen, den Weg in Frontend-Komplexität einzuschlagen
Moden, FOLD, Impostor Syndrome
- In der Entwicklung gibt es viele Moden, besonders stark im Frontend
- Gegenüber neuen Ansätzen, die als revolutionär gelten, sollte man skeptisch sein
- Im Computerbereich wurden viele Ideen bereits mindestens einmal ausprobiert
- Schlechte Ideen können unter neuem Namen zurückkehren
- Es ist gut, wenn Senior-Entwickler öffentlich sagen: „Das ist zu komplex und schwer zu verstehen“
- FOLD steht für Fear Of Looking Dumb, also den Zustand, Komplexität nicht einzugestehen, weil man fürchtet, dumm zu wirken
- Wenn ein Senior sagt, dass etwas komplex ist, fällt es Junioren leichter zuzugeben, dass sie es nicht verstehen
- FOLD ist besonders bei jungen Entwicklern eine wichtige Quelle dafür, dass Komplexität Macht gewinnt
- Entwickler schwanken zwischen dem Gefühl, alles zu beherrschen, und dem Gefühl, gar nichts zu wissen; Impostor Syndrome ist verbreitet
- Wenn sich alle wie Impostors fühlen, ist niemand ein Impostor, und junge Entwickler können ihre Karriere trotz Frust und Sorgen fortsetzen
Leseempfehlungen und Fazit
- Leseempfehlungen
- Das abschließende Fazit ist dasselbe wie am Anfang: Komplexität ist sehr, sehr schlecht
1 Kommentare
Hacker-News-Kommentare
Es ist seltsam, wie kluge Leute wie Motten vom Licht von Komplexität angezogen werden
Es dauert Jahre, zu lernen, gegen den Drang zum Overengineering anzukämpfen
Wenn man es einmal zu sehen beginnt, lässt es sich schwer ignorieren, und inzwischen kann man sofort erkennen, ob Code overengineered ist
Leider wirken etwa 99 % des Codes overengineered, und es gibt starke Anreize für Entwickler, ihre eigene Unersetzlichkeit und abrechenbaren Stunden zu maximieren
Selbst Entwickler, die völlig mechanisch und egolos wirken, verfallen oft dem Overengineering; das läuft unbewusst ab
Viele Entwickler denken nicht bewusst über jede einzelne Zeile Code nach, die sie schreiben; sie setzen sich ein Ziel und produzieren dann einfach den ersten Code, der sie diesem Ziel ein Stück näherbringt, ohne zu merken, dass es bei jedem Schritt eine bessere Alternative gegeben hätte
Erstens mag ich interessante Rätsel. Gerade kommerzieller Code ist, wenn man ihn richtig macht, oft ziemlich langweilig, sodass ich unbewusst Features vorantreibe, deren Implementierung Spaß macht. Und „Spaß macht“ bedeutet hier letztlich: Features, die alles übermäßig kompliziert machen
Zweitens erscheinen mir beim Programmieren alle Entscheidungen, die ich treffe, offensichtlich und notwendig. Erst später, wenn ich Abstand vom Code hatte und ihn mit frischem Blick wieder verstehen will, sehe ich, wie sehr ich ihn vermurkst habe
Bei den meisten Problemen ist die naheliegendste Lösung oft ziemlich komplex, und um zu wissen, wo man die einfache Lösung findet, braucht man Weisheit, Können und Domänenwissen. Einfache, saubere Lösungen sind fast nie offensichtlich
„Wir implementieren hier nach und nach eine halb selbst gebaute Message Queue. Nehmen wir einfach eine Standard-Message-Queue“, „Wir bauen hier ein fehleranfälliges, eigenes binäres Framing-Protokoll. Nehmen wir einfach protobuf/msgpack“, „Bauen wir kein eigenes RPC-Protokoll, um Daten abzurufen, sondern nutzen REST über HTTP. Dann können wir nginx dazwischenschalten, um Backend-Antworten zu cachen, und unseren Custom-Cache wegwerfen“
Letztlich kann man perfekten, einfachen, wiederholungsfreien Code schreiben, wenn man Zeit hat und dafür belohnt wird. Meistens wird aber belohnt, etwas auszuliefern und anhand des Aufwands, der Intelligenz und der Designfähigkeit, die in die Auslieferung geflossen sind, technische Kompetenz zu demonstrieren
Die natürliche Folge ist, dass alles zu aufgeblähten, overengineerten Haufen aus Provisorien wird, die alle 3 bis 7 Jahre neu geschrieben werden
Daten dazu habe ich nicht gesehen, aber ich vermute, dass die „Rewrite-Halbwertszeit“ mit Fluktuation oder durchschnittlicher Betriebszugehörigkeit zusammenhängt. Selbst wenn sich Leute stärker bemühen, nicht zu overengineeren, ist es ohnehin ziemlich wahrscheinlich, dass es neu geschrieben wird
Als Perfektionist stört mich das sehr, aber manchmal wird man dafür besser belohnt, etwas Komplexes und Overengineertes zu bauen, als länger über die beste einfache Lösung nachzudenken. Es mag bessere Organisationen geben, aber in 9 Jahren als Software Engineer habe ich sie noch nicht gefunden
Das beste Gegenbeispiel, das mir einfällt, sind Open-Source-Projekte, aber auch gute Bibliotheken werden oft neu geschrieben oder bekommen neue Versionen, wenn die Maintainer wechseln. Zwischen Open Source und dem Unternehmens-Kleinkram, den viele Vollzeit-Software-Engineers auf HN sehen, unterscheiden sich die finanziellen Anreize stark. Das ist ein bisschen wie der Vergleich zwischen einem sorgfältig ausgearbeiteten wissenschaftlichen Paper und einer schlampig geschriebenen Arbeits-E-Mail
Ich meine mich zu erinnern, dass Casey von Handmade Hero seinen Programmierstil „kompressionsbasiert“ genannt hat. Man schreibt erst einmal Code und trennt dann schrittweise heraus, was zusammengehört
Man abstrahiert, wenn sich etwas wiederholt, nicht indem man bewusst entwirft und dann abstrahiert. Ich nutze diesen Ansatz immer häufiger
Das heißt nicht zwingend, dass es an sich falsch ist, aber wenn es darum geht, die Qualität der Arbeit anderer Leute zu beurteilen, scheint mir eine starke Verzerrung sehr wahrscheinlich
Ich liebe diese Website wirklich und muss immer lachen. Mein Lieblingsteil sind besonders die Microservices
„grug fragt sich, warum großes Gehirn das schwierigste Problem überhaupt nimmt, nämlich ein System richtig zu zerlegen, und dann auch noch Netzwerkaufrufe hineinsteckt. Für grug wirkt das sehr verwirrend“
„Komplexität ist sehr, sehr schlecht“
„Wenn grug zwischen Komplexität und einem 1-gegen-1-Kampf mit einem Tyrannosaurus wählen muss, wählt grug den Tyrannosaurus. Wenigstens kann grug den Tyrannosaurus sehen“
Beim Thema Typen habe ich Einwände
grug sagt, der Wert eines Typsystems liege zu über 90 % darin, dass magisch eine Liste möglicher Dinge erscheint, wenn man auf der Tastatur den Punkt drückt. Aber unsere Juniors deployen in Production häufig Code, der wegen Null-Zugriffen kaputtgeht, und Sentry meldet uns das
In intensiven Entwicklungsphasen gibt es pro Junior-Entwickler ungefähr einen erkannten Null-Zugriffs-Bug pro Tag
Wenn man ein ordentliches Typsystem mit statischer Prüfung nutzt, würde die IDE sagen: „Das könnte null sein, bist du sicher?“ — das wäre enorm hilfreich
Und Autovervollständigung ist auch ohne statische Typen möglich
Die Stelle „big brain type system wizards sagen, Typkorrektheit sei der Kern eines Typsystems, aber grug hat gesehen, dass solche Wizards nicht oft Code deployen. Nicht deployter Code mag in gewissem Sinne korrekt sein, aber das ist nicht die Korrektheit, von der grug spricht“ ist unhöflich und unnötig
Ich habe hauptsächlich Code in C, PHP, Python, Haskell und typisiertem Python ausgeliefert; in typisierten Sprachen ist die Rate der Bugs, die es bis in Production schaffen, deutlich niedriger. Das ist einer der Gründe, warum ich Typen mag
Außerdem wird Refactoring viel einfacher. Wenn man etwas breit Genutztes ändert, kann man binnen Sekunden zuverlässig die kaputten Call Sites in der gesamten Codebase sehen, und das hilft enorm dabei, eine wachsende Codebase iterativ zu verbessern
Stattdessen akzeptierst du ein pragmatisches Typsystem, das einige Probleme erkennt und vor allem Autovervollständigungs-Magie bietet, ohne dich mit allen Details aufzuhalten, die für einen vollständigen Korrektheitsbeweis nötig wären
Er scheint statische Typen auf dem Niveau von C oder Java okay oder gut zu finden, während er die ausgefalleneren Dinge, die man in Rust oder Scala sieht, für Zeitverschwendung hält. Ich denke, dass er damit völlig falschliegt, aber so ist das Leben
Ich habe „big brain type system wizards“ so verstanden, dass damit nicht zwingend Leute gemeint sind, die Code schreiben, sondern zum Beispiel solche, die zu diesem Thema Kurse verkaufen und Leute dafür heißmachen
Weiter oben im Text wird auch bei Agile der Begriff „Wizards“ verwendet; es gibt ziemlich viele Leute, deren Tätigkeit eher dem Verkaufen von Kursen ähnelt als echter Softwareentwicklung
Mit Leuten zu arbeiten, die von Typhierarchien besessen sind, ist schwer auszuhalten, und die Rechtfertigung bleibt meist in einer Welt aus „was wäre, wenn“ und „aber das ist nicht sound“ stecken
Viele Qualitätsverschlechterungen, die durch Typsysteme verursacht werden, kommen von Leuten, die glauben, man könne Bugs vollständig verhindern, wenn man nur genug Typen einbaut. Das stimmt nicht
Ich glaube, in den nächsten Monaten werden alle auf den HTMX-Zug aufspringen, vielen jungen Entwicklerinnen und Entwicklern den Kopf verdrehen, weltweit viel Energie sparen und viele Menschen glücklich machen
Und wenn diese neugierigen jungen Leute dann weiter auf die Links auf htmx.org klicken und hyperscript entdecken, wird es wie der Moment in Dusk till Dawn sein, in dem die Vampire hervorspringen
Jemand hat die JavaScript-Komplexität satt und baut eine einfache JavaScript-Library, die Leute strömen wegen dieser Einfachheit hin, die Library muss alle Web-Features unterstützen und wird komplex, und dann hat wieder jemand diese Komplexität satt und baut eine einfache JavaScript-Library
Ich habe mit reinem JavaScript angefangen, ein paar Jahre mit IE5/6 gekämpft, jQuery hat uns alle gerettet, AngularJS war nicht so toll, also habe ich es übersprungen, und als die React-Beta kam, mochte ich sie wegen ihrer funktionalen Programmierideale. Jetzt wühle ich mich durch Transpiling, Hot Reload, Typescript und einen Haufen schwergewichtiger React-Libraries
Ich habe auch ein paar Projekte mit Intercooler und HTMX gebaut. HTMX ist insgesamt solide, aber es ist nicht der erste Zug, der durch diese Stadt fährt
Ich würde gern echte Kritik an htmx sehen
Persönlich glaube ich, dass eine große, dauerhafte Herausforderung des Webs darin bestand, einen guten Weg zu finden, Seiten zu aktualisieren, und dass wir immer wieder alle möglichen Ansätze ausprobiert haben. Daraus entsteht Müdigkeit, und manche Ideen werden radikal oder mit der Zeit zu einem Sammelsurium
Htmx wirkt wie eine Gegenreaktion im grug-Stil: weniger machen, auf YAGNI beharren
Die Zukunft ist ungewiss, aber Arbeiten wie signals wirken weiterhin wie eine heilige Aufgabe. In viele Richtungen wird viel ausprobiert, und wir erschließen diese Front und andere Fronten noch immer
Auch MobX und Svelte haben bereits eine lange Reise hinter sich. Dass die Reise noch nicht zu Ende ist und wir weiter mit dem Monster der Komplexität ringen, ist meiner Meinung nach weniger ein Zeichen von Schwäche — auch wenn konservatives oder grug-artiges Denken das so sehen mag —, sondern eher die angemessene Anstrengung, nicht bloß zu einem einfachen Ansatz zu gelangen, sondern zu etwas Einfachem und Gutem
_="on load wait 5s then transition opacity to 0 then remove me"Oh nein
_="on htmx:error(errorInfo) fetch /errors {method:'POST', body:{errorInfo:errorInfo} as JSON} "Oh nein!
Ich arbeite seit 30 Jahren als Entwickler und gebe zu, dass ich in den Anfangsjahren arrogant war und dachte, ich sei klüger als andere. Damals war ich auf der „big brain“-Seite und mochte all die Komplexitätsdämonen
Zehn Jahre später war ich eher ein „Grug brain“-Entwickler, und heute konzentriere ich mich auf die einfachste funktionierende Lösung, auch wenn ich weiß, dass sie vielleicht nicht perfekt ist. Das ist trotzdem in Ordnung, weil sie einen näher an das Richtige heranführt und iterative Verbesserungen ermöglicht
Das Beste, was man als Entwickler tun kann, ist Code löschen. Bei uns ist gerade eine Anforderung, die wir zwei Jahre lang gepflegt haben, plötzlich keine Anforderung mehr, und ich freue mich wirklich darauf, eine Menge Code herauszureißen. Denn dadurch wird alles einfacher
Es ist, als könnte man körperlich spüren, wie Raum frei wird
Grug tauchte auch vor einer Woche in den Kommentaren zum Artikel „Philosophy of Software Design“ auf, und ich fand die Diskussion damals ziemlich gut. https://news.ycombinator.com/item?id=38011938
„Ich empfinde bei grug exakt dasselbe. Ich glaube nicht, dass sich Menschen tatsächlich darüber einig sind, was einfach ist; deshalb wirkt es wie Angeberei, so zu tun, als sei die eigene ‚Einfachheit‘ etwas Offensichtliches, dem sogar ein Höhlenmensch zustimmen würde.“
Einfachheit ist oft eines der komplexesten Dinge, die man entdecken kann. Ich habe viele Leute gesehen, die überzeugt sind, dass alles um sie herum komplex sei und in Richtung Einfachheit gebogen werden müsse — alles außer dem, woran sie selbst arbeiten. Das wirkt wie eine gefährlich schwache Haltung, die sich an Autorität klammert.
Die Grug-Brain-Philosophie besteht darin, um jeden Preis Einfachheit zu wählen, außer wenn sie absolut unvermeidbar ist.
Die Big-Brain-Philosophie aus Grugs Sicht besteht darin, um jeden Preis Wiederverwendbarkeit zu wählen, außer wenn sie absolut unvermeidbar ist.
Das Problem dieser Philosophie ist, dass sie Designentscheidungen der ersten Generation festschreibt und iterative Verbesserung erschwert.
Es stimmt, dass Einfachheit schwer zu finden ist, aber der Schlüssel zum Finden von Einfachheit ist iterative Verbesserung.
Lokale Minima verschieben sich je nach Komplexität dessen, was man baut, sodass durchaus mehrere ziemlich kluge Sichtweisen gleichzeitig richtig sein können.
Es ist kein perfekter Indikator, aber die Richtung stimmt, und auf einer Log-Skala funktioniert er sehr gut.
Ich habe das Konzept von Chestertons Zaun unzählige Male verwendet und erklärt; schön, jetzt den Namen dafür zu kennen.
Bei der Arbeit mit neuen Entwicklern passiert das wirklich oft. Wenn sie „alten Legacy-Müll“ sehen, ist ihre erste Reaktion, ihn herauszureißen oder alles wegzuwerfen und von vorn neu zu bauen.
In manchen Fällen kann es lehrreich sein, sie es selbst versuchen zu lassen, aber man sollte im Hinterkopf behalten, dass die Leute vor uns nicht alle komplette Idioten waren und es meistens einen Grund gab, warum sie es so gemacht haben.
Manchmal ist es tatsächlich schmutziger alter Code, der ersetzt werden sollte; aber selbst dann enthält er oft hart erarbeitete Aufzeichnungen über all die Ecken und Randfälle, die man verstehen und behandeln muss, um in dieser Domäne etwas zu bauen.
Der Abschnitt über Tests ist wirklich gut. Er passt genau zu dem, was ich über Jahre gelernt habe.
Um Bugs zu finden, sind Integrationstests der richtige Punkt.
Mock-Objekte neigen dazu, Dinge übermäßig zu verkomplizieren; ich benutze sie gelegentlich immer noch, vermeide aber ihren systematischen Einsatz.
Unit-Tests sind beim Refactoring zu fragil, während Integrationstests beim Refactoring helfen.
Unit-Tests sind schwieriger zu schreiben und zu pflegen, aber wenn sie fehlschlagen, sind sie viel leichter zu verstehen und zu debuggen, und halten langfristig deutlich besser durch.
Die schlimmsten Tests sind Integrationstests, die heimlich davon abhängen, dass vorher ein anderer Integrationstest gelaufen ist. In 99 % der Fälle passt es, aber sobald sich durch irgendeine Änderung die Reihenfolge ändert, brechen sie.
Wenn ein Team einfachen Code hat, helfen Tests sehr. Wenn ein Team aber keinen einfachen Code hat — und meistens hat es den nicht —, ist es besser, die Zeit statt ins Schreiben von Tests in die Vereinfachung des Codes zu stecken.
Eine einzelne Blattfunktion als Unit zu nehmen ist oft zu niedrig angesetzt; ein größeres Modul mit einer relativ stabilen Schnittstelle kann aber eine gute, produktiv testbare Unit sein.
„Komplexität ist sehr, sehr schlecht.“
„Die beste Waffe gegen den Seelendämon der Komplexität ist das magische Wort ‚Nein‘.“
„Traurig, aber wahr: Nachdem man ‚Ja‘ gelernt hat, ist der ideale Karriereratschlag, nach einem Scheitern zu lernen, anderen Grugs die Schuld zu geben.“
Grugs komplexe Weisheit.
Wurde damals ebenfalls diskutiert.
The Grug Brained Developer - https://news.ycombinator.com/item?id=31840331 - Juni 2022, 374 Kommentare