Sonntag, 16. Dezember 2007

Forensik

Wenn ein System mal nicht so läuft wie geplant und der Kunde meldet einen Fehler, so ist aus der Fehlermeldung alleine meist nicht klar, was passiert ist. Nun beginnt die mühsame Suche nach Spuren, die auf das eigentliche Problem hinweisen. Im Gegensatz zu Kriminalfällen hat der Entwickler es aber selbst in der Hand, für geeignete Spuren zu sorgen. Im Gegensatz zu Kriminellen ist es sogar in seinem eigenen Interesse, möglichst brauchbare Spuren zu hinterlassen.

Welche Möglichkeiten gibt es, Fehler zu analysieren?

Speicherauszug

Unix-Benutzer kennen sicher die dump-Dateien, die übrigbleiben, wenn ein Prozeß unerwartet abstürzt. Diese Speicherauszüge können mit dem Systemdebugger untersucht werden, als ob das Programm an dieser Stelle auf einen Unterbrechungspunkt gelaufen wäre. Wenn man hängende Prozesse untersuchen will, so kann man einen Speicherauszug auch durch das kill-Kommando provozieren.

Ein Speicherauszug beantwortet sofort die Frage, wo der Fehler aufgetreten ist. Nicht immer ist aber klar, wie das Programm da hingekommen ist.

Unter Java gibt es übrigens das Tool jstack mit dem ein Stackdump aller in einer JVM laufenden Threads erzeugt werden kann. So läßt sich feststellen, wo ein Programm hängt.

Logs

Ein seit je her beliebtes Verfahren, Probleme analysierbar zu machen, sind Logs. Hier werden Informationen über den Programmablauf laufend gespeichert, meist in Textdateien. In der Vergangenheit gab es immer wieder Probleme mit dem Loggen aus parallellaufenden Threads oder mit unvollständigen Ausgaben, da Puffer nicht rechtzeitig auf die Platte geschrieben wurden. Die Stabilität sollte heute mit der Verfügbarkeit von Bibliotheken wie log4j kein Problem mehr sein. Allerdings bedeutet umfangreiches Loggen immer noch ein Performanceproblem, insbesondere wenn Dateien benutzt werden. Eine Datei kann zu einer Zeit nur von einem Thread verwendet werden. Häufiges Schreiben blockiert die Festplatte für andere Nutzer. Wird seltener geschrieben, werden die letzten Einträge vor einem Absturz wahrscheinlich nicht mehr abgespeichert.

In vielen Projekten werden die Ausgaben ad hoc von den Entwicklern formuliert. Dies führt dazu, daß sie sehr uneinheitlich und unterschiedlicher Qualität sind. Eine automatische Auswertung ist in der Regel nicht möglich. Für einen effizienten Umgang mit Log-Informationen ist es notwendig, daß ein einheitliches Log-Format vorgegeben wird. Eine Umsetzung dieser Idee findet sich z.B. in den Common Base Events. Eclipse bietet Werkzeuge zum Umgang mit auf dieser Spezifikation erfolgenden Logs. Ein Ziel, das hier verfolgt wird, ist es, eine serviceübergreifende Ablaufverfolgung in einer SOA-Umgebung zu ermöglichen.

Häufig wird Information in einer Log-Meldung benötigt, die sowieso in der Datenbank verfügbar ist. Um den Log nicht übermäßig aufzublähen, empfiehlt es sich, nicht die Information auszugeben sondern nur eine Referenz auf die Datenbank (Schlüssel). Ein logische Schlußfolgerung aus dieser Vorgehensweise ist es, direkt in eine Datenbanktabelle zu loggen. Dann kann die vollständige Information durch eine geeignete Abfrage angezeigt werden. Die Datenbank ist das ideale Ziel von Logs. Das Schreiben ist in der Regel wesentlich effizienter und die Werkzeuge zur Auswertung sind auch vorhanden. Eine Strukturierung der Information ergibt sich aus dem Design der Logtabelle.

Datenhistorie

