Zum Hauptinhalt springen

Teil 4: Software: Der Compiler weiß mehr über Vollständigkeit als jeder Workshop

·10 min
Thomas Arends
Autor
Thomas Arends
Management Consulting, Interim-, Projekt-, Qualitätsmanagement

Software hat keine Physik. Es gibt keine Geometrie, aus der Kontaktstellen folgen, und keine Netzliste, aus der Verbindungen folgen. Nach der Logik der beiden vorigen Teile müsste hier jede Aufzählbarkeit enden.

Das Gegenteil ist der Fall — für einen Teil. Was eine Softwarekomponente deklariert nach außen anbietet und aufruft, ist vollständiger maschinell ableitbar als in jeder anderen Domäne. Der Compiler kennt jede Signatur, der Linker jede Abhängigkeit, das Schnittstellenschema jedes Feld. Kein Workshop kommt an diese Vollständigkeit heran.

Und trotzdem sitzen die schwersten Softwarefehler nicht dort. Sie sitzen in dem, was nirgends deklariert ist: gemeinsam genutzter Zustand, Reihenfolge, Zeitverhalten, geteilte Ressourcen.

Das ist die schärfste Ausprägung des Musters aus der ganzen Reihe. Die Domäne mit der besten Beweislage hat gleichzeitig den gefährlichsten Restbereich — weil die gute Beweislage im deklarierten Teil eine Sicherheit suggeriert, die für das Ganze nicht gilt.

Zwei Kopplungsarten
#

Deklarierte Schnittstelleaus dem Code ableitbarModul AModul Bruft aufSignatur, Typ, KardinalitätAufrufer und Aufgerufener benanntWerkzeug findet jede StelleVollständigkeit: nachweisbarWas nicht deklariert ist, kompiliert nicht.Geteilter Zustandnirgends deklariertModul AModul BZustandReihenfolge, Zeit, SperrenVollständigkeit: nicht nachweisbarKompiliert einwandfrei und ist trotzdem falsch.

Der linke Teil ist der Fall, in dem Software besser dasteht als Mechanik und Hardware. Kontaktflächen muss man aus dem Modell auslesen, Netzlisten aus dem Schaltplan — Signaturen liest der Übersetzer ohnehin, sonst würde er nicht übersetzen.

Der rechte Teil hat kein Gegenstück in den anderen Domänen. Zwei Module, die sich nie gegenseitig aufrufen, können sich vollständig zerstören, wenn sie denselben Speicher, dieselbe Datei oder dieselbe Sperre benutzen. Und niemand hat das irgendwo erklärt.

Auch hier: nicht die Liste fehlt
#

Wie in der Hardware lohnt die Präzisierung, denn „nicht aufzählbar" ist zu grob.

Welche Module denselben Zustand erreichen können, ist aus dem Code ableitbar. Statische Analyse leistet genau das: Erreichbarkeit, Aliasing, welcher Zeiger auf welchen Speicher zeigen kann, welcher Thread welche Ressource anfassen kann. Die Kandidatenliste der geteilten Zustände ist also erzeugbar — mit den üblichen Einschränkungen der Analyse, aber erzeugbar.

Was nicht aufzählbar ist, liegt eine Ebene höher: die Verschränkung. Bei zwei Threads mit je m Zugriffen gibt es kombinatorisch viele mögliche Reihenfolgen, und diese Zahl wächst exponentiell. Jede einzelne Reihenfolge kann korrekt sein und eine falsch.

KandidatenlisteBewertung
MechanikGeometrie siebt binärIngenieururteil je Kontakt
Hardwarequadratisch, Sieb fehlt mangels DatenSimulation, Messung
Softwarestatisch ableitbarexponentiell viele Abläufe

Und hier liegt der Unterschied, der die Software von beiden anderen Domänen trennt: In der Hardware fehlt die Bewertung, weil Daten fehlen. Das ist ein behebbares Problem. In der Software gibt es einen bewiesenen Boden: Nicht-triviale semantische Eigenschaften von Programmen sind im allgemeinen Fall unentscheidbar — der Satz von Rice. Kein Werkzeug wird das je auflösen, so lange man beliebige Programme zulässt.

Der entscheidende Nachsatz ist „so lange man beliebige Programme zulässt". Genau da setzt der nächste Abschnitt an.

Der erlaubte Raum ist hier gestaltbar
#

In der Mechanik ist der physikalisch mögliche Raum eine Vorgabe. Die Geometrie bestimmt, welche Kontakte entstehen können; ändern lässt sich das nur durch Konstruktion. In der Hardware ist es ähnlich: Die Feldphysik gibt vor, was koppeln kann.

