Die Motivation
Kennen Sie die folgenden Situationen?

  • Wenn es auf ein Release zugeht, werden Good Practices wie das präzise Beschreiben der Anforderungen über Bord geworfen und alle Aufmerksamkeit auf das Fertigstellen der Software gelenkt.
  • Die Anforderungsdokumentation ist nach einiger Zeit nicht mehr auf dem aktuellen Stand, sie hinkt der Software hinterher.
  • Wenn die ursprünglichen Entwickler nicht mehr verfügbar sind oder viele Teams am selben Produkt arbeiten, wird es zusehends schwieriger herauszufinden, welche Teile der Software angepasst werden müssen um eine bestimmte Funktionalität zu ändern.

Diese Probleme resultieren häufig daraus, dass es kein einheitliches Konzept gibt, wie Anforderungen systematisch und möglichst direkt im Code abgebildet werden.

Auch wenn sich das für Sie erst einmal komisch anhört: Code ist eine Anforderungsspezifikation, und zwar an die Software. Diese Spezifikation ist so ausführlich und präzise, dass sie lauffähig ist. Sie ist daher die einzige Spezifikation, die wirklich bestimmt, wie sich die ausgelieferte Software verhält.

Code wird aber meistens nur von den Entwicklern verstanden. Damit gestaltet sich die Kommunikation über den Code mit anderen Stakeholdern, wie Domänenexperten oder Product Ownern, schwierig und wird meistens vermieden.

Eine Lösung für die genannten Probleme ist Code Based Modeling.

Die Vision

CodeBasedModeling2Mein Ziel ist es, eine Brücke zwischen Anforderungen und Code zu bauen. Dazu gehört: Software einfach änderbar und wartbar zu machen, und so insbesondere die agile Softwareentwicklung zu unterstützen.

Dieses Ziel kann man erreichen, indem man Anforderungen durch lauffähige Modelle im Code dokumentiert. Dafür verwende ich den Begriff „Code Based Modeling“.

Good Practices der Anforderungsdokumentation, im Code

In diesem Kapitel beschreibe ich einige Good Practices der Anforderungsdokumentation und erläutere, wie sie  im Code prinzipiell umgesetzt werden können. (Natürlich gibt es dafür mehrere Möglichkeiten. Ich schlage nur eine konkrete Möglichkeit vor.)

Los geht’s.

Funktionale Softwareanforderungen definieren die durch Benutzer beobachtbare Reaktion der Software auf Ereignisse, die unter bestimmten Bedingungen erfolgt.

Ein Teil des Codes bildet die funktionalen Softwareanforderungen als Use-Case-Schritte ab, die mit Ereignissen und Bedingungen im Code verknüpft sind.

Beispiel:

Use-Case-Schritt: „Produkt in Warenkorb legen“.

Ereignis: „Produkt mit Produktnummer pn in den Warenkorb legen“ wurde angestoßen.

Bedingung: Maximalzahl der Produkte im Warenkorb noch nicht erreicht.

Reaktion der Software: Software zeigt aktualisierten Warenkorb an, der Produkt pn zusätzlich enthält.

Es wird dokumentiert, welche Nutzergruppen welche Softwarefunktionen ausführen können.

Nutzergruppen werden im Code als Akteure modelliert. Die Akteure sind mit Use Cases verbunden und diese enthalten die Use-Case-Schritte. Jeder Akteur im Code „kennt“ also (indirekt) alle Use-Case-Schritte, die von der Nutzergruppe, die er repräsentiert, angestoßen werden können.

Dadurch kann der Code sicherstellen, dass ein Benutzer nur die Use-Case-Schritte zu sehen bekommt und anstoßen kann, die für seine Nutzergruppe vorgesehen sind.

Funktionale Komponentenanforderungen definieren die Fähigkeiten der Komponenten, die der Komponentenumwelt zur Verfügung stehen. Die Komponentenfähigkeiten leiten sich aus den funktionalen Softwareanforderungen ab.

Die Fähigkeiten der Komponenten leiten sich aus den Use-Case-Schritten ab. Zum Beispiel benötigt ein Use-Case-Schritt „Produkt suchen“, den ein Benutzer durchführt, die Fähigkeiten von mindestens zwei Komponenten:

  • die Fähigkeit „Produkte finden“ der Datenbank-Komponente und
  • die Fähigkeit „Produkte darstellen“ der User-Interface-Komponente

Die komponenteninterne Arbeitsweise bleibt der Umwelt verborgen.

Die Trennung von Außen und Innen der Komponenten ermöglicht, verschiedene Umsetzungen zu vergleichen, gegeneinander auszutauschen oder als Varianten zu betreiben.

Die Use-Case-Schritte haben selber keine Fähigkeiten, sondern rufen über Schnittstellen die Fähigkeiten der Komponenten auf. Mit Schnittstelle ist hier nicht unbedingt das programmiersprachliche Konzept (wie ein Java Interface) gemeint, sondern die Grenze der Komponente, an der die Fähigkeiten der Komponente öffentlich gemacht werden, während die innere Arbeitsweise der Komponente von außen unzugänglich bleibt.

Die Use-Case-Schritte sind frei von Entscheidungen für bestimmte Technologien, wissen also zum Beispiel nicht, ob die Datenbank SQL unterstützt oder welches UI-Framework zum Einsatz kommt.