In Geschäftsanwendungen wird häufig eine Revisionssicherheit der Daten gefordert. Dies bedeutet, das Daten nicht geändert oder gar gelöscht werden, sondern immer neu eingefügt werden. Die aktuellen Daten werden am Einfügezeitpunkt erkannt, der mitgeführt werden muß. Dies erfordert zwar eine Menge zusätzlichen Speicherplatz, der heutzutage aber kaum etwas kostet, und etwas zusätzlichen Entwicklungsaufwand. Jedoch lassen sich so Vorgänge optimal rekonstruieren. Auch wenn es nicht direkt gefordert ist, sollte beim Systemdesign darüber nachgedacht werden, ob eine Historie zumindest für die wichtigsten Daten nicht machbar ist.



Sonntag, 9. Dezember 2007

Die ganze Welt nichts als Entwurfsmuster

Als Gamma et al. 1994 "Design Patterns" veröffentlichten, wurde nicht nur mir endlich klar, was objektorientierte Programmierung bedeuten kann. Vorher verstanden eigentlich nur die Wenigen, die das Glück hatten, mit Smalltalk programmieren zu dürfen, wie man ein Programm aus Objekten aufbaut. Gewiß, da war noch C++. Aber mal ehrlich, die am meisten genutzte Eigenschaft von C++ ist die Rückwärtskompatibilität zu C. Klassen werden meist nur zur groben Modularisierung gebraucht.


Jetzt nach 12 Jahren Java denken die meisten Softwarearchitekten nur noch in Mustern. Das ist an sich eine gute Sache. So wie der Unterschied zwischen einem Amateurschachspieler und einem Profi ist, daß der erste 64 Felder und (bis zu) 32 Figuren sieht und der letztere Positionen (Muster), so sieht der Programmieranfänger Klassen und der Erfahrene die Muster im Zusammenspiel der Klassen.


Leider gibt es aber auch Nebenwirkungen. Gamma erzählte einmal im Anschluß an einen Vortrag, daß er ein Erlebnis gehabt hätte, daß ihn daran zweifeln ließ, ob er mit seinem Buch Gutes geleistet hätte. Jemand hätte ihn um Rat gefragt. Die meisten der Muster hätte er in seinem Design schon untergebracht, aber für eines hätte er noch keine Verwendung gefunden.


In letzter Zeit hatte ich mit mehreren Systemen zu tun, bei denen die meisten Klassen schon in den Namen auf die verwendeten Patterns hinwiesen. So weit, so gut. Leider fehlten aber weitestgehend Klassennamen, die darauf hätten schließen können, wozu das System eigentlich gedacht war. Die einzige Ausnahme war die Datenzugriffsschicht, deren Klassen aber nur Datenbehälter waren ohne jede Funktion.


Hier läuft etwas grundsätzlich schief. Entwurfsmuster sind großartig, um Probleme zu lösen. Sie sind aber kein Selbstzweck. Ein Softwarearchitekt macht keinen besseren Job dadurch, daß er in seiner Architektur beweist, wieviele Entwurfsmuster er kennt.


Ein Problem mit derartigen Architekturen ist es, daß es sehr schwer ist, eine neue Anforderung unterzubringen. Die Anforderung wird wohl eher lauten, bei Kunden eine Überprüfung auf die Anwendbarkeit eines neuen Rabatts zu ermöglichen, als noch einen Listener um eine Factory zu ergänzen. Basiert die Architektur auf der Begriffswelt des Kunden, ist es sehr einfach die neue Anforderung zuzuordnen. Basiert die Architektur aber auf rein technischen Begriffen, geht dieser Zusammenhang verloren.


Die weitaus bessere Methode, zu einer guten Architektur zu kommen, ist es, mit der Fachlichkeit anzufangen und die fachlichen Anforderungen so zu implementieren, daß sie durch Modultests (JUnit o.ä) gesichert ist. Die technischen Anforderungen und Anbindungen können dann im zweiten Schritt dazuentworfen werden.


Natürlich leisten hierbei Entwurfsmuster gute Dienste. Aber die Anwendung eines Musters muß aus der fachlichen Anforderung resultieren.


