Von unproduktivem Arbeiten, schlechtem Code, Astronauten und Klebeband

In diesem Blog-Post will ich mich darüber auslassen…

  • … warum unproduktives Arbeiten manchmal gut ist.
  • … warum schlechter Code manchmal gut ist.
  • … was Astronauten und Klebeband nicht gemeinsam haben.

Warum unproduktives Arbeiten manchmal gut ist

Code muss wartbar sein. Wenn ein Stück Code nicht wartbar ist, ist die Wahrscheinlichkeit hoch, dass mir das später Probleme bereiten wird. Fast alles, was man so schreibt, muss man später nochmal anfassen, es ändern, erweitern und vielleicht auch wiederverwenden. Und da mir noch niemand über den Weg gelaufen ist, der behaupten konnte, bug-freien Code zu schreiben (selbst Knuth soll ja Fehler gemacht haben), ist jedes Stück Code ein potentieller Kandidat für „Wartungsarbeiten“. Deshalb interessiere ich mich dafür, wie man Code wartbar machen kann. Wenn man wartbaren Code schreiben kann, kann man sich nervige Nacharbeiten ersparen — nicht alle, aber doch einige.

Mir macht es Spaß, Code anzugucken und mich zu fragen, ob man das hätte besser machen können. Entsprechende Diskussionen findet man zu Hauf im Forum. Gerade letztens wieder: eine Diskussion über 7 Tage und 27 Beiträge, die letztendlich nur zu kleinen Verbesserungen geführt hat. Man kann auch 48-Forenbeiträge darauf verwenden, einen Lottozahlen-Code zu diskutieren. Ich hab auch keine Probleme damit, eine Viertelstunde über eine Zeile Code zu grübeln und zu überlegen, wie man diese am lesbarsten gestaltet.

Das ist *extrem* unproduktiv. In einem realen Kontext, bei dem es wirklich darum geht, in vorgegebener Zeit eine Software zu schreiben, wäre so etwas absolut unwirtschaftlich. Code, den ich unter realen Bedingungen erstellte, ist also längst nicht so bis aufs Kleinste herausgeputzt, wie man das bei meiner Leidenschaft für Codeästhetik vermuten mag. Wenn es „ernst wird“, hab ich schlicht und einfach keine Zeit, ne Viertelstunde über eine Zeile Code nachzudenken, um dadurch ein halbes Jahr später bei irgendwelchen Wartungsarbeiten zwei Sekunden zu sparen. Denn mal ehrlich: So viel schlechter lesbar war die alternative Schreibweise der Zeile nun auch wieder nicht. Lesbarkeit ist zwar wichtig, übertriebener Fokus auf Lesbarkeit ist aber unwirtschaftlich.

Wenn ich aber die Chance dazu habe, bin ich auch gerne unwirtschaftlich. Das kann ich im Studium machen (wobei da auch nur begrenzt) oder im Forum. Und bei dem, was ich so für mich aus Spaß an der Freude schreibe. Dabei ist das nicht nur Spinnerei: Ich lerne daraus. Und ich merke immer wieder, dass ich immer noch mehr dazu lernen kann. Und das hilft dann auch in den realen Kontexten, bei denen man keine Zeit hat, ewig über eine einzige Zeile zu philosophieren.

Wer also lernen will, lesbaren und wartbaren Code zu schreiben, dem kann ich also nur raten, unproduktiv zu sein.

Warum schlechter Code manchmal gut ist

Wenn man also wirklich Software schreibt — im Beruf oder auch nur für die Uni — ist man manchmal gezwungen, schlechten Code zu schreiben. Wenn die Uhr tickt, programmiert es sich anders als, wenn man das nur aus Spaß an der Freude macht. Und manchmal wird man da ein gewisses ungutes Gefühl nicht los. Der Code scheint irgendwie „unvollkommen“ zu sein. Ich denke, das Gefühl bewahrt einen davor, schlimme Fehler zu begehen, von daher ist es gut, das es da ist. Aber das ist nur die eine Seite…

Joel Spolsky beschreibt in Things You Should Never Do, Part I genau so eine Situation. Man sagt ja oft, dass man, wenn man etwas fertig gestellt hat, am besten nochmal alles wegwirft und neu anfängt, weil man dann weiß, wie es richtig geht und die ganzen blöden Fehler nicht mehr macht. Ich hab das bisweilen auch schon behauptet. Spolsky zeigt auf, dass gerade das ein blöder Fehler wäre. Dabei gibt er folgendes Beispiel:

[Y]ou can ask almost any programmer today about the code they are working on. „It’s a big hairy mess,“ they will tell you. „I’d like nothing better than to throw it out and start over.“

Why is it a mess?

„Well,“ they say, „look at this function. It is two pages long! None of this stuff belongs in there! I don’t know what half of these API calls are for.“

[…]

