Bausteine für die Softwareevolution

Erstellt am 6. September 2011 von Ralfwestphal @ralfw

Wenn wir Software naturnah entwickeln wollen, also als evolvierbares System, wie sollte sie denn dann aufgebaut sein? In meinem vorherigen Artikel habe ich mir Gedanken über eher organisatorische Voraussetzungen für naturnahe Entwicklung gemacht. Diesmal will ich die technische Seite angehen. Oder besser: wieder angehen, denn vor Jahren schon hatte ich in diese Richtung spekuliert [1]. Damals waren meine Idee eher im Konzeptionellen verblieben - heute habe ich eine Vorstellung von ihrer Implementation.

Voraussetzung Selbstähnlichkeit

Ich glaube immer noch, dass Software nur wirklich gut evolvieren kann, wenn ihre Struktur selbstähnlich ist. Das heißt erstens, dass es in Software mehrere Ebenen gibt. Und zweitens bedeutet es, dass der grundlegende Aufbau aller Ebenen gleich ist.

Ich möchte diese Ebenen jedoch nicht Schicht/Layer nennen. Der Begriff ist überladen und deutet deshalb in die falsche Richtung. Stattdessen wähle ich Stratum; damit lehne ich mich an Abelson/Sussman an, die von Stratified Design sprechen.

Selbstähnlichkeit hat den Vorteil, dass Erfahrungen und Denken nicht ebenenspezifisch sind. Man kann auf einer beliebigen Ebene beginnen und sich sowohl nach oben wie nach unten vorarbeiten, ohne einen konzeptionellen Bruch zu erleiden.

Methoden sind etwas ganz anderes als Klassen, die wiederum etwas anderes sind als Assemblies, die wiederum ganz anders sind als Prozesse. Wenn wir mit diese Mitteln Software in unserem mentalen Modell repräsentieren, dann ist das kompliziert, weil immer wieder anders.

Eine Reduktion auf Klassen allein jedoch ist nicht möglich, da Klassen konzeptionell nichts enthalten außer Methoden. Klassen sind nicht schachtelbar. Sie sind dafür gedacht, Zustand und Funktionalität zusammenzufassen. Das war´s. [2]

Selbstähnlichkeit ist für mich die Voraussetzung, dass Systeme sich entwickeln können. Sie können in der Breite wachsen (innerhalb einer Ebene) oder sie können nach oben wachsen (Abstraktion, Aggregation) oder sie können nach unten wachsen (Verfeinerung, Spezialisierung).

Voraussetzung Unabhängigkeit

Dann glaube ich, dass die Bausteine in den Strata einer Software, unabhängig von einander sein sollten. Abhängigkeiten – statische wie dynamische – sind der Anfang allen Unwartbarkeitsübels. Dependency Injection zum Zwecke der Bereitstellung von Dienstleistungen ist keine Lösung, sondern perpetuiert das fundamentale Problem.

In der Natur finden sich keine Dienstleistungsabhängigkeiten in der Form, wie sie in Software allerorten existieren. Keinem Organismus wird eine Abhängigkeit injiziert. (Nur Parasiten injizieren sich – wir denken an Sacculina, Juwelwespe oder Ridley Scotts Alien.) Organismen brauchen einander, aber nicht in Form von “Referenzen” oder “Handles”. Sie sind vielmehr auf ihre jeweils autonom hergestellten “Outputs” angewiesen. Das ist gelebte Entkopplung.

Dasselbe gilt für den Maschinenbau oder die Elektrotechnik. Einem Motor wird kein Benzintank injiziert, auch wenn er ohne den Treibstoff nicht arbeiten kann. Benzintank und Motor werden lediglich verbunden; es gibt Berührungspunkte, aber keine Abhängigkeiten.

Allemal gibt es keine so breiten Abhängigkeiten wie die durch Interfaces üblicherweise eingegangenen. Ein Motor ist nicht von einem Chassis oder einen kompletten Restauto abhängig. Er hat verschiedene Input-Kanäle, die nur irgendwie gespeist werden müssen. In einem Motorprüfstand wird deutlich, dass das auch ganz anders als mit einem Auto geschehen kann. Dasselbe gilt für einen menschlichen Körper, der ganz ohne Herz und Lunge auskommen kann, wenn an die Gefäße eine Herz-Lungen-Maschine angeschlossen ist.

Abhängigkeiten machen Systeme starr. Evolvierbarkeit setzt daher maximale Unabhängigkeit voraus.

Voraussetzung Nachrichtenorientierung

Die Verbindung zwischen Lebewesen ist immer “nachrichtenorientiert”. Wichtiger ist aber die Nachrichtenorientierung. “Daten” fließen unidirektional von einem Lebewesen zu einem anderen, gezielt oder diffus. Folgt man dem Konstruktivismus, dann ist die Welt (und damit andere Lebewesen) gar nicht direkt erfahrbar. Wir haben lediglich eine begrenzte Wahrnehmungsbreite und empfangen Signale. Die Ausgangspunkte dieser Signale selbst haben wir nie in der Hand. Wir konstruieren sie uns aus den Signalen.