Wie kommt es nun zu dieser Fehlentwicklung? Ich sehe hier mehrere Gründe:

  • Manchmal liegt es sicher an einem problematischen Rollenverständnis des Architekten. Dieser meint sich von dem "gemeinen" Programmierer dadurch abheben zu müssen, daß er die Abstraktion soweit treibt, bis keiner mehr folgen kann.
  • Häufiger liegt es jedoch daran, daß die fachlichen Anforderungen noch nicht hinreichend klar sind, wenn schon eine Architektur geliefert werden soll.
  • In manchen Organisationen wird bewußt getrennt zwischen fachlichen und technischen Experten. Dies kann nicht gut gehen. Wenn der Architekt keinen Umgang mehr mit dem Kunden hat, so wird er nie dessen Sprache lernen. Die so entstehenden Anwendungen werden nicht mehr die gewünschte Fachlichkeit wiederspiegeln sondern sich in technischer Beliebigkeit auflösen.

Sonntag, 2. Dezember 2007

Dilbert agil

Dilberts Boss hat mal wieder einen neuen Begriff aufgeschnappt. Er will jetzt "agile programming" einführen und erklärt das so, daß ab sofort ohne Planung und ohne Dokumentation entwickelt wird.

Während sonst meist klar ist, daß Dilberts Boss Unsinn redet, verwendet er hier leider weit verbreitete Vorurteile. Häufig höre ich Leute sagen, wenn ich auf agile Prozesse zu sprechen komme: Ach, daß ist wie in unserem letzten Projekt. Da lief auch alles chaotisch und am Ende haben wir dann doch was geliefert.

Leider haben die meisten noch nie das Agile Manifesto gelesen. Agil arbeiten heißt nicht chaotisch zu arbeiten, sondern das ohnehin von außen kommende Chaos beherrschen zu wollen. Das Vorgehen ist wesentlich zielgerichteter als herkömmlich Prozesse.

Planung

Einen Plan über die gesamte Projektlaufzeit zu erstellen ist Zeitverschwendung. Die Randbedingungen werden sich ändern. Kunde wie Auftragnehmer werden dazulernen. Alles vertraglich festschreiben zu wollen und dann den Kunden mit teuren Change Requests zu bestrafen, wenn sich das Festgeschriebene als unzureichend herausstellen sollte, ist nicht gerade förderlich für die Beziehung zum Kunden.

Stattdessen wird beim agilen Vorgehen nicht ein im Vorhinein festgeschriebenes Projekt betrachtet sondern ein kontinuierlicher Entsicklungsprozeß ohne festgelegtes Ende. Dafür werden in kurzen Abständen nutzbare Ergebnisse abgeliefert. Nach jeder Iteration ist der Kunde frei, das weitere Vorgehen in eine neue Richtung zu lenken.

Dadurch, daß im Abstand weniger Wochen Ergebnisse vorzeigbar sein müssen, ist der agile Prozeß wesentlich ambitionierter wie der herkömmliche, bei dem meist nur ein subjektiver Fortschritt von den Beteiligten abgefragt wird und die Entwicklung dann ewig bei einem Fertigstellungsgrad von 90 % stehenbleibt.

Dokumentation

In einem agilen Prozeß wird nichts um seiner selbst Willen getan. Alles dient nur dem Ziel, mit möglichst geringem Aufwand dem Kunden ein möglichst gutes Produkt zu liefern. Deshalb wird es abgelehnt, Dokumentation nach Gewicht herzustellen. es wird aber alle Dokumentation erstellt, die dem Entwicklungsprozeß dient und ganz sicher alle, die der Kunde zur Nutzung seines Produkts braucht (Benutzerhandbuch, Administrationshandbuch etc.). Dokumente, die nur in einer bestimmten Phase sinnvoll sind, werden auch nicht länger weitergepflegt als sie nützlich sind.

Metaprozeß

Der agile Prozeß ist nicht im Sinne von CMMI (zumindest wie es gewöhnlich verstanden wird) festgeschrieben. Es gibt keinen Prozeß der für alle Situationen paßt und genau angibt, was als nächstes zu tun ist. Stattdessen wird der Prozeß regelmäßig von dem gesamten Team reflektiert und angepaßt. Es existiert also ein Prozeß zur Optimierung des Entwicklungsprozesses. Auf dieser Metaebene ist der Prozeß genau definiert. Auf der Ebene der konkreten Handgriffe ist er flexibel.

