- Die Erfahrung, den Kern eines Programms, das ich zwei Jahre lang selbst genutzt und verbessert hatte, innerhalb eines Monats neu zu schreiben, hat meine bisherigen Überzeugungen zu Tests und Versionsverwaltung erschüttert
- 2015 hielt ich Tests und Versionen für wichtiger als schlechte Abstraktionen, wenn es um langlebige Software geht, aber über Mu und Freewheeling Apps hinweg hat sich meine tatsächliche Arbeitsweise immer weiter verändert
- Ich denke, langlebige Programme sollte man nicht für möglichst viele Menschen bauen, sondern innerhalb eines Rahmens aus Personen, Kontexten und Funktionen, die man gut kennt, und dabei reale Grenzen wie Dunbar's number akzeptieren
- Typen, Abstraktionen, Tests, Versionen, Zustandsmaschinen, Immutability und formale Analyse sind in unbekanntem Terrain nützlich, werden bei Übermaß aber zu technischen Schulden, die unnötige Komplexität verdecken
- Wenn das Verständnis des Kontexts stabil wird, lohnt es sich, große Teile wegzuwerfen und neu zu bauen; dazu muss man die nötigen Szenarien gleichzeitig im Kopf halten und das Ganze in einem Zug zusammensetzen
Wandel im Denken über Tests und Versionsverwaltung
- Ich beschäftige mich seit langem mit der Frage, welche Programme man langfristig zuverlässig nutzen kann und wie man sie selbst baut, habe aber nicht das Gefühl, darin besonders gut zu sein
- Im letzten Monat habe ich den Kern neu geschrieben eines Programms, das ich zwei Jahre lang benutzt und schrittweise verändert hatte
- Danach folgten einige Tage, in denen ich ordnete, was ich gelernt hatte und wohin es als Nächstes gehen soll
- Durch diese Arbeit wurde auch ein breiterer Wandel sichtbar, der sich über mein ganzes Leben zieht
- 2015 war ich misstrauisch gegenüber Abstraktionen und legte großen Wert auf Tests und Versionsverwaltung
- Im Code gibt es viele schlechte Abstraktionen, und Tests sowie Versionen erschienen mir als zentrale Fortschritte der 2000er Jahre
- Die Ursachen der Probleme sah ich in schlechten Anreizen, übermäßiger Abstraktion sowie unzureichenden Tests und Versionierung
- Mu1 war ein Versuch, eine Plattform zu entwerfen, die Tests und layers als grundlegende Einschränkungen verwendet
- 2017 begann ich, Mu1 zum heutigen Mu umzuarbeiten
- Anfangs nutzte ich alle neuen Ideen zu Tests und Layers
- Mit der Zeit verwendete ich diese Ideen immer weniger
- Im heutigen Mu gibt es viele Tests, aber größtenteils gewöhnliche Tests, und die Layers-Infrastruktur konnte ich nicht übernehmen
- 2022 begann ich mit Freewheeling Apps
- Anfangs gab es keine Tests, später schrieb ich gründliche Tests für den Texteditor als zentrales Teilstück
- Für den Rest war es schwer, eine Teststrategie zu finden, trotzdem kam ich auch ohne Tests weit genug voran
- 2024 löschte ich alle Tests
- Ich begann, den Texteditor umfassend neu zu bauen, und diese Arbeitsweise hätte eigentlich Sorgen über Merge-Konflikte mit anderen Freewheeling Apps auslösen können
- Am Ende hörte ich auch auf, über Versionsverwaltung nachzudenken
- Nachdem ich Tests und Versionen aufgegeben hatte und dadurch bessere Programme bekam, ließ sich die kognitive Dissonanz zu meinen alten Überzeugungen nicht länger ignorieren
Meine aktuelle Einordnung langlebiger Programme
- Etwas zu bauen, das für viele Menschen lange Bestand hat, ist zu schwierig; deshalb ist es meiner Meinung nach besser, gar nicht erst mit diesem Ziel zu starten
- Man sollte sich von dem leiten lassen, was man gut kennt, von Menschen, die man gut kennt, und von Dunbar's number
- Ich glaube, der Großteil der Softwarewelt ist von Anreizen geprägt, kurzfristig möglichst viele Menschen zu bedienen
- Ich konzentriere mich möglichst auf Software, auf deren Website nicht viele Logos zu sehen sind
- Ich bevorzuge Software, die leicht zu bauen ist, wenige Abhängigkeiten hat und keine automatischen Updates macht
- Unter diesen Einschränkungen bleibt von der langlebigen Software, die die Menschheit bisher geschaffen hat, nur sehr wenig übrig
- Schon kleine Änderungen im Kontext, etwa bei Personen, Orten oder den zu unterstützenden Funktionen, können stark verändern, wie gut ein Programm zu seinem Kontext passt
- In einer von Kurzfristigkeit beherrschten Umgebung ist es schwer, sich darauf vorzubereiten
- Weil der Umfang früherer Arbeiten klein ist und auch der Anwendungsbereich einzelner Programme begrenzt bleibt, führt jedes neu begonnene Programm wahrscheinlich in irgendeiner Form in unbekanntes Gelände
- Schon wenn man in einen Texteditor spezielle „drawing lines“ einbauen will, tauchen viele Fragen auf
- Kann sich der Cursor auf einer Zeichnung befinden
- Kann man auf einer Zeile zeichnen, wenn sich der Cursor auf einer anderen Zeile befindet
- Kann eine Zeichnung höher als die Textzeile sein und am oberen Bildschirmrand nur teilweise sichtbar sein
- Kann man auf eine nur teilweise sichtbare Zeichnung zeichnen
- Weil die Antworten auf diese Fragen lange nicht optimal waren, stapelte sich Provisorium auf Provisorium
- Schon wenn man in einen Texteditor spezielle „drawing lines“ einbauen will, tauchen viele Fragen auf
Werkzeuge sind nötig, werden im Übermaß aber zu technischen Schulden
- Typen, Abstraktionen, Tests, Versionen, Zustandsmaschinen, Immutability und formale Analyse sind Werkzeuge, die man in unbekanntem Gelände einsetzen kann
- Man sollte sie je nach Bedarf und eigenem Stil verwenden
- Menschen neigen dazu, die Werkzeuge zu übernutzen, zu denen sie sich hingezogen fühlen
- Ich glaube, die ideale Menge dieser Werkzeuge ist sehr klein
- Viel kleiner, als es das Gefühl nahelegt, das man in von Kurzfristigkeit geprägten Umgebungen lernt
- Wenn man diese Werkzeuge übermäßig einsetzt, werden sie zu technischen Schulden
- Dann wird es schwer, überhaupt zu bemerken, dass ein Programm unnötig komplex ist
- Das Programm hält dann weniger lange, als es eigentlich könnte
- Und wenn sich der Kontext ändert, wird es schwerer, das Programm anzupassen
Neuschreiben und „alles auf einmal bauen“
- Wenn das Verständnis des Kontexts stabil geworden ist, kann es wertvoll sein, große Teile eines Programms wegzuwerfen und von Grund auf neu zu beginnen
- Vor dem Neuschreiben muss man alles, was man vom Programm will, und alle Szenarien, auf die es reagieren soll, gleichzeitig im Kopf halten
- Das ist schwierig, aber das Ziel ist, einen Zustand zu erreichen, in dem man alles auf einmal bauen kann
- Die endgültige Form ist alles auf einmal zu bauen
- In dieser Erfahrung standen Tests und Versionen dem Erreichen dieses Endpunkts der Entwicklung eher im Weg
- Tests lassen einen die Probleme vergessen, um die man sich kümmern muss
- Versionsverwaltung hält einen ständig an der Vergangenheit fest
- Beides wirkte kontraproduktiv, und um es loszulassen, war ein großer Kurswechsel nötig
- Ich glaube, alle Software, die ich bisher gebaut habe, und auch Freewheeling Apps befinden sich auf Stufe 6 dieser Entwicklung
- Nur das Ergebnis des letzten Monats hat vielleicht Stufe 9 erreicht
- Ob das stimmt, muss sich aber erst noch zeigen
Grenzen der Komplexität und data-oriented design
- Wenn ein Programm zu komplex wird, kann es unmöglich werden, es auf Stufe 8 vollständig im Kopf zu halten
- Ich glaube, das trifft auf den Großteil der bisherigen Software zu, besonders auf Software, die von mehr als ein oder zwei Personen geschrieben wurde
- Sogar ein kleiner Texteditor fühlte sich belastend an, und ich verbrachte einen großen Teil des Monats damit, mich darauf vorzubereiten, dieser Angst zu begegnen
- Nicht jede Software muss unbedingt bis Stufe 9 gelangen
- Viele Freewheeling Apps sind einfach genug und entwickeln sich langsam weiter
- Ich glaube, dass Software, die nur von wenigen Menschen genutzt wird, sich allein dadurch unabhängig von frühen Designentscheidungen in einen bugfreien Zustand stabilisieren kann
- Insbesondere weiß ich jetzt, wie sich ein komplexes Kernstück vereinfachen lässt
- Trotzdem ist es gut zu wissen, wie man etwas verbessern kann, wenn es wertvoll wird
- Als Methode, die beim Erreichen von Stufe 9 eindeutig hilfreich erscheint, nenne ich data-oriented design
- Es ist kein Werkzeug, das man blind anwenden sollte, sondern eine Denkweise, die das Gesamtbild betrachtet, wie ein Programm auf Daten zugreift
- Werkzeuge wie ECS sollten diese wesentliche intellektuelle Arbeit nicht verdecken
- Diese Einteilung in Stufen ist möglicherweise nicht ganz richtig
- Vielleicht unterschätze ich Werkzeuge, mit denen ich wenig Erfahrung habe
- Und was jenseits dieser Stufen liegt, ist weiterhin eine offene Frage
- Schon in dem 2019 geschriebenen Text über meine Art zu programmieren lassen sich Spuren dieses Denkwandels erkennen
1 Kommentare
Meinungen auf Hacker News
Wenn es keine Tests gibt, sieht man auch keine fehlgeschlagenen Tests; es wirkt nur so, als wäre das Problem verschwunden.
Ich habe noch nie etwas getestet und dabei keinen Bug gefunden, und die meisten Dinge, die ich getestet habe, waren solche, von denen ich bereits dachte, sie seien reif für den Release.
Wenn man Tests löscht, täuscht man am Ende wahrscheinlich nur sich selbst. Beim Lesen des Beitrags wirkt es so, als sei der Autor weniger von Tests an sich erschöpft als von Varianten-/Konfigurationsmanagement, und das kann ich gut nachvollziehen. Allerdings verdient man erst mit Nutzerzahlen Geld, und wäre es ein einfaches Problem, wäre der Markt längst mit Allzwecklösungen gesättigt.
Wenn sich UI oder Workflows zu schnell ändern, schreibt man keine Tests, weil man weiß, dass sie in der nächsten Iteration nutzlos werden; ändern sie sich dagegen zu langsam, fasst man diesen Teil ohnehin kaum noch an, sodass durch Refactoring auch seltener neue Bugs hineinkommen. Tests oder Typen sind kein heiliger Gral, sondern Werkzeuge, die zur Aufgabe passen müssen. Ich habe noch keine Codebasis mit guter Testabdeckung gesehen, in der durch manuelles Testen oder tatsächliche Nutzung keine Bugs gefunden wurden. Etwas überspitzt gesagt: Wenn man gut genug ist, um perfekte Tests zu schreiben, kann man auch einfach perfekten Code schreiben. Wenn man Tests nicht perfekt schreiben kann, woher weiß man dann, dass diese Tests vollständig, fehlerfrei und wirklich nützlich sind?
Alle paar Monate habe ich einen neuen Bug gefunden und gewissenhaft Tests hinzugefügt, aber ein paar Monate später hat jemand, der das Programm zum ersten Mal zehn Minuten benutzt hat, wieder einen neuen Bug entdeckt. Auch in der neuen Version wird es Bugs zu finden geben, aber ich denke, dass dank der gewählten Datenstrukturen ein erheblicher Teil der alten Tests strukturell nicht mehr nötig ist. Zumindest bei leichter Nutzung hoffe ich, dass es ziemlich stabil sein wird, sobald noch ein paar Bugs behoben sind. Tests sind sehr wertvoll, wenn große Teams eine Codebasis ständig verändern, aber hier geht es darum, etwas mit einem festen Funktionsumfang zu bauen.
Es enthält sehr viele nützliche Ratschläge, aber besonders erwähnen möchte ich den Punkt, dass man gegen eine einfachere Implementierung testen kann, etwa eine Brute-Force-Implementierung. Darin steckt eine tiefere Einsicht. Die Nützlichkeit eines Tests hängt davon ab, wie viel einfacher die Testimplementierung im Vergleich zur getesteten Implementierung ist. Stärker formuliert: Tests sind nur nützlich, wenn sie einfacher sind als das, was sie testen. Egal wie viele Tests man schreibt, am Ende muss man den Code trotzdem durchdenken, und etwas wird nicht allein dadurch nützlich, dass es ein „Test“ ist. Deshalb sind meiner Meinung nach viele Programmierer vorsichtig, wenn Funktionen in Teile zerlegt werden, die nicht wegen nützlicher Interfaces, sondern nur wegen leichterer Testbarkeit entstehen; wenn einfache Helper oder kleine Queries nur der Coverage wegen getestet werden; oder wenn für Tests Abstraktionen eingeführt werden, also Dependency Inversion und Mocking. Natürlich kann es für all das jeweils Gründe geben, aber wichtig ist, den Kern nicht aus den Augen zu verlieren.
Meist mache ich kein Test-Driven Development, bei dem man zuerst einen fehlschlagenden Test schreibt, manchmal aber schon. Deshalb richten sich solche Tests in der Regel gegen Code, von dem ich bereits dachte, dass er funktioniert. Allerdings bevorzuge ich normalerweise einen Test-Harness gegenüber Unit-Tests[0]. Ich finde dabei weiterhin Bugs, aber der Ablauf ist weniger linear. Er sorgt dafür, dass ich während der Entwicklung mehr teste und Bugs direkt an Ort und Stelle behebe.
[0] https://littlegreenviper.com/testing-harness-vs-unit/
Der Linux-Kernel zum Beispiel hatte früher nicht viele Tests, und heute scheinen es mehr zu sein. Unix hatte vermutlich auch nicht viele „Tests“. Compiler hatten eher Tests, Betriebssysteme weniger, und Spiele wie Doom hatten wahrscheinlich ebenfalls nicht viele Tests. Am Ende muss man einen Gleichgewichtspunkt finden. Ich weiß, dass automatisierte Tests, also Unit-, Integrations- und End-to-End-Tests, dabei helfen, hochwertige Software zu bauen. Gleichzeitig sind gute Tests nicht immer leicht zu schreiben, schlechte Tests erschweren Refactorings, und instabile Tests fressen in großen Projekten viel Zeit. Gerade wenn man allein entwickelt, ist es trotzdem interessant, verschiedene Ansätze auszuprobieren und herauszufinden, was zu einem passt.
Der Teil „Nachdem ich Tests und Versionen aufgegeben hatte, wurde es ein viel besseres Programm“ ist schwer nachzuvollziehen. Ich weiß nicht, wer 2024 freiwillig ohne Source-Code-Management programmieren will.
Selbst bei einem Ein-Personen-Projekt liefert die Möglichkeit, auf mehreren Geräten zu arbeiten, die Historie anzusehen, Änderungen zurückzunehmen und Branches zu verwenden, bei nahezu keinen Kosten einen enormen Wert. Vielleicht habe ich auch missverstanden, was der Autor mit „Version“ meinte.
Es stimmt, dass dieser Ansatz nicht zu den meisten Programmen passt, die heute entwickelt werden, also zu großen Teams und sich ständig ändernden Anforderungen. Source Control nutzt er trotzdem weiterhin. Wie im Original gesagt, hat er lediglich aufgehört, sich Sorgen über Merge-Konflikte mit anderen Forks zu machen. Inzwischen gibt es mehr als 24 Forks; Details stehen im obigen Link. Für grundlegende Zwecke wie Backups, „Was habe ich gerade geändert?“ oder das Aufsetzen der Software auf einem neuen Gerät nutzt er Versionsverwaltung. Nur bei diesem Programm sieht er Versionsverwaltung nicht mehr als Mittel zum Verstehen und Nachverfolgen dessen, was sich geändert hat. Weitere Details gibt es unter https://akkartik.name/post/wart-layers. Zum Beispiel achtet er weniger auf saubere Commit-Messages. Versionsverwaltung existiert, aber in diesem engen Kontext – etwas mit festem Funktionsumfang zu bauen, ein langlebiges Artefakt, das Jahrzehnte halten soll – ist sie als „gute Programmierpraxis“ in der Priorität gesunken.
Es wirkt auch nicht so, als würde er an einem großen System arbeiten oder wichtige Teamarbeit leisten. Unter solchen Bedingungen bringen die Tools möglicherweise nicht viel Wert. Ein Flötist in einem großen Orchester, das eine komplexe Sinfonie aufführt, braucht Noten und einen Dirigenten; wer aber allein zu einer Drum Machine spielt oder Free Jazz macht, braucht Noten kaum – sie können sogar stören.
Wenn es aber immer 1000 einfache Optionen gibt, entsteht eine große kognitive Belastung, um die richtige Wahl zu treffen. Das ist auch einer der Gründe, warum die Branche alle möglichen Best Practices sakralisiert und sozialen Druck auf diejenigen ausübt, die ihnen nicht folgen. Schlechte Architektur und grauenhafter Spaghetti-Code machen die Arbeit sehr schwer; aber wenn man scheinbar offensichtlich richtige Dinge hinterfragt und eine strengere Entwicklungsumgebung erkundet, die Optionen und Tools reduziert, kann man sich stärker auf das eigentliche Problem konzentrieren. Auch Versionsverwaltung verleitet dazu, Programme über Branches in „unabhängige Features“ zu zerlegen; die Historie lässt einen Funktionseinheiten, die veraltet sein können, blind weiterverwenden; und Zusammenarbeit verfestigt meist irrelevante Organisationsgrenzen in der Code-Architektur. Das berührt auch das, was Mel Conway gesagt hat. Die Vorteile von Versionsverwaltung sind Common Sense, aber auf der Ebene „Geschäftsproblem X lösen“ gibt es reale Trade-offs. Dass diese Trade-offs auf Branchenebene kaum sichtbar sind, ist aufschlussreich.
Anfangs dachte ich, der Autor läge völlig falsch, aber es gibt trotzdem einige gute Einsichten.
Dieser Workflow passt für den Autor sehr gut. Die meisten von uns können sich an Zeiten erinnern, in denen Git oder automatisierte Tests frustriert oder die Produktivität gesenkt haben. Es gibt auch einfachere, weniger störende Lösungen wie Code-Backups über Dropbox, FTP usw. Der Grund, warum der obige Ansatz gut passt, ist, dass der Autor seine eigene Produktivität bei einem persönlichen Herzensprojekt optimiert, an dem er mit wenigen Leuten zusammenarbeitet. Automatisierte Tests sind nützlich, aber der Autor scheint gern Programme zu bauen, die klein genug sind, dass ihr Wert schwer sichtbar wird. Auch in diesem Kontext sehe ich den Wert automatisierter Tests, aber wir können uns wohl alle darauf einigen, dass automatisierte Tests bremsen. Natürlich werden viele argumentieren, dass sich der Aufwand später auszahlt. Versionsverwaltung und automatisierte Tests lösen echte Probleme. Heute ein Projekt ohne Versionsverwaltung zu starten, ergibt keinen Sinn, und es gibt Gründe, warum automatisierte Tests als Best Practice gelten. Für den speziellen Anwendungsfall des Autors klingt es jedoch plausibel. Lässt man die kontroversen Teile zu Versionsverwaltung/Tests weg, erfassen die Punkte 7/8/9 meine Denkweise beim Schreiben und Refactoring großer Programme perfekt: schreiben, wegwerfen, neu schreiben.
Menschen machen Fehler, und bei einem Projekt mit über 100.000 Zeilen Code ist es enorm hilfreich zu wissen, was man in den letzten drei Wochen geändert hat. Es hilft, Probleme zu finden und zu beheben. Noch besser ist, dass Branches erlauben, Dinge auszuprobieren, die man ausprobieren will, während man weiterhin einen Weg zurück zu einem früheren stabilen Zustand behält. Automatisierte Tests halte ich dagegen für verzichtbar.
.gitignoreeinzurichten undgit init,git add -A,git commit -a -m "before I changed the foo function to use bar"auszuführen, sodass man zu einer früheren Revision zurückkehren kann.Man muss Git nicht meistern, aber allein Commit-Messages und eine Version, zu der man zurückkann, haben mich unzählige Male gerettet. Von fortgeschritteneren Funktionen ganz zu schweigen.
Das ist ein ziemlich verwirrender Text. Ich frage mich wirklich, wodurch er es bis auf Platz 1 geschafft hat.
Die Hauptmotivation für eine vernünftige Testsammlung ist, Frustration zu reduzieren. Eine Testsammlung gibt Entwicklerinnen und Entwicklern das Vertrauen, ein System weiterzuentwickeln.
Wenn man sie richtig aufbaut, kommt einem oft auch ein Gedanke wie: „Für den Rest war es schwer, eine Möglichkeit zum Testen zu finden, und irgendwie lief es trotzdem ganz ordentlich.“ Mit zunehmender Komplexität der Funktionen kann es unbeherrschbar schwierig werden, Komponenten oder das gesamte System zu testen. Aber die Philosophie, dass der Verzicht auf Tests und Versionsverwaltung zu einem besseren Programm geführt habe, skaliert nicht über eine einzelne Person hinaus. Und selbst dann nur, wenn diese Person alle aktuellen und früheren Entscheidungen im Quellcode als frische, vertraute Erinnerung präsent hat. Wenn man die Implementierung außerdem sehr genau kennt, muss jede Änderungsprüfung per Definition manuell durchgeführt werden.
Wenn der Code am Ende des Tages nicht mergefähig war, bedeutete das für ihn, dass er das Problem nicht gut genug verstanden hatte, um es innerhalb eines Tages auszudrücken, und er versuchte es am nächsten Morgen neu. Ich weiß nicht, ob sich jemand daran erinnert oder ob ich das mit einer anderen Website oder Anekdote verwechsle.
Dokumentation, Tests und Versionsverwaltung reduzieren die Menge an Kontext zum Code, die ich im Kopf behalten muss. Die Details des Codes vor mir muss ich mir merken, aber wenn ich ihn dokumentiert, getestet und mit einer guten Commit-Message dazu eingecheckt habe, warum/wie ich ihn geändert habe, kann ich ihn aus dem Kopf entlassen und zur nächsten Aufgabe übergehen.
Ein gutes Beispiel für Punkt 3, dass „kleine Veränderungen im Kontext, etwa bei den Personen/Orten/Funktionen, die man unterstützen will, drastisch verändern, wie gut ein Programm zu diesem Kontext passt“, ist K9 Mail. Es ist inzwischen dabei, Thunderbird für Android zu werden.
K9 Mail begann mit einer unkonventionellen UI, die auf dem Startbildschirm die Liste der E-Mail-Konten anzeigte und für jedes Konto die Zahl der ungelesenen und aller Nachrichten zeigte. Es gab zwar einen gemeinsamen Posteingang, aber er wurde den Nutzern nicht aufgezwungen. Ich erinnere mich, dass ich diese App ausdrücklich gewählt habe, weil ich ein privates Konto, ein berufliches Konto und mehrere berufliche Konten von Kunden getrennt halten wollte. Wahrscheinlich haben viele K9-Nutzer die App aus demselben Grund gewählt. Deshalb gab es auch so viele Beschwerden, als der Entwickler zu einer traditionellen Android-UI wechselte, bei der die Kontoliste von links hereingleitet und man zum Wechseln zwischen Konten einen zusätzlichen Tap braucht. Wenn wir diese UI gemocht hätten, hätten wir K9 vermutlich von Anfang an nicht gewählt. Eine kleine Änderung also – wenn auch vermutlich mit viel Coding verbunden – ruinierte die Passung der App für ihre Nutzer. Ich benutze weiterhin 5.600, die letzte Version mit der alten UI, und sideloade sie jedes Mal, wenn ich ein neues Gerät kaufe. Noch ungewöhnlicher: Für den Kontozugriff verwende ich ausschließlich POP3. Mein Ablauf ist: auf dem Smartphone vorab ansehen, löschen, was gelöscht werden soll, mir bei Bedarf per Bcc antworten und die Mails letztlich auf dem Laptop herunterladen; K9 passte perfekt zu diesem Workflow. Ich brauche nichts Schickes, eine App auf dem Niveau der 90er reicht völlig.
https://news.ycombinator.com/favorites?id=akkartik&comments=t
Ich frage mich weiterhin, wohin dieser Weg führen wird. Eines ist klar: Software allein zu bauen ist eine völlig andere Tätigkeit, als sie im Team zu bauen.
Was Tests betrifft: Tests sind kein Ziel, sondern ein Mittel. Ich glaube, wonach wir suchen, ist Vertrauen. Wenn wir von der Implementierung überzeugt sind, testen wir weniger. Wenn es umgekehrt etwas gibt, das unbedingt weiter funktionieren muss, fügen wir ein paar Integrationstests an den äußeren Grenzen hinzu, die weniger von Refactorings betroffen sind und uns weniger ausbremsen. Also eher den Web-Backend von außen anstupsen, statt das Innere zu testen. Unit-Tests sind gut, um den Entwurf einer neuen API zu konkretisieren, aber sobald man die Richtung kennt, werden diese Tests fast nutzlos.
Ein if-Statement vorübergehend mit etwas wie
true ||festzuklemmen, um direkt zur Funktion zu gelangen, an der man arbeitet, kostet Zeit und muss später wieder entfernt werden. Man kann stattdessen einfach einen Test schreiben und ausführen, und ihn als Regressionstest behalten. Wenn man eine große oder langsame App ausliefert – manchmal reicht schon die Nutzung von Qt, damit Build oder Start lange dauern –, lädt und läuft ein einzelner Test schneller. Wenn das Reproduzieren eines Bugs 45 Sekunden dauert, ist ein Test die bessere Wahl. Man automatisiert den langweiligsten Teil der Arbeit, bleibt im Flow, kann den Bug-Zustand so oft prüfen, wie man will, ohne jedes Mal zu überlegen, ob sich die Prüfung lohnt, und kann den Test ebenfalls als Regressionstest behalten.Ich mag diesen Autor wirklich sehr, und Mu ist eines meiner Lieblingsprojekte. Es ist so etwas wie eine moderne Lisp-Maschine, und dazu ein interessantes Projekt, das unter QEMU läuft.
Mir gefällt der Satz: „Die meiste Software ist unheilbar von Anreizen infiziert, kurzfristig vielen Menschen zu dienen.“ Er funktioniert genauso, wenn man Software durch „Business“ ersetzt.
Wir sind alle in gewissem Maße von der Komplexität im Bereich Software Engineering überwältigt. Manchmal ist diese Komplexität auch zufällig
Aber ich stimme nicht zu, dass die Lösung darin besteht, alle Ideen zu verwerfen, die über Jahrzehnte entstanden sind. Umgekehrt sollte man auch nicht jede Lösung buchstäblich übernehmen oder „zu viel“ davon einsetzen. Überwältigt zu sein entsteht per Definition, wenn man von etwas zu viel verwendet. Schreibt Tests, nutzt Versionsverwaltung, nutzt Abstraktionen, aber ihr müsst wissen, warum ihr sie nutzt. Wenn dieses „Warum“ nicht mehr gilt, sollte man es neu bewerten
Das ist eine der nahezu schlimmsten Beschwörungsformeln in der Softwareentwicklung. Noch schlimmer ist, dass solche Dinge fast wie eine Religion gelehrt werden. Merkwürdig ist, dass sich die Art, wie Fachleute Software schreiben, in den letzten Jahren stark weiterentwickelt hat. Wie gesagt: Abstraktion ist nicht grundsätzlich für alles schlecht. Es ist sogar schwer vorstellbar, keine Basisklasse mit Feldern wie
updatedoderupdated_byfür typische Daten zu haben, die in eine SQL-Datenbank geschrieben werden. Aber im Allgemeinen verwende ich Abstraktionen kaum, sofern ich nicht wirklich dazu gezwungen bin. In der Wissenschaft hingegen wird immer noch exakt derselbe Lehrplan unterrichtet, den ich vor 25 Jahren gelernt habe. Es fühlt sich seltsam an, Studierende danach zu bewerten, wie sie mit schönem UML riesige Abstraktionen erstellen und in Code umsetzen. 90 % von ihnen werden nie wieder auch nur ein UML-Diagramm sehen. Zumindest in dem kleinen Bereich, in dem ich mich bewege, wird das so sein. Aber die Realität ist nun einmal die RealitätIch wünschte, es gäbe für alles eine Art kommandozeilenbasierte „Porzellan“-Schicht. Mit einer standardisierten
--help=ui-Ausgabe und einer Oberfläche im Stil vondialogließe sich das vermutlich automatisieren. Es geht weniger darum, von der Komplexität überwältigt zu sein, sondern eher darum, dass die Menge an aktiv nutzbarem Muskelgedächtnis begrenzt ist und man irgendwo eine Grenze ziehen muss