home · Publikationen · Informatik-Klassiker · Dijkstra
Dijkstra: The Programming Task ... (Übersetzung: Pieper)

Den folgenden Vortrag hielt Dijkstra im Jahre 1969 (!) unter dem Originaltitel:

The Programming task Considered as an Intellectual Challenge

Der Vortrag fand damals sehr starke Beachtung und gilt seitdem als grundlegendes Dokument der 'Software-Krise', das bis heute nichts von seiner Aktualität eingebüßt hat. Dokumentarisch wichtig und bzgl. 'Zeitgeist' wie Verfasser aufschlußreich ist auch die Selbstdarstellung. Deshalb wurde der vollständige Wortlaut des Vortrags hier mit einiger Sorgfalt übersetzt. (Übersetzung: Friedrich Pieper, Fachhochschule Ulm, März 1982.)
Bekanntestes Zitat aus jenem Vortrag:

Program Testing can be used very effectively to show the presence of bugs, but never to show the absence.


Programmieren - eine intellektuelle Herausforderung

Edsger W. Dijkstra

Entgegen meiner ursprünglichen Absicht, mich ohne Gebrauch schriftlicher Notizen an Sie zu wenden, habe ich beschlossen, Ihnen den Text, so wie er jetzt vor mir liegt, Wort für Wort vorzutragen. Diese Prozedur führt oft zu einer weniger lebendigen, um nicht zu sagen: zu einer einschläfernden Vorführung; ich kann nur meine Hoffnung ausdrücken, daß die langweiligere Präsentation meines Vortrags durch dessen Inhalt ein wenig kompensiert werden möge. Wie dem auch sei, ich fühle mich verpflichtet, Sie zu warnen, daß ich jetzt vollen Gebrauch vom traditionellen Vorrecht des Redners machen werde, nämlich zu sagen, was er sagen will, und nicht zu reden, wie sein Publikum es vielleicht hören möchte. Ich bin mir der Tatsache wohl bewußt, daß ich als Konsequenz dieser Entscheidung womöglich nie wieder eingeladen werde, aber ob dies zum Nutzen oder Schaden unseres Berufs gereichen würde, habe ich nicht zu beurteilen.

Lassen Sie mich jetzt von der Erfahrung berichten, die zu meiner Entscheidung führte, mich in der eben angekündigten Weise an Sie zu wenden. Es war bei der zweiten vom NATO-Wissenschaftsrat veranstalteten 'Conference on Software Engineering' in der letzten Oktoberwoche 1969 in Rom. Wenn ich in der Tradition des british understatement aufgewachsen wäre, so würde ich wahrscheinlich jene Konferenz als 'not completely successful' beschrieben haben, so aber kann ich sie spontan nur als eine 'völlig verkorkste Veranstaltung' oder 'üble Bauernfängerei' charakterisieren.

In einer Hinsicht, fürchte ich, war es eine ehrliche Konferenz: ihr erbärmlicher Zustand mag eine wirkliche Widerspiegelung des augenblicklichen Standes von Software Engineering gewesen sein, so wie diese Kunst allgemein verstanden und praktiziert wird. Aber bevor ich versuche, mögliche Ursachen für diesen bedauernswerten Zustand aufzuspüren, lassen Sie mich erzählen, was am letzten Tag der Konferenz geschah. Danach können Sie dann selbst urteilen. Einige 10 oder 20 Leute waren Zeuge dieses Vorfalls.

Gegen 5 Uhr, kurz vor Konferenzschluß, sah ich einen Stapel Kopien vom Vortrag eines Teilnehmers, welcher mir sogleich ein Exemplar überreichte, als er Anzeichen von Interesse bei mir sah. Wie ich so neben ihm stand, begann ich seine Vorlesung durchzublättern und sah zu meinem Entsetzen doch nur, daß auch dieses Papier leider nicht mehr Bedeutsames enthielt als all das pompöse und oberflächliche Bla-Bla-Bla, dem wir die ganze Woche ausgesetzt gewesen waren. Mein Widerwillen gewann Überhand über mich, und ich ließ mich zu der Unhöflichkeit hinreißen, nachdem ich eineinhalb Seiten gelesen hatte, das Papier auf den Stapel zurückzulegen. Ohne weiteren Kommentar wandte ich mich von dem Stapel und seinem Autor ab. Ende des ersten Aktes dieses Dramas.

Als sich der Vorhang zum zweiten Akt hebt, ist es vier Stunden später, 9 Uhr abends. Schuldgefühle über mein unhöfliches Benehmen nagen an meinem Gewissen, und, um sie zu besänftigen, greife ich wieder zu dem Papier, um es noch einmal zu lesen. Diesmal beginne ich am Ende in der Absicht, es von hinten zu lesen. Da, schließlich, öffnen sich mir die Augen: das Papier ist sorgfältig bis zu einem Höhepunkt ausgearbeitet, und dieser letzte Satz ist ein superber Nonsens. Ich entdecke plötzlich, daß ich mich um ein Haar hatte zum Narren halten lassen: es war eine Parodie, eine Satire, ihr Autor ein Geistesverwandter. Voller Begeisterung zeige ich das Werk (das nur wenige gesehen hatten) den restlichen Teilnehmern, die noch im Hotel sind. Die Leute lesen es, schmunzelnd, kichernd oder in lautes Lachen ausbrechend, je nach Temperament. Aber einige lachen trotzdem nicht, weil sie nicht glauben, daß es eine Satire sei. In der folgenden Diskussion bleibt die Frage 'ist es ein Scherz oder ist es Ernst?' unbeantwortet, und die Gruppe bleibt in zwei Lager gespalten. Ende des zweiten Aktes.

