Der Software-Engineering-Blog

Anmerkungen, Gedanken und Ideen von Dirk Wiesmann

Abstraktion

In der Informatik und speziell in der Programmierung geht es um Abstraktion. Bei der Nutzung der Java-API werden häufig der Dienstanbieter und der Dienstnutzer über eine Schnittstelle entkoppelt. Beispiele sind z.B. Iterator, Comparable, Comparator, FileFilter, Runnable, Supplier und EventHandler (JavaFX). Die Namen unterscheiden sich, das Konzept ist aber immer gleich.
Für Personen, die in der Abstraktion ungeübt sind, handelt es sich bei den obigen Beispielen um sechs völlig unterschiedliche Sachverhalte, die einzeln auswendig gelernt werden müssen. Die Nutzung der Schnittstelle Predicate ist dieser Gruppe danach nur schwer möglich. Personen mit einem Abstraktionstalent reichen dagegen zwei bis drei Beispiele, um dann auch mit unbekannten Namen umgehen zu können.
Abstraktion spielt auf allen Ebenen der Softwareentwicklung eine Rolle. Auch bei der Unterscheidung zwischen Vorgehens- und Prozessmodellen muss zwischen verschiedenen Abstraktionsebenen unterschieden werden. Dies scheint nicht allen Personen leicht zu fallen, ansonsten lässt sich z.B. das ganze unsachliche Gerede rund um den Begriff agil nicht erklären.

Evolutionär statt agil!

Im Bereich der Softwareentwicklung wird der Begriff "Agil" inflationär verwendet.
"Agil" ist aber als Begriff ungeeignet und sollte in einem professionellen und akademischen Umfeld eigentlich nicht weiter verwendet werden.
Der Begriff "agil" hat als Marketinginstrument sicherlich gute Dienste geleistet. Wer möchte nicht gerne agil sein, genauer gesagt, wer möchte schon das Gegenteil von agil sein? Der Begriff "agil" ist durchgängig mit positiven Assoziationen belegt. Damit ist dieser Begriff ein denkbar schlechter Kandidat, um als Basis einer sachlichen/neutralen Definition zu dienen.
Zudem gibt es keine formale/saubere Definition von agilen Prozessmodellen. Das agile Manifest kann sicherlich nicht als formale Definition dienen. Es werden dort in keiner Form klare Grenzen und Festlegungen gezogen. Hiermit können höchstens die extremen Ausprägungen von Prozessmodellen (XP vs V-Modell XT) grob zugeordnet werden. Ohne den Projektkontext ist aber auch dieses nicht möglich (siehe die Einschränkung im Agilen Manifest). Das agile Manifest ist eher eine Vision und hat einen Impuls gesetzt. Für mehr ist es nicht zu gebrauchen.
Trotzdem wird von den Protagonisten von agilen Prozessen so getan, als wäre der Begriff eines agilen Prozessmodells sauber definiert. Dabei wird anscheinend immer die folgende implizite Definition angenommen
"Wenn ein Projekt nicht erfolgreich ist, dann wurde kein agiler Prozess gefahren"
Auf der Grundlage dieser Definition und der positiven Belegung des Begriffs "agil" lässt sich natürlich vortrefflich argumentieren. Wissenschaftliche Untersuchungen zum Thema Agilität verbieten sich auf Basis dieser unzureichenden Definition. Je nach Geschmack und gewünschtem Ergebnis kann alles oder nichts als agil bezeichnet werden.
Wenn man mit Befürwortern von agilen Prozessen diskutiert, dann kann man relativ leicht zu allen Vorteilen eines agilen Prozesses relativ leicht Gegenbeispiele konstruieren bzw. alternative Ansätze benennen, die nicht als agil bezeichnet werden, aber im speziellen Fall nicht schlechter funktionieren.
Das ist aber auch kein Wunder, da es keinen Prozess gibt, der für alle Aufgabenstellungen optimal ist (no free lunch). Das Problem ist nur, dass häufig so getan wird, als seien agile Prozesse die optimale und einzige Lösung.
Wer traut sich heute eigentlich noch, nicht agil zu sein?
In Diskussionen mit Befürwortern der agilen Vorgehensweise kommt recht schnell das folgende Argument: "Bei agilen Prozessen müssen die Anforderungen nicht vollständig vorhanden sein, um mit der Arbeit zu beginnen. Zudem können neue Anforderungen im Projektverlauf aufgenommen werden".
Ja, diese Aussage stimmt! Allerdings stimmt diese Aussage für alle Prozesse, die auf einem evolutionären Vorgehensmodell basieren. Die Unterscheidung zwischen Vorgehens- und Prozessmodellen erweist sich als sehr hilfreich. Ein konkreter Prozess basiert immer auf einem Vorgehensmodell und ist damit eine konkrete und anwendbare Ausprägung eines Vorgehensmodells. Ein agiler Prozess, wie z.B. XP, basiert auf dem evolutionären Vorgehensmodell.
Im Kern scheinen sich viele Vorteile von agilen Prozessen auf die grundlegenden Eigenschaften von evolutionären Vorgehensmodellen zurückzuführen lassen.
Ich plädiere daher dafür, den Begriff der agilen Prozesse durch die evolutionären Vorgehensmodelle zu ersetzen. Evolutionäre Vorgehensmodelle sind ausreichend definiert und auf Basis der Vorgehensmodelle lassen sich unterschiedliche Prozesse definieren. Diese Prozesse können unterschiedlich formal sein und einen sehr unterschiedlichen Grad an Dokumentation erfordern. Damit steckt man dann nicht in der "agilen Falle", sondern ist freier in der Wahl von angemessenen Prozessen für die Softwareentwicklung. Gerade wenn Qualitätssicherung eine Rolle spielt, benötigt man in der Regel mehr Prozess, als die agilen Vertreter gerne zugestehen würden.

