Dirk Herrmanns Gedanken zu Software Engineering

Inhalt

Allgemeines

Was ist methodische SW-Entwicklung?

A 'man of method' was, in Poirot's estimation, the highest praise that could be bestowed on any individual.

Agatha Christie, The Mysterious Affair at Styles

Unter methodischer Softwareentwicklung verstehe ich, bewusst für jede Teilaufgabe der Softwareentwicklung ein unter den jeweiligen Randbedingungen möglichst gut geeignetes Verfahren zu wählen. Zu den (zum Teil beeinflussbaren) Randbedingungen gehören u. a.

Ein Verfahren ist geeigneter als ein anderes, sofern die zu bearbeitende Teilaufgabe damit effizienter erledigt werden kann oder sofern damit ein qualitativ besseres Ergebnis erwartet wird. Für die Verfahrensauswahl kommen einerseits öffentlich publizierte Verfahren, unternehmensinterne Verfahren aber auch individuell für die jeweilige Situation entwickelte oder angepasste Verfahren in Betracht.

Methodische Softwareentwicklung ist keine Selbstverständlichkeit: In der Praxis wird die Frage "wie gehe ich / wie gehen wir sinnvollerweise vor" oft nicht gestellt, oder sie wird übereilt beantwortet. In solchen Fällen erfolgt die Wahl der Methode nicht bewusst, und das darin liegende Potential wird nicht so ausgeschöpft, wie es bei der bewussten Wahl zwischen alternativen Methoden möglich wäre.

Ergebnis der Methodenwahl muss nicht zwingend die Entscheidung für eine einzelne Methode sein: Oft sind Aufgaben zu erledigen, bei denen mehrere Beteiligte für jeweils unterschiedliche Teile der Software ähnliche Änderungen vorzunehmen haben. In solchen Fällen bietet es sich an, obwohl am Ende jeder seinen eigenen Teil bearbeitet, vorher im Team mögliche Vorgehensweisen zu besprechen. Dabei können mehrere geeignete Vorgehensweisen herauskommen, von denen jeder einzelne Entwickler diejenige wählt, die für sein eigenes Teilproblem angemessen ist.

Die Wahl der Methode kann während der Arbeit angepasst werden, wenn dies sinnvoll erscheint. Beispielsweise besteht eine Kernidee der Agilität darin, die Entwicklung in kurze Zyklen zu unterteilen, um neue Erkenntnisse möglichst schnell zu nutzen und für folgende Zyklen entsprechende Anpassungen vorzunehmen. Einige agile Ansätze sind allerdings mit einem relativ begrenzten Methodenportfolio verbunden, obwohl aus meiner Sicht eine Entwicklung nur dann agil ist, wenn auch die Wahl der Methoden selbst einem Lernzyklus unterliegt.

Worin besteht nach meinem Verständnis der Unterschied zwischen Prozess und Methode? Ein Prozess stellt üblicherweise einen relativ groben Rahmen für die Entwicklung dar. Analog zur System- oder Softwarearchitekturdefinition, in der Komponenten und ihre Schnittstellen festgelegt werden, werden in der Prozessdefinition die einzelnen Teilaufgaben und die zu liefernden Arbeitsergebnisse festgelegt. Oft werden in der Prozessdefinition noch zusätzliche Vorgaben hinsichtlich der Vorgehensweise gemacht (beispielsweise für Reviews die Verwendung bestimmter Checklisten). Es bleibt aber in jedem Fall ein Freiraum, der bei der Entwicklung durch die Wahl der Methode zu füllen ist.

Empfehlungen für Softwareentwickler

Programmiersprachen

Modelle der Softwareentwicklung

V-Modell

CMM und CMMI

Prozesse

Good technical workers will focus on good process and continually improving process whether you tell them to or not.

Tom DeMarco, The Deadline