Im dritten Akt kehrt der Autor, der während des zweiten Aktes nicht anwesend war, zurück und erklärt, daß er dieses Dokument nicht als Parodie geschrieben, sondern daß er die Konferenz so wiedergegeben, wie er sie gesehen habe. Ende des Dramas.

Meine Schlußfolgerung ist: Wenn eine Anzahl Leute, die auf ihrem Gebiet als bewandert gelten, sich untereinander nicht einigen können, ob ein offensichtlich professionelles Papier ernst gemeint ist oder nicht, dann ist dies ein Alarmsignal, dann ist der gegenwärtige Stand der Wissenschaft tatsächlich ein erbärmlicher Zustand.

Ein anderes untrügliches Symptom der Misere jener Konferenz war, daß einige Leute als Reaktion darauf ihre Flüge umbuchten und die Konferenz vorzeitig verließen. Ich selbst hätte das auch getan, wenn ich nicht noch einige private Verabredungen gehabt hätte, die es nicht erlaubten, meinen Zeitplan zu ändern.

Ich hoffe, daß diese Geschichte die Existenz der Misere hinreichend dokumentiert. Wenn ich es dabei beließe, so würde ich tatsächlich meine Sache sehr schlecht machen. Also wird mein nächster Schritt sein, die Symptome dieser Krankheit etwas näher zu erforschen, um zu sehen, ob wir schließlich einige ihrer Ursachen finden können.

Das offensichtlichste Symptom der Misere war, daß der größte Teil der Diskussion in die gewöhnliche, nichtsdestoweniger ziemlich fruchtlose Kontroverse zwischen 'Theorie' und 'Praxis' hineintrieb. Ich weiß, und natürlich wissen wir alle es, daß die Bewertungen 'theoretisch' und 'praktisch', jeweils unter Ausschluß der anderen, auf einen recht großen Prozentsatz der Computer-Leute anwendbar sind. Es gibt Leute, die es von sich weisen, irgend etwas mit praktischen Anwendungen zu tun zu haben. So gibt es die Geschichte von dem Mann, der unter Hardy seine Doktorarbeit vorbereitete, und sein Thema hatte etwas mit Fourier-Integralen zu tun; eines Tages war er so unvorsichtig, Hardy gegenüber zu erwähnen, daß einige Leute doch tatsächlich Fourier-Integrale anwenden, worauf Hardy antwortete, daß der Mann sich unter diesen Umständen wohl ein anderes Thema suchen müsse. Auf der anderen Seite des jämmerlichen Zauns finden wir jene, die die Hoffnung aufgegeben haben, jemals etwas Brauchbares in der Arbeit ihrer theoretisch ambitionierten Kollegen finden zu können; und natürlich ist das meiste davon sowieso völlig unnütz. Obwohl die Unterscheidung zwischen 'theoretisch' und 'praktisch' auf - sagen wir - 95 Prozent der Computerleute anwendbar sein mag, und diese Unterscheidung somit als statistisch signifikant betrachtet werden kann, nenne ich sie trotzdem fruchtlos, weil sie keinen Raum läßt, zu erklären, welchen Nutzen uns die restlichen 5 Prozent bringen können, jene Leute und jene Aktivitäten also, für die weder die Bewertung 'untheoretisch' noch die Bewertung 'unpraktisch' zutrifft.

Diese 5 Prozent zu ignorieren, ist eine Simplifizierung der Leute, die das Kind mit dem Bade ausschütten, und deshalb nenne ich die Unterscheidung gewöhnlich und fruchtlos.

Um den Zaun von einer anderen Seite anzugreifen: ich bin an einer Technischen Universität angestellt, und das Erziehungsprodukt meines Instituts erhält den Titel des 'Mathematischen Ingenieurs'. Ich habe oft beobachtet, daß viele Leute, besonders Amerikaner, zu lachen beginnen, wenn sie diesen Titel hören: er erscheint ihnen als begrifflicher Widerspruch, denn per Definition betrachten sie den Ingenieur als nützlich und den Mathematiker als nutzlos. Ich spreche von einer anderen kulturellen Tradition - sonst wäre ich nicht in diesem Beruf. Daraus folgt, daß ich äußerst mißtrauisch werde, wenn der Ingenieur eine 'von-der-Hand-in-den-Mund'-Arbeit rechtfertigt, indem er sich auf ein Naturgesetz beruft, das man mit 'schnell und unsauber' kennzeichnen kann, denn nach meiner Erfahrung und meinem Verständnis halte ich 'schnell und elegant' für eine viel passendere Kombination. Ich werde mißtrauisch, wenn der Mathematiker die Formalität der Darstellung als ein heiliges Ziel an und für sich versteht und nicht als ein profanes Mittel, welches nützlich sein mag (und gelegentlich sogar von vitaler Effektivität). Kurz: ein formales Verfahren verhält sich zu meiner Verständnisfähigkeit wie das Verhalten eines Anwalts in bezug auf meinen Gerechtigkeitssinn.