Fazit

Dilberts Boss meint auch, daß Dilbert einen Film über Kaffee drehen wolle, als Dilbert erwähnt, daß er Javascript verwende. Sei kein solcher Ignorant. Wenn Dilberts Boss seine zielloses Vorgehen für agil hält, so ist agil mit Sicherheit alles andere als ziellos.

Sonntag, 25. November 2007

Teamfähigkeit von Organisationen

Wenn Mitarbeiter gesucht werden findet man gewöhnlich die Formulierung "teamfähig und belastbar". Schaut man sich aber dann die Arbeitsbedingungen an, so findet man häufig Organisationen, die alles andere wollen als Teams. Was meine ich damit?

Projektmanagement Tools

Das Projektmanagement sieht es als Aufgabe an, das Projekt in kleine Tasks zu zerlegen, diese in Pertcharts einzutragen und den "Team"-mitgliedern zuzuweisen. Danach wird regelmäßig der Fortschritt bei jedem Einzelnen abgefragt. Durch diese von oben kommende Arbeitsverteilung wird das Team aber zerstört. Teamarbeit sollte bedeuten, daß die Teammitglieder zusammenarbeiten und die Fähigkeiten des Einzelnen dem Team zugute kommt. Stellt aber ein Teammitglied fest, daß es das Ergebnis des Teams deutlich verbessern könnte, wenn es eine Aufgabe leistet, die ihm nicht zugeordnet ist, wird sich das Teammitglied dies dreimal überlegen, denn es hat dann einen geringeren Fortschritt bei der ihm zugewiesenen Aufgabe und schwierige Diskussionen mit dem Projektmanager zu erwarten. Läßt der Projektmanager aber solches Handeln zu, so verliert sein Plan rasch an Wert. Fazit: wenn Projektpläne so benutzt werden, daß die Tasks einzelnen Personen zugewiesen werden, hat man eine Gruppe von Einzelkämpfern.

Individuelle Zielvereinbarungen

Heutzutage sind variable Gehaltsbestandteile sehr beliebt. Häufig sind sie mit individuellen Zielvereinbarungen verbunden. Diese stehen leicht in Konflikt mit den Projektzielen. Wenn ich neben der Projektarbeit noch über das Jahr z.B. ein Abteilungs-Wiki aufbauen soll, muß ich mir überlegen, ob ich an dem Wiki arbeite und mein Gehalt verbessere oder zum Projektfortschritt beitrage. Besonders schwierig wird es, wenn einzelne Teammitglieder Ziele haben, die sich widersprechen oder in Konkurrenz stehen. Will ich nun die Kundenzufriedenheit steigern oder den mittleren Stundensatz durch Einbindung von Offshorekräften senken? Hier kann es bis zur (eventuell unbewußten) Sabotage kommen.

Mitarbeiterbeurteilung

Bei einem Gespräch mit Kollegen fiel neulich die Bemerkung, daß auch der Wunsch besteht, die persönliche Leistung sichtbar zu machen und so ein besseres Gehalt zu erzielen. Daher kamen Vorbehalte gegen Teamarbeit. Dies finde ich besonders traurig. Die Vorgesetzten hoffen darauf, daß die Konkurrenz untereinander zu höherer Leistung anspornt. In Wirklichkeit entsteht aber nur Reibung. Auch wenn das gesamte Team einheitlich beurteilt wird, werden gute Mitarbeiter in der Regel nach einiger Zeit besser dastehen als weniger geeignete. Ein guter Mitarbeiter muß nur dafür sorgen, daß sein jeweiliges Team erfolgreich ist. Allerdings könnten dann auch ganz andere Eigenschaften zu höherem Einkommen führen wie bei dem Einzelkämpfermodell. Eine Person, die in der Lage ist, die Kommunikation in einem Team positiv zu fördern, so daß die Teammitglieder besser arbeiten können, könnte auch bei mäßigen technischen Fähigkeiten sehr gut darstehen, wenn ihr das immer wieder gelingt.

