Da Du diesen Text hier liest, bist Du offensichtlich genau so ein Nerd wie wir. Komm zu uns und bewerbe Dich bei ///\/ DevBoost: https://devboost.com/karriere (https://api.devboost.de)

"Defend your software from yourself"

12.10.2021 / Maksim Gudow

Entwickler sind Menschen

"Was man nicht ändern kann, muss man von einer anderen Perspektive betrachten."

Fritz P. Rinnhofer

Der Fokus der Softwareentwicklung liegt meist auf dem Erzeugnis des Entwicklers, sei es das Endprodukt oder auch der Quellcode selbst. Viele Blogbeiträge, Tutorials und Konferenz-Talks konzentrieren sich auf den konkreten Prozess des Programmierens mit dem Fokus auf Tools, Frameworks und Prozessen. Aus meiner Sicht kommt die Betrachtung des Entwicklers als Individuum, als Homo Sapiens und als Teil eines Teams zu kurz.

Wenn die Aspekte der Psychologie und der Soziologie in die Betrachtung einfließen, dann ergeben sich Maßnahmen zur Steigerung der Softwarequalität, ohne sich mit Tools und Frameworks beschäftigen zu müssen.

Maksim Gudow hat seine Gedanken, Erkenntnisse und Tipps in einen sehr spannenden Talk verpackt und ihn auf der DecompileD Conference 2021 in Dresden dem Publikum präsentiert.

Im Folgenden drei Beispiele, die er im Vortrag aufgegriffen hat.

Rückschaufehler (Hindsight bias)

Den Satz "Hätte man vor 10 Jahren Bitcoins gekauft, dann wäre ..." hört man öfter. Leider wird dabei ebenso oft außer Acht gelassen, dass vor 10 Jahren die wenigsten von Bitcoin überhaupt wussten.

Wenn man sich als Entwickler mit Quellcode und Architektur-Entscheidungen eines in die Jahre gekommenen Systems beschäftigt, wird man regelmäßig zu der Erkenntnis kommen, dass es ja doch viel besser ginge, hätte man damals etwas anders gemacht. Die Hintergründe und der Kontext von damaligen Entscheidungen bleiben dabei außen vor.

In beiden Fällen fällt der Mensch dem sogenannten Rückschaufehler zum Opfer. Nach dem Eintreten eines Ereignisses erscheint dessen Vorhersehbarkeit als offensichtlich, obwohl das Ereignis vor dem Eintreten nicht absehbar war.

Somit sind getroffene Entscheidungen immer im Kontext der konkreten Situation zu bewerten, und diese ist ohne Dokumentation selten bekannt. Vielleicht war es die zu erfüllende Deadline, oder ein Bug im Framework war der Anlass zu einem Workaround, über den man sich im späteren Verlauf ärgert.

Gegenmaßnahmen

Mit diesem Wissen folgt die Erkenntnis, dass der Kontext einer Entscheidung mindestens genauso wichtig ist, wie die Entscheidung selbst. Somit ist dieser auch zu dokumentieren!

Dafür eignet sich das Format der Architecture Decision Records (kurz ADR) sehr gut. ADRs sind eher kurze, übersichtliche Dokumente, die getroffene Entscheidungen, deren Kontext und die Konsequenzen dokumentieren. Die Begründung oder die Annahmen, die zu einer Entscheidung geführt haben, können im Laufe der Zeit obsolet werden. Dadurch, dass diese aber dokumentiert sind, lässt sich die Entscheidung einfacher verstehen, verändern oder revidieren.

Die folgende Markdown-Vorlage kann dafür genutzt werden. Diese einfache Vorlage hilft schon, dem Rückschaufehler entgegenzuwirken.

# Überschrift

## Status

> Wie ist der Status? (vorgeschlagen, angenommen, abgelehnt, obsolet, überholt, ...)

## Kontext

> Was ist der Hintergrund und die Motivation für die vorgeschlagene Änderung?

## Entscheidung

> Was ist die Änderung?

## Konsequenzen

> Was wird einfacher oder schwieriger durch die Änderungen?

Bestätigungstendenz (Confirmation bias) / Positive Teststrategie