Das Thema Prozesse hat in der Softwareentwicklung mittlerweile einen gewaltigen Stellenwert. Unternehmen unterhalten eigene Abteilungen nur für die Festlegung und Dokumentation von Vorgehensweisen. Und tatsächlich hat die Wahl der Vorgehensweise große Auswirkungen auf die Wirtschaftlichkeit der Entwicklung. In der Praxis ist jedoch zu beobachten, dass die zunehmende Fokussierung auf Prozesse häufig die Wirtschaftlichkeit der Softwareentwicklung nicht erhöht. Schlimmer noch: Nicht selten zeigt sich eine Demotivierung der Mitarbeiter, schon das Wort Prozess ist zu einem Reizwort geworden.

Einer der Gründe liegt in der Frage, ab wann ein Prozess das Kriterium erfüllt, dass die korrekte Durchführung der durch den Prozess geforderten Schritte nachweisbar ist (beispielsweise im Rahmen eines Audits). Als Beispiel sollen mögliche Prozesse zum Umgang mit Compilerwarnungen dienen:

Verwaltungsaufwand beim Umgang mit Compilerwarnungen

Erfahrene und qualitätsorientierte Softwareentwickler werden, auch ohne jede Vorgabe durch einen Prozess, Compilerwarnungen analysieren. Welche Warnungen dabei durch eine Änderung der Software beseitigt werden und welche verbleiben, entscheiden die Entwickler nach eigenem Ermessen. Es kommt nämlich häufig vor, dass der Compiler Warnungen ausgibt, deren Beseitigung zu einer Verschlechterung des Codes führen würde. Warnungen pauschal zu beseitigen ist daher der Qualität der Software ebensowenig zuträglich wie die Warnungen völlig zu ignorieren.

Nutze Compilerwarnungen als Informationsquelle zur Verbesserung der Softwarequalität. In dieser Form stellt folglich der Umgang mit Compilerwarnungen eine allgemein anerkannte best practice dar. Diesen Schritt in den offiziellen Entwicklungsprozess aufzunehmen und somit verbindlich zu machen wird soweit kaum Einwände durch qualitätsbewusste Entwickler hervorrufen.

Auf dieser Abstraktionsebene wird ein solcher Prozess allerdings zumeist nicht formuliert, sondern es wird sofort die weitergehende Frage gestellt, wie die Einhaltung des Prozesses sowie eventuelle Abweichungen überprüfbar (offiziell: für den Manager, inoffiziell: für den Auditor) dokumentiert werden sollen. Wenn der Softwarestand, den ein Entwickler abliefert, noch zur Ausgabe von Compilerwarnungen führt, ist nämlich nicht erkennbar, ob der Entwickler diese Warnungen tatsächlich betrachtet hat. Hier bietet sich ein breites Spektrum an Möglichkeiten. Die folgende Aufstellung zeigt einige, nach steigendem Dokumentationsaufwand und der (vermeintlich) besseren Beweisbarkeit der Prozesseinhaltung sortiert:

  • Mit dem Einstellen eines Softwarestandes in das Versionierungssystem erklärt der Entwickler implizit zugleich, dass die Warnungen gemäß Prozess bearbeitet wurden.
  • Wurden die Warnungen für einen Softwarestand gemäß Prozess bearbeitet, soll der Entwickler das für diesen Softwarestand bestätigen.
  • Wie im vorigen Punkt, jedoch soll der Entwickler für jede verbliebene Warnung einzeln bestätigen, dass diese Warnung bewusst belassen wurde.
  • Wie im vorigen Punkt, jedoch soll der Entwickler für jede verbliebene Warnung eine Begründung liefern, warum die Warnung nicht beseitigt wurde.
  • Wie im vorigen Punkt, jedoch soll das Dokument mit den Begründungen im Rahmen eines Reviews von den Gutachtern gegengezeichnet werden.