Fazit

Bei vielen althergebrachten Organisationen bedeutet Teamfähigkeit, nicht gegen die von oben vorgegeben Spielregeln aufzumucken, und Belastbarkeit ist notwendig, die dadurch entstehenden Reibungsverluste aufzufangen.

Solche Organisationen sind mittelfristig dem Untergang geweiht. Es gibt heute gut ausgearbeitete Modelle zur Projektarbeit im Team, die eine wesentlich höhere Effizienz erreichen. Diese findet man unter dem Stichwort agil (z.B. Scrum). Firmen, die bei den alten Strukturen bleiben, werden sich in der gleichen Situation finden, wie einige große Firmen der Automobilindustrie, die zu spät erkannt haben, wie die japanischen Konkurrenten die Effizienz und Qualität haben steigern können.

Sonntag, 18. November 2007

Wie man aus einer Ameise (k)einen Elephanten macht

Der Hauptgrund, der meist für die Verwendung von typisierten Sprachen genannt wird, ist, daß Typfehler schon zur Compilezeit abgefangen werden. Wenn man versucht, die Methode trompeten bei einer Ameise aufruft, sollte sich der Compiler beschweren. Bei älteren Java- und C#-Versonen ist das aber spätestens dann eine Illusion, wenn das Tier zwischenzeitlich in einer Collection war. Dort wir alles zum Object, wenn es aus der Collection herausgeholt wird, muß es dann wieder zum Elephanten gecastet wird. Wenn es dann doch eine Ameise war, gibt es einen Laufzeitfehler.

Mit Java 5 und C# 2.0 kamen nun die Generics. Wenn wir nun eine Collection Tierkäfig haben, dann können wir jetzt spezielle Käfige herstellen für Ameisen (Tierkäfig<Ameise>) oder Elephanten (Tierkäfig<Elephant>). Jetzt wird zur Compilezeit sichergestellt, daß in jeden Käfig die passenden Tiere kommen und ein Cast ist beim herausholen nicht mehr notwendig.

So, jetzt ist ja alles gut. Wir verwenden überall eifrig Generics. Auch junge, aufstrebende Architekten entwerfen ambitionierte Frameworks basierend auf Generics. Damit brauchen sie dann aber auch Methoden, die als Argument beliebige Tierkäfige verarbeiten können. Das klingt nun zuerst recht logisch, führt aber bald zu Problemen. Natürlich kann man einen Elephantenkäfig nicht einem Ameisenkäfig zuweisen. Aber auch ein Ameisenkäfig kann normalerweise nicht als Tierkäfig verwendet werden, denn dann könnte man aus dem Ameisenkäfig auch einen Elephanten holen - das gleiche Problem, das wir vor den Generics hatten.

Wenn wir Java verwenden, haben wir eine Lösung. Java kennt nämlich die Begriffe Covarianz und Contravarianz. Eine Zuweisung eines Ameisenkäfigs an einen Tierkäfig kann nämlich ungefährlich sein, wenn wir sicherstellen, daß wir nur allgemeine Tiermethoden aufrufen und keine Tiere hineintun, da wir nicht mehr die Art sicherstellen können. Das heißt Covarianz. In Java sieht das so aus:

String gibTierNamen(TierKäfig<? extends ITier> käfig)

In einen Ameisenkäfig können wir auch Waldameisen hineingeben. Das ist dann Contravarianz. In Java schreibt man dann statt extends super.

C# kennt (bislang, Version 3.0) noch keine keine Co- und Contravarianz. Wenn dann die Entwickler, die das Framework unseres jungen Architekten umsetzen, anfangen, auf Reflection zurückzugreifen, sollte der Architekt ins grübeln kommen:

public static String gibTiernamen(object käfig)

{

MethodInfo mInfo = käfig.GetType().GetMethod("Freilassen");

ITier tier = (ITier)mInfo.Invoke(käfig, null);

return tier.Name;

}

Wenn wir so programmieren, sollten wir doch lieber gleich eine dynamische Programmiersprache wie Python oder Ruby verwenden. Die können dies viel besser.