Back to that two page function. Yes, I know, it’s just a simple function to display a window, but it has grown little hairs and stuff on it and nobody knows why. Well, I’ll tell you why: those are bug fixes. One of them fixes that bug that Nancy had when she tried to install the thing on a computer that didn’t have Internet Explorer. Another one fixes that bug that occurs in low memory conditions. Another one fixes that bug that occurred when the file is on a floppy disk and the user yanks out the disk in the middle. That LoadLibrary call is ugly but it makes the code work on old versions of Windows 95.

Ich fand das wirklich… erhellend. Natürlich ist das nicht immer so. Manchmal ist Code wirklich… verbesserungswürdig. Aber im Grunde genommen hat Spolsky wohl recht. Echter Code in echter Software, die wirklich benutzt wird, ist manchmal doch etwas anders, als die schönen kleinen Lehrbeispiele aus der Uni. Solche „Bugfixes“ gibt es überall. Und so hab ich das bisher noch gar nicht gesehen. Ich kann Spolskys Text (nicht nur diesen, sondern auch diverse andere von ihm) also nur wärmstens empfehlen. Es ist gut, manchmal die Dinge auch aus einem anderen Blickwinkel zu sehen.

Auf der anderen Seite wäre es natürlich gut, wenn die Bugfixes den Code nicht unnötig unleserlich machen würden. Mit ein paar Kommentaren im Code und am besten noch einem automatisierten Test pro gefixtem Bug, könnte man wohl die Lesbarkeit entsprechend verbessern und das Vertrauen der Entwickler in die Qualität des Codes erhöhen. Wenn man direkt sieht, warum die ganzen API-Calls nun notwendig sind, tut man sich mit späteren weiteren Bugfixes leichter, man macht weniger kaputt und hat ne bessere Meinung vom Code (und ist damit wohl auch motivierter bei der Arbeit).

Aber Spolsky will noch auf etwas anderes hinaus:

Manchmal wurde es tatsächlich gemacht: Alles weggeworfen und neu angefangen. Und oft ist das nicht gut gegangen. Der verlinkte Artikel nennt mehrere Beispiele. Man verliert viel, wenn man wirklich alles wegwirft. Und man braucht sehr viel Zeit, das alles wieder aufzuholen. Zeit, die in einem marktwirtschaftlichen Umfeld tödlich sein kann. Auch das hab ich bisher noch nicht so bedacht. Zumindest nicht so genau wie Spolsky.

Vielleicht gibt es manchmal wirklich keinen besseren Weg als alles neu zu machen, aber das besagte ungute Gefühl sollte uns eigentlich davor bewahren, dass es so weit kommt. Viel besser ist es, wenn man klein anfängt und langsam besser wird. „Klein“ kann dabei heißen, nur 50% der angedachten Funktionalität zu haben und das auch noch bei inkonsistenten Interfaces und verbesserungswürdiger Architektur. Und durch Refactoring und stetige Verbesserung kommt man dann irgendwann da hin, wo man hin will.

Auf die Spitze getrieben nennt sich dieses Prinzip worse is better.

  • Simplicity-the design must be simple, both in implementation and interface. It is more important for the implementation to be simple than the interface. Simplicity is the most important consideration in a design.
  • Correctness-the design must be correct in all observable aspects. It is slightly better to be simple than correct.
  • Consistency-the design must not be overly inconsistent. Consistency can be sacrificed for simplicity in some cases, but it is better to drop those parts of the design that deal with less common circumstances than to introduce either implementational complexity or inconsistency.
  • Completeness-the design must cover as many important situations as is practical. All reasonably expected cases should be covered. Completeness can be sacrificed in favor of any other quality. In fact, completeness must sacrificed whenever implementation simplicity is jeopardized. Consistency can be sacrificed to achieve completeness if simplicity is retained; especially worthless is consistency of interface.

Irgendetwas in mir sträubt sich gegen diese Denkweise, aber man kann ihr einen gewissen Erfolg nicht absprechen. Ich finde gerade gut durchdachte Schnittstellen wichtig, weil sie helfen, Fehler zu vermeiden. Gerade was Reihenfolge von Parametern, Bezeichner und generelle Funktionsweise betrifft. Wenn ich mir aber ansehe, wie viele schlechte oder zumindest verbesserungswürdige APIs es gibt, scheinen die doch einigen Erfolg zu haben. Ich bin aber trotzdem noch nicht davon überzeugt, dass „worse is better“ als generelles Prinzip eine gute Idee ist. Aber zumindest erinnert es mich daran, dass schlechter manchmal doch besser sein kann.

Was Astronauten und Klebeband nicht gemeinsam haben

Das bringt mich zu zwei weiteren Artikeln, die ich bei joelonsoftware gelesen habe:

Ich mag Architektur, Patterns, Objektorientierung und was man damit alles schönes tun kann. Leider habe ich deshalb auch manchmal die Tendenz zum Overengineering. Manchmal mache ich etwas unnötig kompliziert, weil es aus theoretischen Überlegungen heraus besser ist. Manchmal sind diese theoretischen Überlegungen in der Praxis aber weniger relevant. Spolsky nennt Leute, die solche Probleme haben „Architecture Astronauts“.