Ab einer dieser Stufen (spätestens mit der dritten) beginnen mehr und mehr Entwickler, Warnungen auch dann zu beseitigen, wenn sich dadurch Verschlechterungen des Codes ergeben (z. B. hinsichtlich der Lesbarkeit). Dies ist zwingend, da qualitative Eigenschaften wie die Lesbarkeit der Software dazu dienen, die Effizienz der Entwicklung zu erhöhen. Verschlechtert die Erhaltung der Lesbarkeit aus der Sicht des Entwicklers die Effizienz aufgrund des damit verbundenen Verwaltungsaufwands, nimmt der Nutzen der Lesbarkeit ab. Wieviel Zusatzaufwand ist das bisschen bessere Lesbarkeit wert?

Wie das Beispiel zeigt, können Primärziele wie

Sekundärzielen wie

zum Opfer fallen.

Entwicklungsmethodik

Die Themen in diesem Abschnitt sind grob entlang des V-Modells (Wikipedia:V-Modell) angeordnet. Das Modell dient für mich primär als Orientierungshilfe bei der Frage, welche Aufgaben bei der Entwicklung von Software prinzipiell anfallen. Es beantwortet für mich nicht die Frage, ob die Aufgaben sinnvollerweise in einer festen Reihenfolge (und ggf. in welcher) zu erledigen sind.

Anforderungen

Den Kunden verstehen

Ein systematischer Umgang mit Anforderungen stellt eine der Kernkompetenzen einer reifen Entwicklungsorganisation dar. Bei dem Versuch, den Umgang mit Anforderungen in Prozessbeschreibungen zu formalisieren, wird jedoch teilweise von unzulässigen Annahmen ausgegangen oder es werden zu starke Vereinfachungen vorgenommen. Eines der Hauptziele bei der Erstellung eines Anforderungsdokuments wird dabei unter Umständen aus den Augen verloren, nämlich dass die Entwickler ein möglichst gutes Verständnis von den tatsächlichen Wünschen des Kunden (oder ggf. anderer Stakeholder) bekommen.

Eine erste Schwierigkeit stellt bereits die Frage dar, was eine Anforderung ist, und was nicht. Denn - so die Denkweise in mancher Prozessabteilung - was die Kriterien für eine Anforderung nicht erfüllt, hat auch in einem Anforderungsdokument nichts zu suchen. Mehr als einmal war ich mit einer Definition von Anforderung konfrontiert, nach der eine Formulierung nur dann eine Anforderung darstellt, wenn sie eindeutig und testbar ist (eine verbreitete Auffassung, wie dieser WikiPedia-Artikel belegt). Und tatsächlich hatten Entwickler in entsprechenden Projekten Schwierigkeiten, Anforderungen wie „das Programm soll leicht bedienbar sein” in Anforderungsdokumenten zu platzieren – wie testet man „leicht”?

Die Anforderung nach leichter Bedienbarkeit ist tatsächlich nicht sehr hilfreich, weil sie zu pauschal ist. Man könnte sie beispielsweise folgendermaßen konkretisieren: „Das Programm soll auch für Gelegenheitsanwender ohne Zugriff auf das gedruckte Handbuch leicht bedienbar sein”. Die Anforderung ist konkreter, das Wort „leicht” sind wir trotzdem nicht losgeworden. Und das ist in Ordnung, denn dem Kunden geht es hier um die leichte Bedienbarkeit, und die Entwickler müssen sich über diesen Wunsch des Kunden im Klaren sein. Die abgelieferte Software wird hoffentlich wegen dieser Anforderung anders aussehen.

Absolute und relative Anforderungen

Anforderungen wie „das Programm soll zu einer übergebenen natürlichen Zahl ausgeben, ob es sich dabei um eine Primzahl handelt” sind absolute Anforderungen im folgenden Sinne: Die Frage, ob das entwickelte Programm die Anforderung erfüllt, kann mit ja oder nein beantwortet werden, ohne zum Vergleich andere Programme oder andere mögliche Lösungswege heranziehen zu müssen.

