Suche
Über den Missbrauch von XML in der Softwareentwicklung

Vorsicht ansteckend

Johannes Link

Die aktuelle Wunderwaffe der Softwareindustrie heißt XML. Kein Programmierwerkzeug, dessen neueste Version nicht mit dem Slogan Jetzt mit noch mehr XML! beworben würde. Als vorsichtige Programmierernatur bringt mich diese Euphorie in einen ernsthaften Zwiespalt: Auf der einen Seite habe ich in mühevollen Jahren die grundlegenden Prinzipien der Modularisierung und anschließend der Objektorientierung für gut befunden und verinnerlicht. Auf der anderen Seite spüre ich das herablassende Lächeln meiner Kollegen, wenn ich zugebe, dass ich noch in Java programmiere und nicht schon längst in XML konfiguriere.

Der Streitpunkt

Gewichtige Vorteile der eXtensible Markup Language sind unbestreitbar:

  • XML ist der akzeptierte Standard für die Beschreibung hierarchischer Daten. Die große Akzeptanz hat zahlreiche Werkzeuge entstehen lassen, sowohl im kommerziellen Bereich als auch im Open Source-Sektor.
  • Alle gängigen Programmiersprachen bringen mittlerweile Bibliotheken mit, um XML zu parsen, zu erzeugen und zu transformieren. Dies erspart dem Programmierer die Mühen, einen eigenen Parser bzw. Generator zu schreiben.
  • XML-Daten führen einen Teil ihrer Metainformationen immer mit sich. Dies ermöglicht es einer lesenden Applikation, auch nach einer Änderung der Datenstruktur noch sinnvoll mit einem XML-Dokument umzugehen.
  • Es gibt eine standardisierte Spezifikationssprache für erlaubte XML-Strukturen. Genauer gesagt gibt es sogar zwei: Die alte DTD und das neuere XML Schema.

Mit den genannten Eigenschaften drängt sich XML unvermeidbar auf, wenn es darum geht, konfigurierbare Programmeinstellungen in Dateien auszulagern: Der Programmierer überlegt sich, in welcher Struktur die Applikationspräferenzen am besten abgelegt werde können, schreibt eine entsprechende XML Schema-Definition und schon kann er die bereitgestellte Konfigurationsdatei auf korrekte Syntax und Struktur überprüfen und flexibel auf die einzelnen Elemente des Datenbaums zugreifen.

Konfiguration ist jedoch nicht gleich Konfiguration. Bei der Frage, wie viel Programmlogik nicht fest verdrahtet im Quellcode, sondern dynamisch änderbar in XML-Dateien ausgelagert werden sollte, scheiden sich die Geister. Die Verfechter der maximalen Iks-em-ellisierung führen ins Feld, dass

  • XML-Dateien auch leicht vom programmier-unkundigen Anwender angepasst werden können und solcherlei Änderungen keinen Kompilier- und Deploymentvorgang erfordern.
  • durch programmexterne Konfiguration das Verhalten einer Applikation zur Laufzeit ohne Neukompilieren geändert werden kann.

Warum ich mich dieser Argumentation nicht anschließen mag, ist Inhalt dieses Artikels.

jdbc://dbserver.de/instanceadminZAKHJD98798AJKHwww.myhost.de8080/myapp

So weit ist das alles sehr klar und für jedermann zu verstehen. Die selbe Information hielt man in Vor-XML-Zeiten ebenfalls in Konfigurationsdateien, mit dem einen Unterschied, dass sich jeder Programmierer seine eigene Syntax ausdachte, seinen eigenen Parser schrieb und auch gleich noch eine Miniapplikation für das Erstellen der Konfigurationsdatei.

Ein weitere Komplexitätsstufe wird erreicht, sobald man auf Redundanz in den Konfigurationsdaten stößt. Stellen wir uns beispielsweise vor, dass obige Webapplikation für mehrere Hosts und mehrere Datenbanken konfiguriert werden kann, wobei die selbe Datenbank in mehreren Hosts zur Anwendung kommen kann (aber nicht muss). Erweitern wir daher obiges Format um ein Konstrukt zur Referenzierung bestimmter Teilbäume, d.h. wir führen eine Objekt-ID für Datenbanken ein und referenzieren diese in der Host-Konfiguration:


...

...
www.myhost.de
...
www.yourhost.de
...
www.herhost.de
...

Ist das nicht wunderbar objektorientiert? Macht das ein Java-Compiler nicht genauso? Ja und nein. Ja, weil natürlich intern tatsächlich Objekt-IDs und Referenzen auf diese IDs verwaltet werden müssen. Nein, weil diese Tatsache für den Programmierer transparent ist, d.h. er muss sich nicht um die IDs kümmern; weder muss er dafür sorgen, dass eindeutige IDs vergeben werden, noch muss er bei der Referenzierung darauf achten, dass er auf eine existierende ID verweist. Er benutzt lediglich das Objekt und basta.