Von nun an werde ich es von mir weisen, jene gewöhnliche Unterscheidung noch mitzumachen; ich hoffe, daß ich meine Einstellung hinreichend klar gemacht und daß ich jedem sein gleiches Teil gegeben habe; und ich bin nicht ganz sicher, ob ich die 95 Prozent unter meine Freunde oder meine Feinde verteilt habe.

Dies war meine Einführung. Lassen Sie mich jetzt mit meinem eigentlichen Vortrag beginnen.

Der Angelpunkt meiner Betrachtungen ist das Thema 'Software-Fehler'. Vor einem oder zwei Jahren stand die weit verbreitete Existenz dieses bedauerlichen Phänomens außer Zweifel; soweit ich informiert bin, leben Software-Fehler noch immer so zäh wie eh und je, und ihre Wirkungen sind hinreichend alarmierend, um unsere Sorge und Aufmerksamkeit zu beanspruchen. Was aber ist es?

In Abhängigkeit von speziellen Fehler-Beispielen, die man auswählt, kann das Phänomen auf viele verschiedene Weisen beschrieben werden. Besonders häufig stellt es sich so dar: es beginnt mit einem spannenden Software-Projekt, aber mit fortschreitender Entwicklung werden immer mehr Grenzen übertreten, und was einmal als faszinierender Thriller begann, verwandelt sich langsam in ein Drama, das von einer ständig steigenden Zahl von Akteuren gespielt wird, von denen die meisten vielleicht ihre eigene Rolle kennen, sicher aber längst das Verständnis für die Bedeutung der Aufführung im ganzen verloren haben. Zum Schluß fällt der Vorhang, aber nur, weil es zu spät ist, noch weiter zu spielen, nicht, weil irgend etwas wirklich vollendet wurde; denn das so entstandene Software-Produkt steckt noch immer voller Fehler, und so wird es bleiben bis an das Ende seiner Tage. Es gibt andere Erscheinungsformen, ihnen allen aber ist gemeinsam, daß es sich als sehr, sehr schwer erweist, das ganze Programm mit akzeptabler Zuverlässigkeit zum Laufen zu bringen.

Wenn wir versuchen, die Gegenwart als die natürliche Folge der jüngsten Vergangenheit zu erklären, erhalten wir ein besseres Verständnis des Geschehens. In den letzten 10, 15 Jahren wuchs die Leistungsfähigkeit der allgemein verfügbaren Computer um etwa den Faktor 1000. Der Ehrgeiz der Gesellschaft, diese wunderschönen Wunderwerke einzusetzen, wuchs im gleichen Maße, und der arme Programmierer sieht in diesem Spannungsfeld zwischen Maschine und Zielvorgabe seine Aufgabe hinsichtlich Umfang, Anwendungsgebieten und Schwierigkeitsgrad explosionsartig anwachsen. Und der arme Programmierer hat das noch gar nicht begriffen. Wenn wir zurückblicken, müssen wir erkennen, daß die Schwierigkeit der angepackten Aufgaben vorab stets unerhört unterschätzt worden ist. Es wurden Vorausschätzungen aufgestellt, wie viele und wie leistungsfähige Computer installiert werden müßten, aber die Reaktion der Gesellschaft auf diese heranrollende Welle maschineller Intelligenz war nur der Ruf nach immer mehr Programmierern - statt nach fähigeren, deren größere Fähigkeiten aus einem besseren Verständnis des Wesentlichen am Programmierhandwerk resultieren.

Wie wenig sich die allgemeine Einstellung der Programmierer zu ihrer Arbeit in dieser Periode geändert hat, kann auf vielfältige Weise belegt werden. Während der NATO-Konferenz in Rom wurden eine Anzahl Testhilfen (debugging aids) diskutiert; ein Teilnehmer machte dann die Bemerkung, daß alle dargestellten Techniken seit 15 Jahren bekannt und in Gebrauch seien, und dieser Bemerkung widersprach niemand! Zum anderen wurde das Aufkommen höherer Programmiersprachen als ungeheurer Fortschritt bejubelt. Nun gut, aber reicht das? Vielleicht befähigt uns dieser Schritt, unseren Spielraum um den Faktor 10 zu erweitern, aber nicht um den Faktor 1000. In alten Tagen waren Programme mit 1000 oder 2000 Assembler-Befehlen ein Alptraum, aber inzwischen produzieren Programmierer in höheren Programmiersprachen - zugegebenermaßen - längere Programme mit genau demselben Grad an Unlesbarkeit und Unzuverlässigkeit. Die Rolle der alten Maschinencode-Tricks wurde an listige High-level- Tricks überliefert. Ich kann wirklich keinen Unterschied sehen. Es ist festzuhalten: Statt unseren Spielraum auch um den Faktor 1000 zu erweitern, versucht die heutige Programmierkunst ihre Probleme im Kern mit den alten Methoden zu lösen. Wenn wir die Sache verbessern wollen, so müssen wir uns deshalb ernsthaft vornehmen, den Einsatz unserer jetzt und künftig knappsten Mittel zu minimieren, nämlich unserer eigenen geistigen Mittel. Die brennende Frage lautet: Können wir zu einem besseren Verständnis des Wesens der Programmierkunst kommen, so daß kraft dieses besseren Verständnisses Programmieren um Größenordnungen leichter wird und daraufhin auch unsere Fähigkeit, zuverlässige Programme herzustellen, in der gleichen Größenordnung wächst?