In der Software ist der mögliche Raum eine Entwurfsentscheidung.

EbeneWas zulässig ist
Maschinennah, ungeprüfte Zeigerfast jeder Speicher von fast jeder Stelle
Verwaltete Laufzeitumgebungnur typrichtige Zugriffe auf erreichbare Objekte
Kapselung ohne Umgehungspfadnur über deklarierte Schnittstellen
Unveränderliche Datenstrukturenkein schreibender Zugriff — die Fehlerklasse existiert nicht
Prozess- oder Speicherisolationnur über deklarierte Nachrichten

Jede Zeile verkleinert den möglichen Raum, ohne den gewollten anzutasten. Das ist keine Metapher, sondern dieselbe Operation, die in der Fertigung eine Vorrichtung leistet: Wenn das Werkstück nur in einer Lage hineinpasst, muss man die falschen Lagen nicht mehr analysieren.

Die vierte Zeile ist der stärkste Fall. Bei unveränderlichen Daten ist der Rest — die unerwünschten Einwirkungen — für die Klasse der konkurrierenden Schreibzugriffe leer. Nicht klein, nicht beherrscht, sondern leer. Es gibt keine Zeile mehr im Lückenbericht, weil es nichts mehr zu bewerten gibt.

Damit lässt sich die Mengenlehre des erlaubten Raums über alle Domänen einheitlich lesen:

DomäneMöglicher Raum ergibt sich ausVerkleinert durch
Prozess, MenschHandhabungsfreiheitVorrichtung, fehlersichere Gestaltung
MechanikGeometrieKonstruktion
HardwareFeldphysik, AnordnungSchirmung, Abstand, Massekonzept
SoftwareLaufzeitumgebung und SprachmittelKapselung, Unveränderlichkeit, Isolation

Die Software ist die einzige Zeile, in der sich der mögliche Raum für eine ganze Fehlerklasse auf null bringen lässt — und zwar zur Übersetzungszeit, nicht durch Prüfung. Genau das leisten Typsysteme, die Datenrennen strukturell ausschließen: Sie schränken die zulässigen Programme so weit ein, dass die Eigenschaft entscheidbar wird.

Das ist die praktische Antwort auf den Satz von Rice. Er gilt für beliebige Programme. Er gilt nicht mehr, wenn man die Sprache so wählt, dass die gefährlichen Programme gar nicht erst formulierbar sind.

Wie eine Softwarefunktion aussieht
#

Software kennt nur Signalflüsse. Es fließt kein Material und keine Energie, sondern Daten und Steuerung. Ansonsten gilt derselbe Baukasten wie überall:

Solange der Dienst im Zustand betriebsbereit ist, MUSS die Auftragsannahme die eingehende Anfrage vom Endpunkt an die Verarbeitung übergeben.

Merkmale: Antwortzeit 95. Perzentil ≤ 200 ms · Durchsatz ≥ 50 Anfragen/s · Nutzlast ≤ 2 MB Nachweis: Test

Auch hier gilt die n:1-Beziehung: eine Funktion, mehrere Merkmale. Und auch hier ist die Richtung Pflicht — „verarbeitet Anfragen" ohne Quelle und Senke sagt nicht, welchen Weg die Daten nehmen.

Die typischen Wirkverben:

VerbBeispiel
übergibtDer Endpunkt übergibt die Anfrage an die Verarbeitung.
wandeltDer Serialisierer wandelt das Objekt in das Austauschformat.
prüftDie Validierung prüft die Eingabe gegen das Schema.
speichertDas Repository speichert den Vorgang in der Ablage.
stellt bereitDer Zwischenspeicher stellt den Wert der Abfrage bereit.
sperrtDie Berechtigungsprüfung sperrt den Zugriff ohne gültige Rolle.
begrenztDie Drosselung begrenzt die Anfragerate je Aufrufer.
protokolliertDie Nachweisführung protokolliert jede Änderung.

Zwei Anmerkungen dazu, die aus den vorigen Teilen kommen und hier unverändert gelten:

Sperren ist eine Wirkung, gemessen wird der Rest. Bei der Berechtigungsprüfung ist der Rest die Rate der fälschlich gewährten Zugriffe; bei der Drosselung die Zahl der durchgelassenen Anfragen über dem Grenzwert. „Keine unbefugten Zugriffe" ist als Merkmal ebenso unbrauchbar wie „Leckrate = 0".

Ein Verb pro Satz. Der häufigste Verstoß in Softwareanforderungen lautet „verarbeitet und speichert". Das sind zwei Funktionen mit zwei Fehlermodusgruppen und meist zwei verschiedenen Verantwortlichen.