Die Komponenten können daher relativ leicht ausgetauscht werden, wenn es technologische Änderungen gibt. Außerdem wird die Testbarkeit der Software erhöht, weil die Use-Case-Schritte beispielsweise auch ohne die User-Interface-Komponente getestet werden können (sie wird durch Test Doubles ersetzt).

Die zentrale Verwaltung der Anforderungen, plus die Verfolgbarkeit von Anforderungen zur Implementierung ermöglicht eine Auswirkungsanalyse bei Anforderungsänderungen.

Die Use Cases enthalten Use-Case-Schritte, die Komponentenfähigkeiten aufrufen. Damit erreicht man eine implizite Verfolgbarkeit von den Use Cases zu den Komponentenfähigkeiten im Code. Wenn sich die Use Cases und Use-Case-Schritte an einer zentralen Stelle im Code befinden, ist somit bei Anforderungsänderungen sofort klar, welche Komponentenfähigkeiten davon potentiell betroffen sind.

Über die Bedeutung der Begriffe, die in den Anforderungen verwendet werden, sollte ein gemeinsames Verständnis herrschen.

Die Namen der Klassen und Methoden im „Anforderungscode“ enthalten Wörter der Domäne und so weit wie möglich keine technischen Begriffe. Dadurch wird eine automatisierte Übersetzung des Codes in andere Sprachen möglich, am einfachsten in UML-Diagramme. Über diese, stets aktuelle Anforderungsdokumentation kann dann auch mit Stakeholdern, die nicht Entwickler sind, gesprochen werden.

Einblick in die Lösung

Um zu prüfen, ob sich Code Based Modeling auch in der Praxis umsetzen lässt, habe ich Beispielanwendungen geschrieben, die die Good Practices exemplarisch umsetzen. Dabei bin ich von einer eins-zu-eins-Abbildung von Konzepten wie Use Cases, Use-Case-Schritten usw. auf Klassen ausgegangen.

Das folgende Bild zeigt die Klassen für Akteure, Use Cases und Use-Case-Schritte in einer Beispielanwendung, mit der man Produkte im Internet kaufen kann: ShoppinappClasses

Aus diesem Modell im Code lassen sich automatisiert verschiedene UML-Diagramme generieren, über ein Add-On, das auf PlantUML basiert (Link). Ein UML Use-Case-Diagramm zeigt die Akteure und ihre Verbindung zu den Use Cases, nicht aber die Use-Case-Schritte. Im Fall unserer Beispielanwendung ist das Diagramm sehr einfach, denn man kann mit ihr ja nur (als Endkunde) Produkte kaufen. ShoppinappUsecaseOverview

Um nun ein konkretes Szenario eines Use Cases im Code zu durchlaufen, wie zum Beispiel den Basic Flow, schreibt der Entwickler einen Test, der nacheinander die einzelnen Use-Case-Schritte über Ereignisse anstößt („triggert“) und die erwartete Reaktion der Software mit der tatsächlichen vergleicht. Die durchlaufenen Schritte können wiederum automatisiert in einem Diagramm dokumentiert werden: Shoppinapp_UseCase_BasicFlow

Obiges Diagramm zeigt also Use-Case-Schritte des „Purchase Product“ Use Cases – von der Anzeige der Produkte durch die Software, bis zur Bestätigung der Zahlung durch den Benutzer. Sobald ein Schritt angestoßen wird, ruft er  Komponentenfähigkeiten auf. Auch diese können automatisiert in einem Diagramm dokumentiert werden.

Die Details

Viele Details der Lösung habe ich in diesem Beitrag nicht angesprochen, weil sie den Rahmen sprengen würden. Einige dieser Details werden in folgendem (englischen) Youtube-Video erläutert: Link.

Was wird aus der Anforderungsdokumentation?

Auf alle Dokumentationen außerhalb des Codes kann man trotz Code Based Modeling nicht verzichten. Zum einen ist CBM nicht vollständig – Qualitätsanforderungen werden zum Beispiel bisher noch nicht berücksichtigt. Zum anderen sind gerade für die initiale Ermittlung von Anforderungen leichtgewichtigere, weniger formale Dokumentationstechniken als der Code gefragt – Modelle am Whiteboard, über die das Team spricht, zum Beispiel.

Wenn Sie allerdings eine ausführliche Anforderungsspezifikation schreiben, dann sollten Sie sich bewusst sein, dass Sie Redundanz erzeugen – denn die gleiche Information wird noch einmal im Code abgebildet. Und mit der Redundanz kommen die Probleme, die ich im Motivationskapitel beschrieben habe.

Status von Code Based Modeling

Code Based Modeling funktioniert. Ich habe es nicht nur an einfachen Beispielen wie oben getestet, sondern auch an einer Anwendung mit mehreren tausend Lines of Code. Trotzdem steht die Methode noch am Anfang – ich bin sehr an einer weiteren Verbreitung und Erprobung interessiert. Wenn Sie Interesse haben, melden Sie sich bei uns (Link).

Insbesondere sehe ich die Chance, mit CBM agile Vorgehensweisen auf technischer Seite zu unterstützen – den schnellen Änderungsmöglichkeiten auf Anforderungsseite sollte eine leicht änderbare Software gegenüberstehen, damit agiles Arbeiten wirklich funktioniert. Ein Beispiel für eine solche agile Vorgehensweise, die sich hervorragend mit Code Based Modeling kombinieren lässt, ist der Use Case 2.0 Ansatz (Link).