Die Tatsache, daß Programmzuverlässigkeit zum wichtigsten Ziel wird, ist nicht nur in unserer täglichen Arbeit offensichtlich, es ist auch leicht zu sehen, warum. Ein sehr großes Programm ist notwendigerweise aus einer großen Zahl N von Einzelkomponenten zusammengesetzt. Aus der Tatsache, daß N groß ist, folgt, daß die einzelnen Programmkomponenten mit sehr großer Sorgfalt hergestellt werden müssen. Wenn für jede Komponente die Wahrscheinlichkeit, daß sie fehlerfrei sei, gleich p ist, so gilt für die Wahrscheinlichkeit P des ganzen Programms, fehlerfrei zu sein:

P < p N

und wenn wir wünschen, daß P wesentlich von Null verschieden sei, dann muß p sehr nahe an Eins liegen, weil N so groß ist.

Eine übliche Methode, ein Programm fehlerfrei zu machen, heißt 'debugging', und wenn die offensichtlichsten Fehler (bugs) gefunden und entfernt sind, versucht man, sein Zutrauen in die Richtigkeit des Programms zu erhöhen, indem man das Programmstück zahlreichen Testfällen unterzieht. Aus den Fehlern, die uns umgeben, können wir reichlich die Einsicht gewinnen, daß dieser Ansatz nicht angemessen ist. Man kann es glauben oder nicht, aber es wurde uns eingeredet (auf eben jener NATO-Konferenz in Rom), daß das, was wir jetzt wirklich brauchen, 'automatische Testfall-Generatoren' seien, mit deren Hilfe zu verifizierende Programmstücke noch ausführlicher getestet werden könnten. Aber wird das wirklich helfen? Ich glaube nicht.

Wenn man einem Mechanismus gegenübersteht - sei er Hardware oder Software -, so kann man sich fragen: 'wie kann ich mich davon überzeugen, ob er korrekt ist?' Solange wir den Mechanismus als black box ansehen, ist das einzige, was wir tun können, ihm jeden möglichen Input anzubieten und jedes Mal nachzuprüfen, ob er den richtigen Output produziert. Aber für die Art Mechanismen, die wir betrachten, kann dieses Verfahren unmöglich zur Debatte stehen, selbst für die einfachsten und schnellsten Mechanismen. An meiner Universität haben wir einen Rechner, der für eine Festpunkt-Multiplikation bedeutend weniger als 0.1 ms benötigt. Dennoch würde der Zeitbedarf für alle möglichen Multiplikationen 30000 Jahre überschreiten. Und dies gilt schon nur für einen Multiplizierer! Die Zahl der Fälle, die man in der Praxis wirklich ausprobieren kann, ist ein verschwindend kleiner Bruchteil der Gesamtzahl aller möglichen Fälle, und ganze Klassen kritischer Fälle können dabei übergangen werden. Die erste Moral dieser Geschichte ist:

    Programmtest ist sehr nützlich, um die ANWESENHEIT von Fehlern zu zeigen, nie aber die ABWESENHEIT.

Solange wir aber den Mechanismus als black box ansehen, ist Testen das einzige, was wir tun können. Daraus folgt, daß wir nicht fortfahren können, den Mechanismus als black box anzusehen, wir haben also seine interne Struktur in Betracht zu ziehen. Man studiert seine interne Struktur, und bei dieser Analyse überzeugt man sich davon, daß, wenn dieser und jener Fall läuft, auch 'alle anderen Fälle ebenso laufen müssen'. Das bedeutet, daß die interne Struktur ausgenutzt wird, um die Zahl noch notwendiger Testfälle zu reduzieren. Von allen übrigen Fällen (die überwältigende Mehrheit) versucht man sich durch Überlegung zu überzeugen, wobei das einzige Problem darin besteht, daß der notwendige Aufwand für die Überlegungen oft ausufert.

Diese Funktion der internen Struktur eines Mechanismus eröffnet einen neuen Weg, um das Zuverlässigkeitsproblem anzupacken. Nachdem wir eingesehen haben, daß das notwendige Zutrauen in die Richtigkeit eines Programms nur kraft seiner Struktur erreicht werden kann, daß der Grad, bis zu dem die Programmkorrektheit getrieben werden kann, nicht nur eine Funktion der externen Spezifikationen und des Programmverhaltens ist, sondern kritisch von der internen Struktur abhängt, können wir nun die Frage umkehren: 'Welche Formen der Programmstrukturierung, welche Elemente eines neuen Programmierstils und welche Regeln, um diese diszipliniert anzuwenden, können wir finden, um ein besseres Zutrauen in die Richtigkeit unseres Endprodukts zu erhalten?'