Der Zustand ist explizit — das ist ein Vorteil
#

In der Mechanik muss man Betriebszustände erst zusammentragen. In der Software sind sie oft bereits modelliert: als Zustandsautomat, als Lebenszyklus eines Vorgangs, als Bereitschaftsstufe eines Dienstes.

FunktionStartbetriebsbereitÜberlastWartungAbschaltung
Anfrage übergebengiltreduziertmuss enden
Vorgang speicherngiltgiltmuss abschließen
Zugriff sperrengiltgiltgiltgiltgilt
Anfrage ablehnengiltgiltgiltgilt

Drei Beobachtungen, die jeweils einen realen Ausfalltyp betreffen:

Beim Herunterfahren müssen laufende Vorgänge abschließen, nicht einfach enden. Wer den Abschaltzustand nicht als eigene Spalte führt, hat für abgebrochene Schreibvorgänge keine Anforderung — und findet das Ergebnis später in inkonsistenten Daten.

Die Berechtigungsprüfung gilt in jedem Zustand. Auch im Wartungs- und Abschaltzustand. Genau dort wird sie in der Praxis am häufigsten umgangen, weil „das ist ja nur Wartung".

Überlast ist ein eigener Zustand. Kein Fehlerzustand, sondern ein regulärer Betriebszustand mit eigenen Anforderungen — was wird abgelehnt, was wird verzögert, was läuft weiter.

Der nicht nachweisbare Rest
#

Was bleibt, ist der gefährliche Teil: Wettlaufsituationen, Verklemmungen, verlorene Aktualisierungen, Nutzung nach Freigabe, Zeitannahmen zwischen Komponenten.

Diese Fehler haben eine gemeinsame Eigenschaft, die sie von allen anderen unterscheidet: Sie sind in keiner Deklaration sichtbar und im Test nur zufällig reproduzierbar. Der Code kompiliert, alle Schnittstellen stimmen, und die Tests laufen grün — bis die Last steigt oder ein Zeitverhalten sich verschiebt.

Der Umgang damit ist derselbe wie bei der Feldkopplung in der Hardware und bei der menschlichen Handhabung im Prozess: strukturelle Verkleinerung statt Aufzählung.

MaßnahmeWirkung
Unveränderliche Datenstrukturengeteilter Zustand entfällt strukturell
Ein Schreiber je Datenbereichkonkurrierende Änderungen unmöglich
Sperrhierarchie mit fester ReihenfolgeVerklemmung ausgeschlossen
Kapselung ohne UmgehungspfadZugriff nur über deklarierte Schnittstelle
Nachrichtenaustausch statt gemeinsamer SpeicherKopplung wird deklariert

Die letzte Zeile ist die stärkste: Sie überführt nicht deklarierte Kopplung in deklarierte. Was vorher unsichtbar über den Speicher lief, ist danach eine deklarierte Nachricht — und damit im linken Teil des Bildes.

Und wie in der Hardware gilt: Diese Maßnahmen sind keine Umsetzungsdetails, sondern Anforderungen mit Merkmal und Nachweis. „Die Vorgangsverwaltung erlaubt genau einen schreibenden Zugriff je Vorgang. Nachweis: Analyse."

Wo der Zustandsraum beschränkt werden kann, gibt es zusätzlich formale Verfahren: Modellprüfung durchsucht alle Abläufe eines endlichen Modells erschöpfend. Das ist kein Widerspruch zum Satz von Rice, sondern dieselbe Bewegung wie beim Typsystem — man beschränkt den Raum, bis die Frage entscheidbar wird. Der Preis ist, dass das geprüfte Modell und der laufende Code zwei verschiedene Dinge sind.

Die Tyrannei, die hier zuerst beschrieben wurde
#

Es passt, dass die Reihe in der Software endet, denn hier wurde das zugrundeliegende Problem zuerst benannt.

Tarr, Ossher, Harrison und Sutton beschrieben 1999 die tyranny of the dominant decomposition: Jedes System lässt sich nach mehreren Kriterien zerlegen, aber ein Artefakt kann immer nur einer Zerlegung folgen. Alle anderen Sichten müssen sich verbiegen.

In der Software ist die dominante Zerlegung die Modulstruktur. Alles, was quer dazu liegt — Protokollierung, Berechtigung, Nachweisführung, Fehlerbehandlung —, verteilt sich über alle Module und hat nirgends ein Zuhause. Genau das war der Ausgangspunkt der aspektorientierten Programmierung.