Die Einführung von Objekt-IDs hat folgende Auswirkungen: Sowohl der Programmierer der Webapplikation als auch der konfigurierende Anwender müssen beim Einlesen bzw. Erstellen der Konfigurationsdatei darauf achten, dass in den -Knoten des XML-Baums nur IDs verwendet werden, die in den -Knoten auch vergeben wurden. Darüber hinaus muss die Eindeutigkeit der vergebenen IDs sichergestellt werden.

Beide Konsistenzbedingungen können übrigens von einer XML Schema-Definition nicht geleistet werden. Lediglich die Eindeutigkeit von IDs kann mit Hilfe des DTD-Attributs IDREF verifiziert werden, falls eine solche existiert. Auch gibt es keinen leichten Weg, die XML-Spezifikation sinnvoll um solche Referenzen zu erweitern. Der Grund liegt in der Natur des XML-Parsens: Sowohl DOM als auch SAX sind für echte Baumstrukturen ausgelegt und nicht für Vorwärts- oder Rückwärtsreferenzen auf andere Knoten. Übertragen auf Programmiersprachen könnte man sagen: Die Verwaltung von Querbezügen, z.B. zwischen Variablendefinition und Verwendung, ist Aufgabe des Compilers und nicht des Parsers. In Zeiten mächtiger IDEs ausgemerzt geglaubte Krankheiten erleben hierdurch eine Renaissance: Tippfehler und nur unvollständig vorgenommene Umbenennungen führen plötzlich wieder zu Laufzeitfehlern.

Die Objekt-ID mit Referenz ist lediglich ein Spezialfall eines Phänomens, das in Sprachen wie XML eine große Rolle spielt: ungesicherte Abhängigkeiten. Eine Abhängigkeit ist dann vorhanden, wenn ein einzelnes Datum nicht für sich selbst steht, sondern als Stellvertreter für etwas, das an anderer Stelle definiert wurde. Ungesichert ist die Abhängigkeit, wenn der Parser die Einhaltung der zugehörigen Konsistenzbedingung nicht verifizieren kann. Ein typisches Beispiel ist die Angabe einer Java-Implementierungsklasse wie in der folgenden Erweiterung unserer Konfigurationsdatei:


...
com.mydatabase.driver.MyDatabaseDriver

Dies ist ein Sonderfall des Ansatzes, Komponenten eines Systems nicht im Programmcode fest zu verdrahten, sondern diese Verantwortung der Konfiguration zu überlassen. Die Schwierigkeit liegt darin, dass erst bei Start der Webapplikation tatsächlich bestimmt werden kann, ob die entsprechende Klasse:

  • vorhanden ist,
  • im Klassenpfad liegt
  • und das richtige Interface unterstützt.

Und selbst, wenn all diese Voraussetzungen erfüllt sind, bringt jede Implementierung eines Interfaces ihre eigenen Besonderheiten und potenziellen Fehlerquellen mit. Dies soll keineswegs bedeuten, dass Datenbanktreiber nicht mehr konfiguriert werden sollten, sondern dass wir eine Konfigurationsänderung bewusst durch geeignete und ausreichende Maßnahmen absichern. Und nur in den allerwenigsten Fällen ist ein Programmstart ohne Laufzeitfehler ausreichend, um das funktionieren einer neuen Komponente sicherzustellen.

Gehen wir noch einen weiteren Schritt in Richtung eierlegende Woll-Milch-Konfiguration. Die Webapplikation soll in Abhängigkeit von der gewählten URI unterschiedliche Programmmodule aufrufen. Wir ergänzen die Konfiguration daher, um einen weiteren Bereich, den wir Landkarte (map) nennen:


...

Beginnt die Zugriffs-URI mit app1/, so soll Modul 1 aufgerufen werden, entsprechend Modul 2 bei app2. Zusätzlich wurde ein Default-Modul definiert, das für alle anderen Zugriffs-URIs gelten soll. Die Verwendung von Attributen anstatt zusätzlicher XML-Tags ist hier willkürlich und soll zeigen, dass die Problematik in beiden Fällen die gleiche ist.

Auch hier gibt es das Problem ungesicherter Abhängigkeiten, da module1|2|3 Konstanten sind, die wiederum unsere Webapplikation auswerten muss. Das Umbenennen bzw. Hinzufügen eines neuen Moduls im Applikationscode erfordert daher zusätzliche Änderungen in der Konfigurationsdatei. Der neue Komplexitätsgrad an diesem Stück XML ist jedoch, dass eine Änderung der Konfiguration auch eine Änderung der Ablauflogik und des Kontrollflusses zur Folge hat. Und mögliche Änderungen der Programmlogik erfordern – zumindest bei geschäftskritischer Software – die Ausführung systemweiter Regressionstests.