Anstatt daß ich versuche, Methoden zu ersinnen, um die Korrektheit beliebiger gegebener Programme zu begründen, möchte ich jetzt nach einer Unterklasse 'intellektuell machbarer Programme' Ausschau halten, die man verstehen kann und für die wir unseren Glauben an ihren unter allen Umständen richtigen Ablauf überprüfen können. Ziel dabei ist, die Zahl noch erforderlicher Testfälle zu reduzieren. Für Software sehe ich überhaupt keinen Grund, warum dieser Ansatz nicht so effektiv sein könnte, daß die Zahl der noch benötigten Testfälle eventuell auf Null reduziert, d.h. daß die Korrektheit a priori gezeigt werden könnte. So wie es jetzt ist, scheint mir das Pferd vom Schwanz aufgezäumt: statt nach vollkommeneren Testhilfen zu suchen, würde ich lieber versuchen, die viel produktiveren Fehler-Generatoren aufzuspüren und auszumerzen!

Kurz: Ich schlage vor, daß der Programmierer weiterhin verstehen sollte, was er tut, daß er sein wachsendes Produkt intellektuell fest im Griff behält. Ich habe die schlechte Erfahrung gemacht, daß dieser Vorschlag den Durchschnitts-Programmierer abstößt, der nämlich offensichtlich einen großen Teil seines beruflichen Selbstverständnisses daraus bezieht, daß er nicht ganz versteht, was er tut. In unserer schnellebigen Zeit ist eines unserer am wenigsten befriedigten psychologischen Bedürfnisse die Sehnsucht nach Schwarzer Magie, und offensichtlich kann der Computer dem professionellen Software-Ingenieur, der insgeheim von den gigantischen Risiken seiner waghalsigen Unverantwortlichkeit fasziniert ist, dieses Bedürfnis befriedigen. In seinen Frustrationen kann ich ihm nicht helfen.