Bieten Unit-Tests einen Nutzen?

Eine provokante Frage? Die Antwort ist eigentlich klar. Natürlich bieten Unit-Tests einen Nutzen, sonst hätten sie nicht eine solche Verbreitung erfahren. Meine Vermutung ist aber, dass doch viele Entwickler ein falsche Vorstellung vom eigentlichen Nutzen haben.
Meine Vermutung ist, dass häufig mit den Unit-Tests Ziele verfolgt werden, die mit einem Unit-Test gar nicht erreicht werden können!
Welche Ziele verfolgt man mit einem Unit-Test? Ohne einen Nutzen würde/sollte man den Aufwand von Unit-Tests sicherlich nicht betreiben. Das Ziel ist sicherlich eine Verbesserung oder zumindest eine Absicherung. Beim Unit-Test handelt es sich um ein analytisches Verfahren. Wir können damit im erfolgreichen Fall nur Fehler finden. Der bloße Einsatz von Unit-Tests führt damit noch zu keiner Verbesserung. Ich werde im weiteren Verlauf ganz optimistisch davon ausgehen, dass die gefundenen Fehler durch anschließende Korrekturmaßnahmen auch behoben werden.
Die Frage ist, was soll und was kann verbessert werden? Ich gehe davon aus, dass man in der Regel eine Verbesserung der Produktqualität erreichen möchte (mögliche Auswirkungen auf die Prozessqualität betrachte ich hier nicht). Um nicht im luftleeren Raum zu argumentieren, werde ich für die weitere Argumentation ein Qualitätsmodell bemühen. Die Norm ISO 25010 bietet u.a. die funktionale Eignung (functional suitability) als Qualitätsmerkmal. Dieses Qualitätsmerkmal gliedert sich in die Kriterien der funktionalen Angemessenheit (functional appropriateness), der funktionalen Korrektheit (functional correctness) und der funktionalen Vollständigkeit (functional completeness). Mit einem Unit-Test können wir unsere Arbeit nur verifizieren, nicht aber validieren (siehe V-Modell). Damit können wir die funktionale Angemessenheit mit Unit-Tests nicht verbessern. Sobald die Testfälle aus dem Code abgeleitet werden (Glass-box-test), können wir auch die funktionale Vollständigkeit nicht über einen Unit-Test absichern. Es bleibt also noch die Korrektheit.
Meine Vermutung ist, dass viele Entwickler meinen, dass sie mit Unit-Tests die funktionale Korrektheit der Software verbessern können. Was ist Ihre Erfahrung? Leider ist es in der Praxis in den allermeisten Fällen gar nicht möglich, die Korrektheit des Produkts über Unit-Tests abzusichern! Für einen systematischen Test benötigt man einen Soll-Wert. Ein Soll-Wert muss aus der Spezifikation (für die Agilen: vom Product Owner, vom Kunden-vor-Ort) kommen. Soll-Werte werden aber (in der Praxis) nur für einen Anwendungsfall vorgegeben. Eine zu testende Komponente entsteht aber über eine Dekomposition (und Abstraktion) des Anwendungsfalls. Damit muss auch der Soll-Wert über mehrere Dekompositionsstufen transformiert werden. Dabei muss jeder Schritt auf Korrektheit geprüft werden. Nur dann erhält man einen Soll-Wert, der eine Aussage über Produktfehler erlaubt. Dies erzeugt einen erheblichen Aufwand. Dieser Aufwand wird selten betrieben.
In der Praxis werden die Soll-Werte vom Entwickler eher intuitiv hergeleitet. Eine formale und gesicherte Ableitung der Soll-Werte (bezogen auf die Gesamtanwendung) findet also nicht statt. Der Entwickler testet daher gegen seine eigene Erwartung (Vermutung). Was ist, wenn die Vermutung falsch war? Eine Aussage über die Korrektheit ist also mit einer entsprechenden Unsicherheit verbunden. Der Nutzen von Unit-Tests ist aus dieser Sicht fraglich. Trotzdem werden Unit-Tests massenhaft eingesetzt. Viele Programmierer sind geradezu begeistert. Unit-Tests werden also doch wohl einen Nutzen bieten, oder? Ja, dazu betrachten wir das Qualitätsmerkmal der Wartbarkeit (maintainability). Hier gibt es u.a. die Kriterien Modularity, Reusability, Analysability und Modifiability. Unit-Tests haben in der Regel einen positiven Einfluss auf die Wartbarkeit!
Dazu betrachten wir folgende Situation. Ein Programmierer hat eine Komponente fertiggestellt und per Unit-Test geprüft. Es wurden keine Fehler gefunden. Die Komponente erfüllt also die funktionalen Erwartungen des Programmierers. Im weiteren Verlauf stellt der Programmierer evtl. fest, dass die Struktur von Komponenten nicht (mehr) angemessen ist. Mit Unit-Tests bewaffnet hat der Entwickler nun den Mut, auch umfangreiche Änderungen an der Struktur vorzunehmen. Mit einem Regressionstest hat er eine gewisse Sicherheit, dass er durch seine Strukturänderungen die Funktionalität der Komponenten nicht (völlig) zerstört hat.
Unit-Tests geben dem Programmierer ein gutes (sicheres) Gefühl. Ein Refactoring verliert seinen Schrecken.
Unit-Tests geben dem Programmierer also ein gutes Gefühl. Nicht mehr und nicht weniger. Als positive Folge kann sich die Wartbarkeit der Software verbessern. Die konkrete Verbesserung ist aber vom Know-How des Entwicklers abhängig (und von den weiteren Massnahmen der Qualitätssicherung).
Unit-Tests haben eher einen positiven Einfluss auf die Wartbarkeit, als auf die funktionale Korrektheit.
Es ergibt sich nun eine wichtige Folgerung. Das Qualitätsmerkmal der funktionalen Korrektheit muss über andere Techniken der Qualitätssicherung erreicht werden.
Folgerung: Neben Unit-Tests werden weitere Maßnahmen der Software-Qualitätssicherung benötigt, um die funktionale Korrektheit zu verbessern.