Wenn das für die Evolution der Natur so zentral ist, dann scheint es mir vorteilhaft für Software, die ebenfalls hochgradig evolvieren muss. Ihre Bausteine sollten ebenfalls nur über Nachrichten kommunizieren. Das heißt, es gibt kein Call/Response, sondern nur unidirektional fließende Datenpakete. (Inwiefern die Referenzen auf den Zustand von Bausteinen enthalten können/dürfen, lasse ich hier mal dahingestellt.)

Softwarebausteine verstehen bestimmte Nachrichten, die sie von der Umwelt empfangen. Und sie senden eine bestimmte Menge von Nachrichten an ihre Umwelt. Sendung und Empfang sind in der Natur asynchron. Zwischen Softwarebausteinen würde ich Asynchronizität jedoch nicht zwingend voraussetzen.

Die Form evolvierbarer Software

Wenn ich diese Voraussetzungen zu einer Form für Softwarebausteine zusammenfasse, dann kommt das folgende Bild heraus:

Ich könnte diesen Softwarebaustein “Objekt” nennen, da er zustandsbehaftet ist und über Nachrichten kommuniziert. Aber “Objekt” ist so belastet, dass ich zumindest heute auf einen neutralen Begriff ausweichen möchte. Ich nenne ihn deshalb “Holon”. Das passt zur Selbstähnlichkeit; Holons sind “Dinger”, die gleichzeitig Teil und Ganzes sind.

Jedes Holon nimmt die Umgebung durch einen “Kanal” wahr und sendet über einen weiteren “Kanal” Signale an seine Umgebung, die dann andere Holons wahrnehmen können. Wahrnehmungen (Input) verarbeitet das Holon zu Zustand und/oder Signalen (Output).

Natürlich kann jedes Holon nur eine bestimmte Menge von Signalen verarbeiten und erzeugen. Signale bzw. Nachrichten bestehen daher nicht nur aus Daten, sondern haben auch noch einen Typ oder eine Bedeutung. Die Zahl 42 mag einmal die Bedeutung einer Antwort auf die ultimative Frage haben und einmal schlicht das Alter einer Person sein.

Aus Holons lassen sich nun Verarbeitungsstrukturen auf mehreren Ebenen (Strata) bilden:

Meine These ist, dass Software viel besser evolvierbar wird, wenn wir sie aus Holons aufgebaut denken. Die scheinen merkwürdig eingeschränkt gegenüber unseren heutigen OOP-Objekten. Ich behaupte aber mal, dass einen eventuelle Einschränkung mehr als kompensiert wird durch das, was wir bekommen. Und das wir nur bekommen, was wir wollen – viel mehr Evolvierbarkeit –, wenn wir uns einschränken.

Wir denken mit Holons nicht mehr über Abhängigkeitsverhaue nach. Wir haben einen ganz regelmäßigen Aufbau von Software von der kleinsten Codeeinheit bis zur größten. Wir können ganz natürlich Stratified Design betreiben. Und wir bekommen Bausteine, die sich von Hause aus für asynchrone/parallel Verarbeitung eignen.

Klar, das bedeutet, wir müssen unsere Denkstrukturen ändern und Gewohntes über Bord werfen. Der in Aussicht gestellte Gewinn scheint mir jedoch groß genug, um es damit zu probieren. Und so schwer ist es ja auch nicht, es auszuprobieren. Hier das universelle Interface für Holons:

interface IHolon {
  void Process(IHolonMessage input, Action<IHolonMessage> output);
}

Im Grunde reicht sogar ein Delegat, um jeden Baustein in jedem Stratum zu beschreiben:

delegate void Holon(IHolonMessage input, Action<IHolonMessage> output);

Und die Nachrichten, die zwischen den Holons fließen, sind auch ganz einfach:

interface IHolonMessage {
   string Type {get;};
   object Data {get;};
}

Kaum zu glauben, aber ich denke, dass mit solch einfachen Mitteln sich grundsätzlich alle Softwarelösungen ausdrücken lassen. [3] Diese einfachen Mittel stellen für mich das Rückgrat evolvierbarer Software dar.

PS: Wenn in der Softwareentwicklung Eleganz wirklich einen hohen Stellenwert hat, dann wage ich mal keck zu hoffen, dass eine Softwarestruktur dieser Art doch einigen Appeal hat. Ich zumindest finde sie in ihrer Einfachheit elegant.

PPS: Ich weiß, die Holons sehen aus wie Funktionseinheiten des Flow-Designs. Natürlich gibt es da auch einen Zusammenhang. An dieser Stelle überlege ich jedoch grundlegender. Deshalb haben Holons auch nur einen Eingang und einen Ausgang.

Spendieren Sie mir doch einen Kaffee, wenn Ihnen dieser Artikel gefallen hat...

Fußnoten

[1] s. ältere Artikel in der Rubrik “Software als System” in diesem Blog, insbesondere diesen und diesen Artikel.

[2] Das heißt nicht, Objekte seien nutzlos für evolvierbare Software. Nein, ich denke, wir müssen keine neue Programmiersprache erfinden, sondern die vorhandenen nur besser einsetzen.

[3] Natürlich meine ich damit nicht, dass Software bis hinunter zu Ausdrücken aus Holons aufgebaut werden sollte. Mir geht es um das Herunterbrechen von Funktionalität bis zu einer Granularität, dass man begründet zuversichtlich sein kann, ein Blatt-Holon mit wenigen Zeilen konventionellen Codes umsetzen zu können.