Auf der Suche nach 'intellektuell beherrschbaren' Programmstrukturen sind wir unmittelbar mit der Frage konfrontiert: 'Wie behandeln wir komplexe Strukturen intellektuell? Welche geistigen Hilfsmittel haben wir, welche Denkmuster sind effektiv? Was sind die immanenten Schranken menschlichen Geistes, die wir besser respektieren sollten?' Ohne Kenntnisse und Erfahrungen wären solche Fragen sehr schwer zu beantworten, aber glücklicherweise beherbergt unsere Kultur eine intellektuelle Disziplin mit jahrhundertelanger Tradition, deren Hauptzweck es ist, komplizierten Sachverhalten, die intellektuell anders nicht beherrschbar wären, eine leistungsfähige Struktur zu unterlegen. Diese Disziplin heißt 'Mathematik'. Wenn wir die Existenz des eindrucksvollen Gebäudes der Mathematik als den experimentellen Nachweis der Aussage auffassen, daß für den menschlichen Geist die mathematische Methode tatsächlich der effektivste Weg ist, um Komplexität zu begreifen, dann haben wir keine Wahl mehr: wir sollten unser Programmiergebäude derart umgestalten, daß die mathematischen Methoden darin ebenso anwendbar werden, denn es gibt keinen anderen Weg. Nebenbei möchte ich darauf hinweisen, daß Sie meine Empfehlung nicht dadurch beiseiteschieben können, daß Sie sagen: 'Oh Gott, schon wieder ein Mathematiker, der uns die Wichtigkeit und universelle Anwendbarkeit seiner Sache verkaufen will!' denn zufällig bin ich nicht als Mathematiker ausgebildet; ich selbst halte mich für einen Programmierer. Aber ich habe mich in den letzten zwei Jahren ständig gefragt, welche Art Programme ich bevorzugt schreiben würde, wenn ich meine Abstraktionsfähigkeit genauso effektiv, wie das in der Mathematik üblich ist, einsetzen würde, um etwas zu verstehen. Im einzelnen war ich dabei, zu untersuchen, wie man seine Abstraktions- und Innovationsfähigkeit dazu verwenden kann, zu erreichen, daß die Zahl der Fälle, die man gedanklich zu unterscheiden hat, nur noch additiv statt multiplikativ zu verknüpfen sind. Ich möchte den nächsten Teil meines Vortrags dazu benutzen, Ihnen aus der Vogelperspektive einen Überblick über meine Ergebnisse zu geben.

    (1) Wenn man programmiert, sollte man ständig daran denken, daß, obwohl der Programmtext dasjenige ist, was der Programmierer als letztes aus den Händen gibt, der wirkliche Gegenstand seiner Tätigkeit die möglichen Berechnungen seines Programms sind, die Berechnungen nämlich, die er mit Hilfe seines Programms dem Computer aufträgt. Wenn wir salopp sagen, ein Programm sei in Ordnung, so meinen wir damit, daß die entsprechenden Berechnungen die Anforderungen erfüllen. Mit anderen Worten: Wir sollten die Tätigkeit des Programmierers nicht als 'Herstellung von Programmtexten' auffassen, sondern als 'Entwurf einer großen Klasse von Berechnungen'. Die Notwendigkeit, unseren Intellekt auf den zeitliche Ablauf zu lenken, während der statische Programmtext das letzte ist, worauf wir uns berufen können, die Notwendigkeit also, die Berechnungen zu verstehen, wie sie zeitlich durch den uns allein greifbaren Programmtext laufen, diese Notwendigkeit muß uns dringend veranlassen, die Regeln des sequentiellen Ablaufs, d.h. die Abbildung zwischen dem Fortgang im Programmtext und dem Ablauf der Berechnung so durchsichtig wie möglich zu machen. Als Folgerung daraus habe ich mich entschlossen, beim sequentiellen Programmieren auf die 'goto'-Anweisung zu verzichten und nur noch Bedingungs-, alternative und Wiederholungs-Klauseln, sowie den Unterprogramm-Mechanismus zu verwenden, um sequentiellen Ablauf zu steuern. Angesichts der Notwendigkeit, die konzeptionelle Lücke zwischen statischem Programmtext und dynamischen Berechnungen geistig zu überbrücken, erkannte ich die 'goto'-Anweisung als einen der Verursacher kombinatorischer Komplexität, nach denen ich suchte.

    (2) Eine andere, speziell die Ablaufsteuerung betreffende Schlußfolgerung lautet: Immer dann, wenn man ein Programmstück mit Hilfe einer Ablaufklausel konstruiert, trachte man danach, es abzukapseln und eine Beschreibung seines tatsächlichen Effekts zu finden, welche die Tatsache, daß dieses Programmstück eine Klausel enthält, verbirgt. Die Schreibweise

    IF x < 0 THEN x : = - x

    bedeutet: 'ersetze x durch seinen Absolutbetrag', und diese zweite Schreibweise ist gleichermaßen auf beide Fälle anwendbar. Und wenn Ihnen keine fertige Funktion (so wie der Absolutbetrag) zur Verfügung steht, mit deren Hilfe Sie den tatsächlichen Effekt einer Verbundanweisung beschreiben können, dann erfinden Sie diese Funktion und sichern Sie zu, daß sie in Ordnung ist und auch das tut, was Sie wünschen. Wenn Sie eine solche Funktion nicht finden können, dann seien Sie gewarnt, denn dann sind Sie dabei, Dinge durcheinander zu bringen! Um Ihnen ein analoges Beispiel zu nennen: Wenn Sie im Maschinencode programmieren, dann können Sie auf einen Additionsbefehl zurückgreifen, aber indem Sie diesen Befehl verwenden, kann es Ihnen gleichgültig sein, ob die Hardware als serieller oder Parallel-Addierer realisiert ist.

    (3) Die eben erwähnte Abkapselung der internen Struktur einer Programmkomponente ist der Spezialfall eines allgemeineren Prinzips, das wir in der Struktur jeder mathematischen Theorie wiederfinden: Wann immer ein Teil eines mathematischen Gedankens sich auf einen Lehrsatz bezieht, ist allein das von Interesse, was dieser Satz zusichert, und auf dieser Ebene ist es völlig unwichtig, wie dieser Satz (irgendwo anders) bewiesen wurde. Der Bezug auf einen Satz ist nicht eine Abkürzung eines bestimmten seiner möglichen Beweise, die Existenz des Satzes als solchem gestattet dem Anwender, alles über dessen Beweis zu vergessen. Wir können - und sollten - das gleiche Prinzip auf die Konstruktion von Programmen anwenden, wo immer es sich lohnt, für jede Programmkomponente zu unterscheiden, 'was sie tut' und 'wie sie abläuft'. Mit Ausnahme vielleicht der rekursiven Prozedur unterscheidet sich die Ebene, auf der für den Gebrauch einer Programmkomponente wichtig ist, was diese Komponente tut, stets von der Ebene, die sich damit befaßt, wie diese Komponente funktioniert. Diese zwei Seiten derselben Medaille sind aus der Beziehung zwischen Hauptprogramm und den von diesem aufgerufenen Unterprogrammen wohlbekannt; unsere Sicht auf diese Beziehung wird jedoch verschwommen, sobald wir den Aufruf nur als eine Abkürzung der aufgerufenen Anweisungsfolge ansehen und als Folge davon versuchen, den gesamten Ablauf auf ein und derselben homogenen semantischen Ebene zu verstehen: so hat man eine vorteilhafte Struktur gedanklich zerstört. Diese weit verbreitete Verwirrung - es tut mir leid, dies sagen zu müssen - scheint durch die Theorie der formalen Sprachen (inspiriert durch die Automatentheorie) verursacht zu sein und am Leben gehalten zu werden.

    (4) Während der vorige Punkt besagte, daß es unklug sei, die interne Struktur einer Programmkomponente auf der Ebene in Betracht zu ziehen, auf der es allein darauf ankommt, was diese Komponente tut, so soll hier jetzt dieselbe Aussage in bezug auf Datenstrukturen gemacht werden, die sich letztlich aus Variablen primitiverer Typen zusammensetzen. Die Ebenen, auf denen es allein auf die Zusammensetzung der möglichen Variablenwerte ankommt, sind sehr verschieden von der Ebene, die sich mit der Frage befaßt, wie diese zusammengesetzten Werte durch Zusammenfassung von Werten einfacher Typen dargestellt werden können. Eine der häufigsten Quellen von Programmfehlern scheint zu sein, daß eine Operation auf einer Variablen fahrlässig niedergeschrieben wurde derart, daß Komponenten einer speziellen Datendarstellung benutzt wurden. Um wieder ein ganz einfaches Beispiel anzuführen: auf einer Binärmaschine mag man versucht sein, die Frage 'ist diese ganze Zahl gerade?' durch die Frage zu ersetzen: 'ist ihr letztes signifikantes Bit null?'. Wenn man dann später mit diesem Programm von einer Binärmaschine auf eine andere geht, entdeckt man, daß diese Umsetzung der Fragestellung falsch wird, wenn negative Zahlen durch Einerkomplemente dargestellt werden.

    Das Unterprogramm repräsentiert die Datenverarbeitung, d.h. einerseits 'was tut es für mich?', andererseits 'wie tut es?'; die abstrakten Datentypen repräsentieren die Datendarstellung, d.h. einerseits 'welche Werte kann ein Typ annehmen?', andererseits 'wie werden diese Werte dargestellt?'. Die meisten heute üblichen Programmiersprachen unterstützen die Abstraktion der verarbeitenden Komponenten hinreichend gut durch das Prozedurkonzept, doch ihre Konzepte für die Abstraktion der darstellenden Komponenten, wenn sie überhaupt welche bieten, sind weniger befriedigend. Die Möglichkeit einer darstellenden Abstraktion, die sich im Programmtext widerspiegelt, scheint jedoch ebenso wichtig zu sein.

    (5) Ein Programm sollte nicht als ein Objekt für sich allein betrachtet werden, sondern stets als Element einer Klasse von Alternativprogrammen für dieselbe Aufgabe bzw. einer Klasse ähnlicher Programme für ähnliche Aufgaben. Wir wollen den Übergang von einem Element dieser Klasse zu einem anderen als Austausch eines oder mehrerer Programmbausteine durch einen oder mehrere alternative Bausteine auffassen. Dabei legen wir Wert darauf, daß der Korrektheitsnachweis für die vom Austausch unberührten Bausteine ebenso gültig bleibt wie deren Beziehung untereinander. Bei der Analyse letzterer Forderung bekam ich ein sehr viel besseres Verständnis dafür, wie verschiedene Abstraktionsebenen in einem großen Programm unterschieden werden können und wie die Unterscheidung dieser Abstraktionsebenen vorteilhaft ausgenutzt werden kann. Dadurch gelang es mir, das Ziel 'Programm-Modularität', dessen inhaltliche Beschreibung ja oft kaum mehr als eine leere Aussage ist, wesentlich zu spezifizieren. Derartige spezifische Folgerungen meiner Analyse sind beispielsweise:

      (5a) Ein guter Modul ist im allgemeinen mehr als eine Prozedur: in seiner allgemeinen Form reflektiert - oder 'dokumentiert', wie Sie wollen - er alle Konsequenzen einer lokal unabhängigen Design-Entscheidung, was im allgemeinen gewisse damit zusammenhängende Darstellungs- und Ablauf-Feinheiten einschließt.

      (5b) Die Angemessenheit kontextfreier Methoden zur Darstellung von Programmstrukturen scheint überschätzt worden zu sein.