Dagegen sind Anforderungen wie die nach „leichter” Bedienbarkeit relative Anforderungen: Je nachdem, welches Bedienkonzept bei der Entwicklung gewählt wird, unterscheidet sich die Bedienbarkeit. Erst durch den Vergleich der Lösungen bzw. der verschiedenen Konzepte lässt sich zwischen leichter und weniger leicht bedienbaren Lösungen unterscheiden. Unter Umständen kommen bei dieser Bewertung unterschiedliche Personen auch zu unterschiedlichen Einschätzungen. Weiterhin kann, wenn ein neues Bedienkonzept entwickelt wird, das bisher am leichtesten bedienbare Programm auf Platz zwei zurückfallen. Möglicherweise verschiebt sich durch das neue Konzept auch die allgemeine Auffassung von leichter Bedienbarkeit, so dass selbst die besten bisherigen Konzepte zukünftig nicht einmal mehr als halbwegs gut bedienbar gelten.

Die Einteilung von Anforderungen nach absolut oder relativ ist orthogonal zu anderen Klassifikationen wie beispielsweise funktional oder nichtfunktional, kundenrelevant oder von interner Relevanz: In einem Computerspiel sollen die ersten Level „einfach” zu bewältigen sein, aber höhere Level immer „schwieriger” werden (funktional und relativ). Der Quellcode soll „leicht” lesbar sein, die Komplexität der Software „gering” (nichtfunktional und relativ).

Wann sind relative Anforderungen erfüllt?

Das Problem, das sich mit solchen relativen Anforderungen ergibt, ist die Frage, wann sie als erfüllt gelten. Wann ist die beste bisher gefundene Lösung gut genug? Wann kann die Suche nach weiteren Lösungsalternativen eingestellt werden? Wie soll damit umgegangen werden, dass es kein absolutes, objektives, quantitatives Kriterium für die Erfüllung gibt? Wann ist ein Programm „leicht” bedienbar, wann ist der Quellcode „leicht” lesbar?

Hier zeigt sich ein Muster: Relative Anforderungen verlangen vom Entwickler, ein Portfolio von Lösungsalternativen mit ihren jeweiligen Eigenschaften zu kennen und ein Gespür für die jeweils geforderte Eigenschaft im jeweiligen Kontext zu entwickeln. Damit ist aber noch nicht beantwortet, wie man zu einer belastbaren Vereinbarung über die Frage kommen kann, wann die relative Anforderung als erfüllt zu betrachten ist.

Ein formales Abnahmekriterium gibt es bei internen Anforderungen wie hier nach der einfachen Lesbarkeit von Code in der Regel nicht. Es wird darauf vertraut, dass die Kollegen die entsprechende Kompetenz besitzen und zu einer für das Unternehmen geeigneten Einschätzung kommen werden. Unter Umständen erfolgt noch eine Bewertung im Rahmen von Reviews, sehr selten werden außenstehende Berater für eine Einschätzung hinzugezogen.

Um mit dem Kunden bei relativen Anforderungen wie leichter Bedienbarkeit zu einer Konkretisierung zu kommen, wird man sich auf die Lösungsebene begeben, und beispielsweise anhand von bestehenden Vergleichslösungen, Prototypen, Konzeptskizzen oder ähnlichem eine Referenz zu definieren. Hier ist folglich viel Kommunikation mit dem Kunden gefragt.

Kann man nicht auch ohne relative Anforderungen auskommen?

Kann man nicht das Problem der schwierigeren Fassbarkeit von relativen Anforderungen damit umgehen, dass man einfach eine mit dem Kunden abgestimmte Referenzlösung zur Anforderung erhebt und die relative Anforderung unter den Tisch fallen lässt? Zum einen hat dies den Nachteil, dass das Wissen über den eigentlichen Kundenwunsch verloren geht: Dem Kunden geht es nach wie vor um einfache Bedienbarkeit. Zum anderen schränkt sich die Softwareentwicklung auf diese Weise den eigenen Lösungsraum unnötig ein: Sollten sich während der Entwicklung andere vergleichbar einfach bedienbare Lösungen ergeben, die mit geringerem Entwicklungsaufwand realisierbar sind, wird mancher Kunde mit sich reden lassen – umso mehr, wenn der Kunde die neue Lösung sogar als noch einfacher bedienbar empfindet.