Egal ob man Software nach dem Prinzip des Test-Driven-Development entwickelt oder die Tests nach der Entwicklung schreibt: in beiden Fällen muss der Entwickler Annahmen über die Funktionsweise des Codes treffen. Diese Annahmen werden dann in automatisierte Tests überführt. Je nach Erfahrung des Entwicklers werden diese mehr oder weniger gut. Möglicherweise wird auch noch die eine oder andere Metrik, wie Code Coverage oder Testanzahl, relevant und hat entsprechende Auswirkungen.

Im beschriebenen Prozess tendiert der Mensch unglücklicherweise grundsätzlich dazu die eigene Annahme bestätigt zu sehen und blendet tendenziell die negativen Testfälle einfach aus. Das Verhalten ist in der Psychologie unter dem Namen "Bestätigungstendenz" bekannt. Das Experiment "2-4-6-Aufgabe" des englischen Psychologen Peter Wason veranschaulicht diese Tendenz deutlich.

Zusätzlich kommt hinzu, dass die Messung der Quantität von Tests oder der "Code Coverage" sehr einfach ist, eine Aussage über die tatsächliche Qualität dieser Tests zu treffen jedoch deutlich komplizierter.

Gegenmaßnahmen

Wie erwähnt spielt die Erfahrung des Softwareentwicklers, der den Test schreibt, dabei eine große Rolle. Die Denkweise entgegen dieser Tendenz kann trainiert werden. Es ist auch eine spezielle Rolle eines QA-Engineers denkbar, die genau den Fokus auf diese Denkweise / Sichtweise hat.

Als Entwickler können wir unseren Fokus auf die Stellen richten, die sich tendenziell am Code ändern können, um daraus die notwendigen Testszenarien abzuleiten. Beispiele:

  • Erweiterung der Prüfungen von < zu <= oder > zu >=
  • Entfernen von Funktionsaufrufen mit dem Rückgabetyp void
  • Ersetzen des Rückgabewerts einer Funktion durch null

Die einzelnen möglichen Änderungen nennt man Mutationen und die Methode an sich Mutationen-Test (Mutation Testing). Dieser Prozess kann auch mit Tools automatisiert werden. Für Java kann ich das Tool PITest sehr empfehlen. Es übernimmt genau den beschriebenen Vorgang der Mutation und Messung der Wirksamkeit von bestehenden Tests gegen diese Mutation. Nach jeder Mutation werden die vorhandenen Tests gegen den veränderten Code ausgeführt mit der Erwartung, dass die Änderung des Codes zum Scheitern eines Tests führt. Ist dies nicht der Fall, dann wird angenommen, dass ein entsprechender Testfall fehlt. Die Anzahl von Code-Mutationen, die die Tests abfangen ist ein Indikator für die Qualität der Testsuite.

Überlebenden-Verzerrung (Survivorship bias)

Als Softwareentwickler und -architekten treffen wir unsere Entscheidungen auf Grundlage von vorhandenen Daten. Die vorliegenden Daten auszuwerten und daraus Rückschlüsse zu ziehen sind bereits schwierige Themen für sich. Zusätzlich kommt erschwerend hinzu, dass diese Daten durch das Problem der systematischen "Überlebenden-Verzerrung" verfälscht sind.

Konkret bedeutet die "Überlebenden-Verzerrung", dass Daten, die für die Auswertung relevant wären, gar nicht erhoben werden, weil sie den Prozess der Sammlung in der einen oder anderen Form nicht überleben. In manchen Bereichen wie dem Militär ist meist tatsächlich das körperliche Überleben gemeint. Im Kontext der Softwareentwicklung kann es zum Beispiel eine Open-Source Bibliothek sein, die wenig bekannt ist.

Gegenmaßnahmen

Diese Art der Verzerrung ist leider in jedem einzelnen Bereich unserer Gesellschaft vorhanden, sei es die Architektur, Finanzwirtschaft oder eben auch Softwareentwicklung. Sich der Tatsache bewusst zu werden, dass die zugrundeliegenden Daten vielleicht verfälscht sein könnten oder auch kritisch den Datensammelprozess zu hinterfragen ist schon der erste Schritt in die richtige Richtung.

Eine weitere Folge dieser Verzerrung ist möglicherweise die Anzahl der Optionen bei der Technologiewahl. Dadurch, dass weniger bekannte Bibliotheken oder Frameworks von ihren Konkurrenten verdrängt werden, kommen diese bei der Technologiewahl vielleicht gar nicht zum Zuge. Möglicherweise wären aber genau sie die richtige Wahl gewesen. Frei nach dem Motto: es muss nicht immer Spring Boot sein!