Soweit im Überblick die Schlußfolgerungen, die ich bei dem Versuch gezogen habe, unser Programmiergeschäft zu einer Tätigkeit umzugestalten, die unseren geistigen Fähigkeiten und Grenzen besser angepaßt ist. Ich konnte Ihnen nur einen Überblick aus der Vogelschau geben; dennoch hoffe ich, daß Sie sich ein paar Blumen daraus pflücken, möglicherweise sogar eine Richtschnur ziehen konnten. Der Exkurs ist keineswegs vollständig, weitere Einsichten werden folgen, vielleicht als Frucht meiner eigenen Tätigkeit, aber hoffentlich werden sie ebenso auch von anderen gewonnen.

Obwohl ich mein Bestes getan habe, mich so klar wie möglich auszudrücken, fürchte ich, daß ich mich vergeblich bemüht habe, irgendjemanden unter meinen Zuhörern zu erreichen - wenn doch, so jemanden, der die Tätigkeit des Programmierens wegen seiner augenblicklichen Anlagenausstattung mit der Tätigkeit gleichsetzt, FORTRAN-Programme zu schreiben - ein Programmierwerkzeug, welches, als es vor etwa 15 Jahren entworfen wurde, wirklich einmal ein großer Schritt vorwärts war, heute aber als 'Low-Level-Language', als minderwertiges Codierwerkzeug betrachtet werden sollte. Wenn er an diesem Verständnis seiner Tätigkeit festhält, so wird er mich nicht verstehen, denn eines meiner Anliegen ist, klar zu machen, daß sein Programmierwerkzeug und seine Denkmuster, die es erzeugt hat, inzwischen hoffnungslos unangemessen geworden sind. Diese Furcht, mißverstanden zu werden, wird leider durch manche enttäuschende Erfahrung genährt. Als Lehrender ist es meine Aufgabe, Programmierern bei der Aufklärung ihres eigenen Denkens zu helfen. Dies ist oft eine sehr lohnende Tätigkeit, für beide Teile gleichermaßen erbaulich und lehrreich. Wenn ich aber von dem Modeprodukt spreche, welches gerade als 'reine FORTRAN-Maschine' bekannt wird, so gerate ich gewöhnlich in Verzweiflung darüber, wie sich ihnen vor meinen eigenen Augen unerwartete Weiten des Unverständnisses auftun. Es ist wohlbekannt, daß wir nicht automatisch aus jeder Erfahrung lernen, im Gegenteil, daß eher die schlechte Erfahrung die Klarheit unseres Urteils korrumpiert. Im Falle FORTRAN habe ich den Eindruck, daß sein intellektuell verheerender Einfluß noch nicht allgemein bemerkt wurde, daß zu wenig Leute erkannt haben, daß, je eher wir vergessen können, daß es FORTRAN jemals gab, um so besser, denn FORTRAN ist heute zu unangemessen, zu schwierig und daher zu teuer und zu riskant, um noch eingesetzt werden zu können.