Probleme einer XML-zentrierten Entwicklung

Fassen wir noch einmal zusammen, was bislang klar geworden sein sollte:

  • XML bietet keinen standardisierten Mechanismus, um Querbezüge zwischen XML-Knoten herzustellen.
  • Viele Abhängigkeiten in XML-Konfigurationen sind ungesichert, d.h. ein bloßes Parsen – auch unter Zuhilfenahme eines Schemas – genügt nicht, um die Konsistenz der XML-Datei sicherzustellen.
  • Schon einfache Konfigurationsmöglichkeiten können einen Einfluss auf die tatsächliche Programmlogik haben.

Dies alles hat einen gewaltigen Einfluss auf eine XML-zentrierten Applikationsentwicklung:

  • Aufgaben, die bei normaler Programmierung der Compiler übernimmt, müssen plötzlich wieder vom Applikationsentwickler übernommen werden bzw. liegen brach und führen im Endeffekt zu einer Zunahme der Laufzeitfehler.
  • Die sinnvolle Modularisierung eines Programms wird durch in XML-Dateien versteckte Logik mehr als erschwert. Dies führt tendenziell zu zahlreichen Duplikationen in Quellcode und Konfigurationsdateien und damit unweigerlich zu schwer veränderbaren Systemen.
  • Fehlende Modularisierung bedeutet auch das Fehlen sinnvoller Modultests und erfordert dadurch erhöhten Aufwand für systemweite Funktionstests.
  • Ad-hoc-Änderungen an Konfigurationsdateien sind keineswegs harmlos, sondern verlangen – genau wie Änderungen am Quellcode – einen kontrollierten Qualitätssicherungsprozess inklusive Erstellung neuer Testfälle, Durchführung von Testläufen sowie Versions- und Konfigurationsmanagement. Damit lösen sich die zu Beginn des Artikels zitierten Vorteile Änderung durch den Anwender und zur Laufzeit zum Teil in Luft auf.

Ein weiteres Phänomen erschwert die Rückführung einer XML-infizierten Applikation in gesunde Bahnen: Ist die psychologische Schranke Keine Logik in XML erst einmal überwunden, dann kennen viele Entwickler oft kein Halten mehr: Immer mehr Funktionalität wandert aus ins XML-Reich, fast immer gefolgt von einem oder mehreren typischen Sünden:

  • DTDs werden gar nicht erst erstellt oder irgendwann nicht mehr angepasst.
  • Ad-hoc-Programmiersprachen ohne mathematische Grundlage und ohne geeignete Mittel zur Modularisierung werden erfunden.
  • Geeignete Werkzeuge zum Erstellen, Testen und Debuggen (!) dieser XML-basierten Programmiersprachen gibt es nur selten. Häufig wird deren Erstellung zwar angedacht, aber nie durchgeführt.

Die Opfer der XMLitis leben übrigens nicht im Verborgenen. Es gibt eine Reihe (sehr) prominenter Vertreter. So leidet bereits XSLT an seiner von Nicht-Programmierern und Gelegenheitsanwendern kaum zu beherrschenden Komplexität und an (immer noch) fehlenden bzw. nicht ausgereiften Werkzeugen zum intuitiven Testen und Debuggen von Transformationen. Nicht ohne Grund hat der ursprünglich so verlockende Ansatz, die Präsentationsebene von Webapplikationen ausschließlich mit XSLT zu implementieren, die serverseitigen Skripte (z.B. JSPs) nicht verdrängen können.

Ein weiteres populäres Framework, das nach Meinung des Autors die Grenzen einer sinnvollen XML-Konfiguration deutlich überschritten hat, ist Cocoon2 [1]. In den XML-Dateien von Cocoon-Anwendungen benötigt man jede Menge ungesicherter Abhängigkeiten, mehrere versteckte Programmiersprachen kommen zur Anwendung und es gibt weder eine vollständige DTD noch wirklich hilfreiche Entwicklungstools. Wer einmal versucht hat, die Sitemap einer durchschnittlich komplexen Cocoon-Anwendung zu entwanzen und dabei lediglich auf die Unterstützung durch den XML-Editor angewiesen war, weiß, wovon ich rede.