Wie kann sich nun in C# derzeit retten? Nun, in diesen allgemeinen Methoden wird auch nur allgemein auf das Tier zugegriffen und keine artspezifische Information verwendet. Man definiert einfach ein Interface, daß alle Methoden der Tierkäfigs enthält, die nicht von der Art abhängen. Dieses ist nicht generisch. So kann man jetzt dieses Interface in der allgemeinen Methode verwenden:

public static String gibTierNamen(ITierkäfig käfig)

{

return käfig.Tierart.Name;

}

Sonntag, 4. November 2007

Paradigmenverschiebung bei Programmiersprachen

Es hat sich schon seit einigen Jahren abgezeichnet, aber jetzt mehren sich die Zeichen, daß es beginnt: Die funktionale Programmierung verläßt ihre Nische und wird zum wichtigen Bestandteil zukünftiger Programmiersprachen.

Microsoft plant offensichtlich F# zur gleichberechtigten Sprache neben C# zu machen (Somasegar's WebLog). Dies wäre nur konsequent, da die meisten Neuigkeiten von C# 3.0 - insbesondere LINQ - zuerst in F# ausprobiert und dann in C# übertragen wurden. Wobei C# mit seinen Delegates bereits ein funktionales Element enthält.

Martin Oderskys Scala, eine Sprache für die JVM (alternativ auch CLR), die elegant den objektorientierten und funktionalen Ansatz verbindet, zieht zunehmend Entwickler an, wie z.B. das Lift Web-Framework zeigt.

Die Begeisterung für dynamische Sprachen (Python, Ruby et altera), kommt zum Teil auch daher, daß hier Funktionen vollwertige Elemente sind und die Eleganz funktionaler Programmierung erreicht werden kann. Allerdings wird es Guido van Rossum offensichtlich langsam unheimlich, daß sein Python langsam für viele Nutzer zu einem Lisp ohne Klammern wird (siehe).

Woher nun dieses Interesse? Funktionale Programmierung ist jetzt 50 Jahre alt, wenn man LISP als erste funktionale Sprache ansieht. Im Laufe der Zeit wurden eine Reihe von funktionalen Sprachen entwickelt wie z.B. die ML-Familie, deren jüngstes Kind F# ist, und Haskell. Diese Sprachen führten bislang aber ihr Nischendasein im akademischen Bereich und in der "künstlichen Intelligenz". Sie sind abstrakter und mathematisch besser fundiert als andere Ansätze. Lange Zeit hatten sie den Ruf, weniger effizient zu sein wie imperative und objektorientierte Sprachen.

Die Verwendung in KI-Programmen zeigt, daß die Stärke des funktionalen Ansatzes in der Programmierung von komplexen Algorithmen liegt. Wenn es nur darum geht, Speicherbereiche zu verschieben, geht das auch mit Sprache wie C. Die Anforderungen an Computerprogramme steigen immer weiter und die Entwicklungszeit ist häufig wichtiger als das letzte an Effizienz herauszuholen. Daher lohnt es sich, Sprachen zu bevorzugen, die elegante Formulierungen der Probleme erlauben.

Das funktionale Programmierung erst jetzt ins allgemeine Bewußtsein gerät, hat drei Gründe:

  • Sie verbrauchen mehr Speicher, da sie Daten nicht ändern sondern neu erzeugen. Speicher ist aber jetzt billig.
  • Die Compiler sind besser geworden, so daß effektiverer Code erzeugt wird.
  • Im Gegensatz zu C-Programmen braucht es mehr Erfahrung abzuschätzen, welchen Aufwand ein Stück Code bei der Ausführung braucht. Das ist heute eher akzeptiert als vor zehn Jahren, da dies z.B. auch bei Java nicht immer einfach ist und wir es gewohnt sind mit komplexen Applicationservern und Frameworks umzugehen, deren Innenleben weitgehend unbekannt ist.