Im Zusammenhang mit dem vorangegangenen Abschnitt über FORTRAN möchte ich gern mein Schweigen über COBOL erklären. Dies liegt daran, daß ich weder aus erster noch aus zweiter Hand Erfahrungen darüber habe, wie COBOL seine Benutzer beeinflußt. Aber der vorangegangene Abschnitt wurde von einem meiner Kollegen mißverstanden, der von einer kombinierten FORTRAN - COBOL - Anlage kam: er war schrecklich verwirrt und sagte: 'Warum den Einfluß von FORTRAN angreifen? Der Einfluß von COBOL ist um Größenordnungen schlimmer!' Ende der Erklärung.

"Meine verehrte Hörer und Hörerinnen" - dies mußte ich auf Deutsch sagen, weil mein Englisch dafür nicht reicht! - lassen Sie mich jetzt zu meinen abschließenden Folgerungen kommen. Die Computer begleiten uns nun seit 20 Jahren und in dieser Zeitspanne haben sie sich als äußerst flexibles und mächtiges Werkzeug erwiesen, dessen Gebrauch das Gesicht der Erde (und des Mondes ebenso!) zu verändern scheint. Angesichts ihres wachsenden Einflusses auf so gut wie jedem Gebiet, wo immer sie zu Hilfe gerufen werden, ist es meine wohlüberlegte Meinung, daß wir die Bedeutung der Computer für unsere Kultur solange unterschätzen, solange wir nur ihre Fähigkeiten als brauchbare Werkzeuge sehen. Auf die Dauer werden sie mehr sein als nur eine Kräuselwelle auf der Oberfläche unserer Kultur. Sie haben uns mehr gelehrt: Sie haben uns gelehrt, daß es wirklich sehr schwer ist, irgendeine nicht-triviale Leistung zu programmieren, und ich rechne mit einem noch sehr viel tiefer gehenden Einfluß, wenn die Möglichkeiten des Computers als ungeheure intellektuelle Herausforderung erkannt werden, wie sie in der Geschichte der Menschheit ohne Beispiel ist. Diese Meinung ist als sehr praktischer Hinweis gedacht, denn sie bedeutet, daß, solange die Tragweite dieser Herausforderung nicht erkannt wird, solange wir nicht zugeben, daß die vor uns liegenden Aufgaben so schwierig sind, daß auch die besten Werkzeuge zu ihrer Bewältigung kaum ausreichen werden, die Software-Fehler weiterhin unsere ständigen Begleiter sein werden. Wir mögen weiterhin denken, daß Programmieren nicht grundsätzlich schwierig ist, daß es von gewissenhaften Schwachköpfen besorgt werden kann, vorausgesetzt, es gibt genug von ihnen, aber dann werden wir uns weiterhin selbst betrügen, und niemand kann das besonders lange ungestraft tun.

Zum Schluß: aus meinen Worten mag mancher von Ihnen den Schluß gezogen haben, ich sei boshaft und verbittert. Lassen Sie mich Ihnen versichern: ich bin weder boshaft noch verbittert. Noch nicht. Obwohl ich zugeben muß, daß der größere Teil der Computer-Wissenschaft (oder vielleicht genauer: Programmier-Folklore) mir häufig wie eine Sammlung ungeprüfter Vorurteile vorkommt, die immer und immer wieder als Glaubensbekenntnis repetiert werden. Aber ich habe in meiner Einführung angekündigt, daß ich sagen würde, was ich zu sagen habe. Ich weiß, daß ich mit harter Sprache und starken Worten gesprochen habe - aber die Zeit vornehmer Zurückhaltung ist vorbei: die Lage ist zu ernst, um durch Höflichkeit verschleiert zu werden.

Der Saal ist jetzt offen zur Diskussion, und ich danke Ihnen für Ihre Geduld.

Eindhoven, Dez. 1969.