Sind relative Anforderungen nicht nur ein Zeichen dafür, dass man das Problem nicht scharf genug definiert hat? In manchen Fällen mag das stimmen, aber nicht grundsätzlich: Der zwanghafte Versuch, relative Anforderungen in absolute Anforderungen zu transformieren, führt häufig zu skurrilen Formulierungen und Quantisierungen, bzw. stellt Forderungen nach Eigenschaften, die den Kern des Problems nicht treffen. Ein Beispiel ist, statt einfacher Lesbarkeit des Codes die Einhaltung fester Grenzwerte von ausgewählten Software-Metriken zu fordern. Ich bin noch keinem erfahrenen Softwareentwickler begegnet, der nicht der Auffassung war, dass das am eigentlichen Problem vorbeigeht. Auf manche Manager muss diese Idee dagegen einen unheimlichen Charme ausstrahlen...

Der Umgang mit relativen Anforderungen ist anspruchsvoll: Sie verlangen vom Entwickler, sich mit Lösungsalternativen zu befassen und ein Gespür für die Fragestellung aus Kundensicht zu entwickeln. Sie verlangen mehr Abstimmung und Kommunikation mit dem Kunden. Sie lassen einen größeren Lösungsraum. Sie wirken bedrohlich auf Manager, die auf Absicherung und Kontrolle fokussiert sind, weil die Erfüllung sich nicht mit ja oder nein beantworten lässt. Fazit: Es sind häufig die interessantesten Anforderungen an ein Produkt.

Software Design

Mechanismen

Globale Objekte / globale Variablen

Überprüfung

Bei der Überprüfung von Software auf Korrektheit unterscheidet man zwischen statischen und dynamischen Analysetechniken. Im Rahmen der statischen Analyse wird die Software in Bezug auf sämtliche möglichen Ausführungsszenarien oder hinsichtlich ihrer Struktur untersucht. Dagegen betrachten dynamische Analyestechniken die Software im Rahmen ausgewählter tatsächlicher oder simulierter Ausführungen, und somit jeweils nur eine Teilmenge der möglichen Ausführungsszenarien.

Konzeptionell ist die statische Analyse mächtiger, und zwar einerseits hinsichtlich der Frage, ob eine Software korrekt funktioniert, und andererseits hinsichtlich der Bewertung nichtfunktionaler Eigenschaften der Software. So stellt in Hinblick auf die Korrekheit ein (manuell oder automatisiert erbrachter) Beweis, dass eine Software ein bestimmtes Verhalten hat, die höchste Stufe der statischen Analyse dar. In der Praxis werden jedoch echte Korrektheitsbeweise nur selten vorgenommen, da dies in der Breite noch immer zu zeitaufwendig erscheint und außerdem hohe Ansprüche an die Fähigkeiten der entsprechenden Entwickler stellt.

Um dennoch ein hohes Vertrauen in die Korrektheit der Software zu gewinnen, werden daher zumeist Kombinationen von einfacher anwendbaren statischen Analysetechniken und dynamischen Analysetechniken eingesetzt.

Statische Analyse

Begutachtung (Review)

Hinsichtlich der Menge der überprüfbaren Softwareeigenschaften stellen Reviews den mächtigsten Ansatz zur statischen Analyse dar. Im Rahmen von Reviews kann Software auf funktionale Korrektheit untersucht werden, aber es können auch strukturelle Eigenschaften wie die Zerlegung in Module überprüft werden, bis hin zu der Frage, ob der Code mit verständlichen und sinnvollen Kommentaren versehen ist. Darüberhinaus können im Rahmen eines Code Reviews sogar Fehler in der dem Code zugrundeliegenden Spezifikation entdeckt werden.