Es ist damit zu rechnen, daß sich die gängigen Sprachen (Java und C#) in den nächsten Jahren zu gemischt funktional-objektorientierten Sprachen entwickeln werden. Zum anderen ist es heute leichter, ganz neue Sprachen einzusetzen, da der Microsoftansatz eine CLR für beliebig viele Sprachen zu haben, sich auch in der Welt der JVM durchgesetzt hat und das "J" in JVM nur noch den Ursprung in Java angibt. Interoperabilität zwischen Sprachen ist kein Problem mehr.

Sonntag, 28. Oktober 2007

Softwareentwicklung: Der Weg von mündlicher zu formaler Sprache

Am Anfang steht das Wort. Jemand beschreibt, was er von der Software erwartet. Dies geschieht in deutsch, englisch oder sonst einer menschlichen Sprache. Daher ist es unvollständig, in sich widersprüchlich und mißverständlich. Am Ende steht ein Softwareprogramm, daß sich nach präzisen Regeln verhält.

Man kann den Entwicklungsprozeß also als zunehmende Präzisierung und Formalisierung der Anforderung begreifen.

Jeder Schritt auf diesem Weg führt zu einer Einengung der Auslegungsmöglichkeiten der Formulierung der Anforderung. In jedem Schritt sind somit auch fachliche Entscheidungen zu treffen.

Damit werden viele klassische Ansätze fragwürdig, die vorsehen, daß das fachliche Verständnis in einer Analysephase festgehalten wird und anschließend in einer Designphase nur die technische Umsetzung behandelt wird. Diese Vorgänge verschwimmen alle zusammen mit der Programmierung.

Eine vollständige, überprüfbare Darstellung der Software kann nur in einer formalen Sprache erfolgen. Dies war z.B. auch der Ansatz von UML/MDA. Jedoch kann es auch eine DSL oder ein hinreichend gut strukturiertes Programm sein. Wenn die Interfaces der Komponenten so gestaltet sind, daß sie die fachliche Intention offenbaren, werden Analyse, Design und Codierung eins.

Wenn eine vollständige, widerspruchsfreie Beschreibung vorliegt, so läßt sie sich zumindest prinzipiell Compilieren. Es gibt heute genügend Werkzeuge, um dies auch wirklich zu tun. Menschliche Arbeit ist hier meist Verschwendung von Ressourcen, so billig sie auch zu bekommen ist.

Dienstag, 23. Oktober 2007

Lob des Wasserfallmodells

Das Wasserfallmodell der Softwareerstellung ist einfach nicht totzukriegen. Zwar scheitern immer wieder Projekte, die dieses Modell verwenden spektakulär, aber das liegt sicher nur an dem Unvermögen der Beteiligten.

Es lebt sich halt so schön mit dem Wasserfallmodell. Da schreibt man erst in aller Ruhe seine Lasten- und Pflichtenhefte, macht großartige Projektpläne und verteilt Aufgaben (heute möglichst international). Dann haben alle erst mal monatelang ihre Ruhe. Es gibt nichts, was die Theorie eines stetigen Projektfortschritts stört.

Wenn dann der vorgesehene Ablieferungstermin naht, werden alle etwas nervös. Das ist dann die Gelegenheit Karriere zu machen. Das Projekt hat jetzt die volle Aufmerksamkeit des Managements. Man kann in den Krisensitzungen wunderschön Kontakte mit wichtigen Leuten knüpfen.

Wenn es dann schief geht, müssen bei Auftraggeber und -nehmer geeignete Sündenböcke gesucht werden. Alle anderen haben doch alles richtig gemacht.

Nicht selten geht es im letzten Moment dann doch noch gut. Dann nämlich, wenn das Management in seinen Krisensitzungen das Entwicklungsteam ganz vergessen hat. Dieses organisiert sich nun selbst, da keiner mehr Zeit für die alten Regeln hat. Wenn man nachträglich das jetzt verwendete Vorgehen betrachtet, erkennt man meist große Ähnlichkeiten mit Vorgehensweisen, die in agilen Projekten geplant benutzt werden. Aber psst, nicht weitersagen.

Sonntag, 21. Oktober 2007

Sprache, menschliche

Bei der Erstellung von Software hat man es nicht nur mit Computersprachen zu tun sondern auch mit der menschlichen Sprache. Dabei stellt sich häufig heraus, daß die menschliche Sprache das größere Problem ist. So ist die menschliche Sprache nicht konstant, sondern wird ständig erweitert und geändert. Im Prinzip benutzt jede Gruppe von Menschen eine unterschiedliche Sprachen. Dies merkt man zum Beispiel daran, daß man sehr schnell in der Kantine erkennt, in welchem Projekt ein Kollege arbeitet, allein an der Sprache die er in seiner Unterhaltung mit Kollegen verwendet.

Zudem ist die menschliche Sprache nicht kontextfrei. So hat das Wort "Decke" z. B. für einen Anstreicher eine andere Bedeutung als für einen Bettenverkäufer.

Wenn man sich in ein neues Thema einarbeitet, ist der erste Schritt, die Sprache zu verstehen, mit der die Fachleute über dieses Thema reden. Damit hat man dann auch die gedankliche Struktur verstanden, da wir in der Regel sprachlich denken.

Es ist daher enorm wichtig, daß in einem Softwareprojekt alle Beteiligten eine gemeinsame Sprache finden. Daher sollte auch eines der ersten Dokumente, das in der neuen Projekten angelegt wird, das Glossar sein. Darin wird jedes Projekt spezifische Wort definiert.

Ohne ein solches Glossar ist es z. B. auch fast unmöglich, Projektdokumentation in eine andere Sprache zu übersetzen, da dem Übersetzer in der Regel der Kontext fehlt. Hierbei muß dann das Glossar zuerst von Fachleuten übersetzt werden, ehe die restliche Dokumentation auch allgemeinen Übersetzern übergeben werden kann.

Daß die Kontextinformation enorm wichtig ist, sieht man sehr leicht an den Grenzen der automatischen Übersetzung. Genau das ist nämlich der Grund, weshalb die Übersetzungen zum Beispiel in Babelfish häufig eher spaßig als nützlich sind.

Donnerstag, 18. Oktober 2007

Softwareentwicklung als kreativer Prozeß

Ziel der Softwareentwicklung sollte es immer sein, etwas Neues zu schaffen. Dies muß nicht eine noch nie dagewesene Idee sein, sondern kann auch die Variation von etwas bekannten sein. Wenn aber etwas zum zweiten Mal gemacht wird, ohne neue Aspekte hinzuzufügen, so gibt es prinzipiell keinen Grund, nicht das Original zu benutzen und alle weitere Arbeit ist nur Zeit- (und Geld-)verschwendung.

Gleichzeitig ist die Softwareentwicklung auch eine sehr strikte Anwendung von Logik. In dieser Dualität zwischen Kunst und Mathematik ist die Softwareentwicklung der Architektur sehr ähnlich. Es ist daher kein Zufall, daß die für die Softwareentwicklung sehr fruchtbare Diskussion über Entwurfsmuster ihren Ausgang in einem Buch über Architekturmuster nahm.

Im Unterschied zur Architektur ist aber ein komplett aufgearbeiteter Entwurf bereits praktisch identisch mit dem Ergebnis. Wenn jedes Detail einer Software ausgearbeitet ist, muß ich nur noch kompilieren. Es werden nicht mehr große Mengen von Handwerkern bebraucht, die die Umsetzung von der Pläne in ein physisch existentes Gebäude durchführen.

In den Frühzeiten der Informatik war das mal anders. Damals war es recht mühselig, Lochkarten anzufertigen, diese zum Operator zu bringen, den Ausdruck des Ergebnisses abzuholen und dann die Lochkarten zu korrigieren. Damals wurden die Programme häufig von Experten auf Papier entworfen und dann von sogenannten Kodierern in verarbeitbare Lockkartenstapel umgesetzt. Bei den heutigen Entwicklungsumgebungen aber ist dieser Zwischenschritt überflüssig.

Wenn doch noch bei der Entwicklung langweilige Routineaufgaben anfallen, die man Hilfskräften übertragen könnte, machen wir etwas falsch. Dann ist entweder der Entwurf redundant oder wir benutzen die uns zur Verfügung stehenden Werkzeuge nicht richtig.