Obwohl es schwierig ist, eine harte Grenze zwischen der sinnvollen und der unsinnigen Verwendung von XML zu ziehen, ist die XML-Sünderliste groß und auch SOAP, EJBs sowie zahlreiche Open Source-Projekte gehören dazu. Ich möchte keineswegs behaupten, dass ein programmiererfahrenes und qualitätsbewusstes Team nicht auch mit XML-zentrierten Frameworks funktionsfähige und stabile Software bauen könnte. Ich glaube jedoch, dass das gleiche Team mit weniger XML, mehr richtiger Programmierung und einer ausgereiften Entwicklungsumgebung spürbar schneller wäre, ganz besonders in der Anpassung eines Systems. Sicherlich fehlschlagen wird der Versuch, die Wartung des Systems in die Hände der Anwender zu legen, d.h. denjenigen, die zwar für den Inhalt verantwortlich sind, aber keine ausreichende programmiertechnische Kompetenz besitzen.

Fazit und Tipps

Der gegenwärtig verbreitete Missbrauch von XML durch die übermäßige Verschiebung von Programmlogik aus Quellcode in XML-Dateien verstößt gegen Erkenntnisse und Errungenschaften der letzten 30 Jahre Softwareentwicklung:

  • Eine gute Modularisierung und Information Hiding (vgl. [2]) sind unumgänglich für die Programmierung wartbarer Software.
  • Programmiersprachen sollten auf mathematisch fundierten Prinzipien beruhen, um größtmögliche Orthogonalität zu ermöglichen und Anomalien zu vermeiden.
  • Mächtige IDEs beschleunigen die Entwicklung von Programmen maßgeblich und erhöhen deren Qualität.
  • Qualitativ hochwertige Software benötigt die Berücksichtigung der Testbarkeit eines Programms von Anfang an und auf allen Ebenen (vgl. [3]).

Die Angst ist daher nicht unbegründet, dass XML-zentrierte Softwareentwicklung für die Entstehung der unwartbaren Legacy-Applikationen von morgen mitverantwortlich gemacht werden kann. Aber wissen werden wir das natürlich erst übermorgen.

Die geschilderten Nachteile sind keineswegs die Schuld von XML, sondern treten immer auf, wenn Konfiguration zu Programmierung wird. Die Popularität von XML und die Verfügbarkeit entsprechender Werkzeuge hat lediglich die Umsetzung unguter Praktiken erleichtert. Und natürlich führt an der Konfiguration komplexer Anwendungen kein Weg vorbei. Ein paar Regeln helfen dabei, nicht in die oben genannten Fallen zu tappen:

  • Konfigurationsdateien sollten so einfach wie möglich sein und auf keinen Fall konditionale Logik enthalten. Eine entsprechende DTD oder XML Schema-Datei ist ein Muss für alle per Hand zu verändernden XML-Dateien. Vor jedem neuen XML-Tag sollte man sich fragen, ob das damit verknüpfte Verhalten notwendigerweise konfigurierbar sein muss. Eine zu generische Applikation ist ebenso unbrauchbar wie eine zu unflexible.
  • Referenzielle Abhängigkeiten in Konfigurationsdateien erfordern ein Werkzeug, das die Integrität der Referenzen innerhalb der Datei sicherstellt. Häufig wird dadurch XML zu einem bloßen Persistierungsmedium, das der normale Anwender nicht mehr zu Gesicht bekommt. Ein solches Werkzeug ist im Grunde nichts anderes als eine Entwicklungsumgebung für Konfigurationsdateien und hat nur dann eine Existenzberechtigung, wenn die Anwender nicht die Programmierer selbst sind.
  • Spätestens wenn in XML-Dateien konditionale Logik Verwendung findet, ist die Grenze zwischen Konfiguration und Programmierung überschritten. Dies kann dennoch unter bestimmten Umständen der richtige Weg sein, beispielsweise wenn die Spezifikation von Geschäftsregeln von der Entwicklung der eigentlichen Applikation unabhängig sein muss. In diesem Falle gelten dann aber auch die gleichen Regeln und Maßnahmen wie bei normaler professioneller Programmierung: Vermeidung von Redundanz, Versionskontrolle der Dateien und der Konfiguration sowie ausreichendes Testen des geänderten und des (hoffentlich) gleich gebliebenen Systemverhaltens. Zudem sollte zunächst die Verwendung einer existierenden Skriptsprache in Erwägung gezogen werden.

Trotz aller genannten Nachteilen gilt: XML ist auf Grund seiner Verbreitung und Standardisierung die erste Wahl, wenn es um echte Konfiguration geht. Um es mit einem der frühen Reviewer dieses Artikels zu sagen: Wenn XML die schlechteste Lösung mit Ausnahme all derer ist, die man vorher probiert hat, dann verwende ich lieber XML.

  • [1] xml.apache.org/cocoon/
  • [2] David L. Parnas, On the Criteria to be Used in Decomposing Systems into Modules, Communications of theACM, 15, 12, December 1972, pp. 1053 – 1058
  • [3] Robert Binder, Testing Object-Oriented Systems, Addison Wesley, 1999

Geschrieben von
Johannes Link
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.