Generische Code Checker

Generische Code Checker sind darauf ausgelegt, in der Software Konstrukte zu erkennen, die unabhängig von der jeweiligen Applikation auf Fehler hindeuten oder als nachteilig gelten. Wird beispielsweise in der Software irgendwo eine Division durch null stattfinden, so deutet dies auf einen Fehler hin, unabhängig davon, ob es sich bei der Software um eine Waschmaschinensteuerung oder ein Computerspiel handelt.

Es gibt eine Reihe kommerzieller und auch freier generischer Code Checker, die sich hinsichtlich der Menge der implementierten Prüfungen und auch hinsichtlich ihrer Analysetiefe unterscheiden. Sie sind zumeist unmittelbar auf bestehenden Code anwendbar und verlangen, bis auf einfache Konfigurationsschritte, kaum Vorbereitung. Beim Einsatz in der Breite im Rahmen eines qualitätssicherungsprozesses ergibt sich jedoch die Herausforderung, die jeweilige Organisation zu einem aufgeklärten Umgang mit den von derartigen Werkzeugen ausgegebenen Befunden zu bewegen. Eine Diskussion dieser Problematik findet sich in verallgemeinerter Form auf der Seite Entwurf von Coding Guidelines.

Formale Verifikation

Formale Verifikation prüft, ob die Software ihrer Spezifikation entspricht. Die formale Verifikation benötigt daher als Referenz die Beschreibung des gewünschten Verhaltens bzw. gewünschter Eigenschaften der Software.

Dynamische Analyse

Testen

A test that reveals a bug has succeeded, not failed.

Boris Beizer, Software Testing Techniques

Die mit dem Test von Software verbundenen Arbeitsschritte (Testspezifikation, Testimplementierung, Testdurchführung, Testauswertung) beanspruchen einen großen Teil der Gesamtentwicklungszeit. Es ist jedoch möglich, einen mehrfachen Nutzen aus dem Aufwand für das Testen zu ziehen. Folgende Ziele können bei geschickter Herangehensweise und geeigneter Gestaltung von Tests erreicht werden:

Einige dieser Ziele lassen sich besser durch einen unabhängigen Tester (T), andere durch den Entwickler des Codes (E) erreichen. Die Frage, ob der Entwickler seinen eigenen Code testen soll oder ob die Tests immer zwingend von einem unabhängigen Tester erstellt werden müssen ist daher falsch gestellt. Besser wäre zu fragen, wer beim Testen zur Erfüllung welcher Testziele beiträgt.

Dem unabhängigen Tester ist es möglich, mit der folgenden Einstellung auf Fehlersuche zu gehen: "Ihr Luschen, Euer Code ist eh Schrott, und das werd ich Euch zeigen." Diese fürs Testen hilfreiche Sicht auf seine Person und seinen Code fehlt dem Entwickler zumeist.

Dagegen übernimmt der Entwickler selbst sinnvollerweise die Erstellung bzw. Überarbeitung von Testfällen im Rahmen von Refactoringmaßnahmen. Auch die Gestaltung von Testfällen, so dass sie zugleich als Beispiele für einen Anwender taugen und sich harmonisch in die weitere Dokumentation eingliedern, ist eine Aufgabe für den Entwickler, der seinen Code ohnehin dokumentieren wird.

Überwachung

Es gibt eine Reihe von Techniken, bei denen die Korrektheit der Software während der Ausführung überprüft wird. Dazu gehören beispielsweise explizit vom Entwickler in den Code eingebrachte Assertions, aber auch vom Compiler erzeugte Prüfungen von Zeigern, Array-Indices, Wertebereichen, Stacküberläufen, und schließlich auch hardwareseitige Überwachungsmechanismen wie beispielsweise Prozessor-Exceptions oder Watchdogs.

Querschnittsthemen

Planung

Dokumentation

Konfigurationsmanagement

Tools