Im Anforderungswesen ist es dasselbe Problem mit anderer Besetzung: Die Stückliste dominiert, und die funktionale Sicht, die Prüfsicht und die Wirkkettensicht bauen sich jeweils eigene Beschreibungen. Ab diesem Moment gibt es keine gemeinsame Wahrheit mehr, sondern nur noch Abgleichaufwand.

Der Ausweg ist derselbe wie in der Softwarearchitektur: keine Zerlegung bevorzugen. Die Felder tragen die Information, die Zerlegung ist eine Abfrage. Nach Träger gruppiert entsteht die Strukturansicht, nach Fluss die Wirkkette, nach Merkmal die Prüfliste, nach Domänengrenze die Schnittstellenliste. Kein Artefakt ist mehr das Original.

Was die Reihe insgesamt sagt
#

Vier Domänen, ein Baukasten, unterschiedliche Beweislagen:

DomäneKopplungsstelleKandidatenlisteWarum die Bewertung schwierig ist
Systemgeforderte Schnittstellekeine — es wird gefordert, nicht vorgefundennoch keine Lösung vorhanden
MechanikKontaktstelleGeometrie, binär gesiebtIngenieururteil je Kontakt
HardwareAnschluss, Netz, FeldNetzliste plus alle PaareEmissions- und Empfindlichkeitsdaten fehlen
Softwaredeklarierte Schnittstelle, geteilter ZustandCompiler und statische AnalyseAbläufe sind exponentiell, Eigenschaften unentscheidbar

Die Kernaussage steht in der letzten Spalte.

Eine Methode, die nur den nachweisbaren Teil bearbeitet, ist in jeder Domäne unvollständig — und zwar auf eine Weise, die im Werkzeug grün aussieht. Der Wert liegt darin, beide Anteile getrennt auszuweisen: Hier ist der Nachweis geführt, dort ist er nicht führbar und muss durch Architektur ersetzt werden.

Und der zweite Ertrag der Reihe ist der Umgang mit dem Rest. Er ist überall derselbe: Wo man nicht aufzählen kann, verkleinert man den möglichen Raum. Eine Vorrichtung im Prozess, ein Trennungsabstand in der Hardware, eine unveränderliche Datenstruktur in der Software. Drei Domänen, dieselbe Bewegung.

Das ist eine unbequeme Aussage, weil sie einem Werkzeug verbietet, überall grün zu sein. Sie ist aber die einzige, die im Audit und im Feld standhält.


Wo ich stehe
#

Was in dieser Reihe beschrieben ist, baue ich als Werkzeug — unter dem Namen OTSM. Die Feldstruktur, die Projektionen über alle Domänen, der getrennte Ausweis von nachgewiesen und offen: das läuft.

Fertig ist es (noch) nicht. Es wird wahrscheinlich auch in zwanzig Jahren noch nicht fertig sein, und ich halte das für den Normalzustand, nicht für ein Eingeständnis. Ein Werkzeug, das Methodik trägt, ist so lange unfertig, wie die Methodik weiterdenkt. Was ich sagen kann: Es geht.

Die Reihe zu dieser Methode:

TeilInhalt
Anforderungen als DatensatzKunde und System, domänenneutral
MechanikKontaktstellen als Vollständigkeitsanker
HardwareAnschlüsse und Feldkopplung
Softwaredieser Artikel

Wenn Sie das prüfen wollen
#

Ich spiele mit offenen Karten, deshalb der Ablauf ohne Umweg:

Sagen Sie mir Ihre Branche. Nicht Ihr Problem — noch nicht. Die Branche entscheidet, welche Regelwerke gelten, welche Nachweise verlangt werden und wo die Zerlegung üblicherweise dominiert. Das kann ich vorbereiten.

Im Gespräch sagen Sie mir Ihr Problem. Dann passiert eines von zwei Dingen:

Entweder ich zeige Ihnen die Lösung. Das dauert in der Regel keine fünfzehn Minuten, und Sie sehen sie an Ihrem eigenen Fall. Danach wissen Sie, ob Sie mehr davon wollen.

Oder ich kann es nicht. Dann sage ich Ihnen das — in denselben fünfzehn Minuten, mit der Begründung, woran es liegt. Das ist kein schlechteres Ergebnis; es ist nur ein schnelleres.

Was ich nicht mache: Ihnen zusagen, dass die Methode Ihren Fall trägt, bevor ich ihn gesehen habe. Eine Methode, die angeblich alles kann, hat keine Grenzen benannt — und was keine Grenzen hat, ist nicht prüfbar. Die Grenzen dieser hier stehen oben unter „Der nicht nachweisbare Rest". Wenn Sie einen Fall haben, an dem sie zerbricht, will ich ihn hören. Das ist keine Höflichkeit, das ist der einzige Weg, wie so etwas besser wird.