Ich weiß, dass ich das Problem hab und ich arbeite daran. Es hat sich auch schon deutlich gebessert, aber ganz zufrieden bin ich trotzdem noch nicht. Einfachheit ist wichtig. Wenn der Code einfach ist, lässt er sich auch leicht lesen und leicht ändern. Einfacher Code ist weniger anfällig für Bugs. Kraft meiner Arroganz hab ich mich sogar schon erdreistet, darüber Vorträge zu halten. Etwas verstanden zu haben (und demnach Vorträge drüber halten zu können) und etwas wirklich verinnerlicht zu haben sind aber zwei verschiedene Dinge. Ich kann also noch einiges dazu lernen und deshalb sollte ich mir den Satz von Tony Hoare, den ich so gerne zitiere, auch des öfteren mal vor Augen halten:

Tony Hoare
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult. It demands the same skill, devotion, insight, and even inspiration as the discovery of the simple physical laws which underlie the complex phenomena of nature.

Die zweite Gruppe von Entwicklern, die Spolsky benennt (im Gegensatz zu ersteren aber lobt), sind die „Duct Tape Programmers“. Das sind die, die sich nicht um Objektorientierung, Vererbung, Architektur, Unittests und ähnliches stören, sondern lieber mit einfachen Mitteln (eben mit „Klebeband“ wie Integers und Pointer) erstaunliche Dinge leisten können. Das Resultat mag nicht schön sein, aber es funktioniert und nach ein paar Iterationen wird der Code auch besser.

In einer Weise sind die „Duct Tape Programmers“ also das genaue Gegenteil der „Architecture Astronauts“. Sie machen lieber alles zu Fuß und kümmern sich nicht um Frameworks, Architektur und Sauberkeit, sind dafür aber schnell und effektiv. Man könnte fast meinen, sie hätten im Sinne von Tony Hoare die Einfachheit zu ihrem Grundsatz erhoben, aber das stimmt auch nicht so ganz. Sie sind eher Anhänger von „worse is better“. Aber auch das stimmt nicht ganz. Spolsky schreibt dazu:

Joel Spolsky
They have to be good enough programmers to ship code, and we’ll forgive them if they never write a unit test, or if they xor the “next” and “prev” pointers of their linked list into a single DWORD to save 32 bits, because they’re pretty enough, and smart enough, to pull it off.

In meinen Augen haben „Duct Tape Programmers“ das Problem, dass sie die Komplexität von der Architektur in den Code verlagern, anstatt sie zu reduzieren. Und ich weiß nicht, ob ich das generell für eine gute Idee halten soll. Ich will also eher nicht lernen, mit Klebeband zu programmieren. Viel eher will ich lernen, einfachen, geradezu langweiligen Code zu schreiben. Langweiliger Code ist leicht zu verstehen und man kann sich dann besser auf die Stellen konzentrieren, die nicht langweilig sein können, weil sie echte Komplexität enthalten, die sich nicht einfach reduzieren lässt. Lieber den Code klar und sauber schreiben und dafür die ganzen tollen Features [1], mit denen man eigentlich so schön hätte angeben können, nur dann nutzen, wenn sie wirklich nötig sind. Das ist nicht leicht, aber das ist es, was ich lernen will.

[1] mögen es nun Architektur-Tricks, Patterns oder Pointerspielereien sein

2 Kommentare


  1. In der Praxis treffen „Astronauten“ und „Kleber“ oft zusammen – aus meiner Erfahrung (ich bin so ein „Astronaut“) geht das nur am Anfang gut, irgendwann kriegst Du einfach nur die Krise, wenn man nicht-vererbaren, super-redundanten Code zwischen die Finger kriegt und erstmal die XORs und die SHLs entfernen muss, damit man den Code lesen kann (die Kleber sind auch die, die von STANDARD-formatiertem Code gar nichts halten, da ihr über Jahrzehnte entwickeltes eigenes Format „sowieso besser“ ist). Bei mir gibts auch keine super-kurzen Abkürzungen wie „KUNU“ – das Feld heisst einfach „KundenNummer“ und ist damit trotzdem einfach schnell zu lesen…


  2. Hallo Dennis. Danke für deinen Kommentar.

    Ich das auch so wie du. Code muss lesbar sein. Ich finde das außerordentlich wichtig. Normalerweise wird Code ja deutlich öfter gelesen als geschrieben. Deshalb muss Code leicht zu lesen sein; nicht unbedingt leicht zu schreiben.

    Und Aussagekräftige Bezeichner gehören da unbedingt dazu. Unverständliche Abkürzungen kann ich auch nicht leiden. Selbst, wenn es ein bisschen mehr zu tippen ist (moderne IDEs sollten das Argument sowieso zerstreuen), es ist viel leichter zu lesen. Insbesondere, wenn man nicht alleine entwickelt ist das sehr wichtig.

    Was ich aber aus Spolskys Artikel meine gelernt zu haben: „Wir Astronauten“ müssen aufpassen, dass wir nicht ins andere Extrem abdriften und die Architektur zu kompliziert machen. Wie immer ist ein guter Mittelweg zu finden.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert