Follow my new blog

Sonntag, 17. August 2008

Technologiedämmerung - Nur konstantes Beobachten hilft

Welche Technologien bringen eigentlich 'was? Wann lohnt es sich aufzuspringen? Diese und ähnliche Fragen höre ich immer wieder von Konferenzteilnehmern oder bei Beratungen. Sie sind Zeichen der Ratlosigkeit angesichts einer kaum mehr überschaubaren Zahl von technologischen Optionen. Wer eine Fachzeitschrift wie die dotnetpro liest, der hat deshalb Anspruch, dass sie ihn darüber informiere, was lohnt und was nicht lohnt.

Die Empfindung bei vielen ist jedoch, dass Fachzeitschriften solchem Informationsanspruch nicht gerecht werden. Sie verkünden nicht immer "die Wahrheit", d.h. sie scheinen nicht neutral gegenüber Technologien. Sie sagen nicht offen (oder nur viel zu selten), welche gut und welche schlecht sind.

Beispiel CORBA-Berichterstattung

image Anlässlich eines Artikels mit dem Titel "The rise and fall of CORBA" sind mir die Eingangsfragen nun wieder eingefallen. Sicherlich haben in den letzten 10-12 Jahren viele Entwickler überlegt, "Soll unser Projekt auf CORBA setzen oder nicht?" Sie mögen sich dann den einschlägigen Fachzeitschriften wie dem OBJEKTspektrum oder JAVAspektrum um Entscheidungshilfe zugewandt haben. Andere Quellen mögen natürlich auch konsultiert worden sein. Aber nun... nun ist CORBA tot. Doch nicht nur das! CORBA hat auch - wie der Artikel resümiert - nie wirklich gut funktioniert. CORBA war zum Scheitern verurteilt.

Soviel weiß zumindest der geschichtliche Rückblick. Wie ist es aber heute? Welche Technologien und Ansätze sind das CORBA von morgen? Das würden alle Softwareentwickler gern so früh wie möglich wissen, um nicht ihre kostbare Zeit an Verlierer zu verschwenden.

Ich habe mir deshalb mal die Berichterstattung zum Thema CORBA im OBJEKT- und JAVA-spektrum angeschaut. Ganz grob nur, aber trotzdem mit einem interessanten Ergebnis, wie ich finde. Die folgende Grafik zeigt die Häufigkeit der Erwähnung von CORBA in Artikeln im Allgemeinen und den Artikelüberschriften im Besonderen über die Jahre.

image

In den Jahren 1994 bis 2000 nimmt die Berichterstattung zu. CORBA ist ein heißes Thema. Auf dem Gipfel Relevanz enthält fast jedes Heft einen CORBA-Artikel und in jedem zweiten ist CORBA auch noch im Titel erwähnt.

Doch dann verliert CORBA abrupt an Präsenz. Im Verlauf von 3 Jahren sinkt die Berichterstattung auf Null - und erholt sich danach auch nicht wieder. Insbesondere bleibt CORBA aus den Artikeltiteln verschwunden.

Was war geschehen? In den Jahren 2000 bis 2003  hatten SOAP-basierte Web Services und EJB 2.0 die Aufmerksamkeit der Berichterstattung. Sie haben der CORBA-Initiative das Wasser abgegraben und schließlich zu dem Urteil geführt, dass CORBA seine Versprechen nicht eingelöst hat und wohl auch nicht einlösen konnte.

interpretation der Berichterstattung

Jetzt zur Frage, wie ein unbefangener Leser dieser Zeitschriften die CORBA-Entwicklung hätte einschätzen sollen. Was boten OBJEKT- und JAVAspektrum für Hinweise?

Mein Eindruck ist, dass es keine wirklich kritische Berichterstattung gibt. Das hat nichts mit diesen beiden Zeitschriften zu tun, sondern ist endemisch. Kritische Berichte mit einer Überschrift wie "Die Nachteilte von Technologie X" sind anscheinend unattraktiv. So, wie die Bild-Zeitung vor allem über Negatives berichtet, so berichten Fachmagazine vor allem über Positives. Skandale, Gewalt, Sex verkaufen sich beim Massenpublikum der Boulevardpresse. Verheißungen, Erfolge, Hype verkaufen sich beim Fachpublikum. (Und ab und an auch Negatives wie Microsoft-Bashing.)

Kritik ist kaum explizit vorhanden. Implizit findet sie allerdings statt: Wie die Grafik zeigt, ist ab 2001 CORBA deutlich weniger ein Thema. Das ist ein erster Hinweis auf Nachteile bzw. ungenügende Vorteile. Ein noch deutlicherer Hinweis scheint jedoch die fast völlige Abwesenheit des Begriffs in den Artikelüberschriften. Wo CORBA nicht mehr in der Überschrift steht, da ist wenig oder gar kein CORBA drin. Bis 2000 fand CORBA in ca. 50% der Artikel den Weg in die Überschrift, nach 2000 im Mittel nur noch bei 10%.

In der Berichterstattung ist die Abwesenheit von Lob bzw. titelgebender Erwähnung die deutlichste Form von Kritik, wie es scheint.

Schlüsse

Natürlich wäre eine breitere Auswertung der Berichterstattung über CORBA und auch andere erfolgreiche wie erfolglose Technologien wünschenswert. Als anekdotische Analyse mit Motivationscharakter mag dieser Versuch dennoch aber gewissen Wert haben. So ziehe ich denn einmal folgende Schlüsse aus den Zahlen:

  • Technologiedämmerungen werden nicht wirklich thematisiert, sondern durch die Morgenröte der Alternativen überstrahlt.
  • Wer also etwas über den Wert von Technologien erfahren will, der darf nicht darauf warten, dass eine neutrale pro und contra abwägende Abhandlung veröffentlicht wird. Der (wahrgenommene) Wert einer Technologie ergibt sich aus ihrer An- und (!) Abwesenheit bei der Berichterstattung.
  • Technologien haben einen Lebenszyklus. Der kann - wie CORBA zeigt - viele Jahre umfassen, selbst wenn die Technologie suboptimal ist. Ob eine Technologie wirklich ihrem Anspruch gerecht wird, ist dabei ebenfalls womöglich erst nach Jahren feststellbar. Wer also auf schnelle Orientierung durch die Berichterstattung hofft, hofft vergebens.

Daraus folgt: Ob eine Technologie etwas taugt, ist an der Berichterstattung über sie nicht wirklich ablesbar. Die informiert zunächst und vor allem über Neuigkeiten und das, was geht. Das muss dann jeder Einzelne als Anlass nehmen, die Technologien selbst einer Prüfung im Hinblick auf seine Problemszenarien zu unterziehen.

So ist es halt. Aber muss es so sein? Wahrscheinlich. Es gibt einfach derzeit keine Lobby für negative Berichterstattungen zu Technologien und Konzepten. Wer schreibt, will kaum über Misserfolge schreiben. Und wer veröffentlicht, der ist an attraktiven hoffnungstragenden Themen interessiert und nicht an Misserfolgen. Wenn schon die Veröffentlichung von falsifizierenden Forschungen in der qua Anspruch neutralen Wissenschaft ein Problem ist, dann allemal die von negativen Erfahrungen in Publikationen "nur" für ein Fachpublikum.

Wer Orientierung sucht, muss also selbst ständig beobachten und die Themenlebenszyklen im Blick behalten. Um das Selberlernen, das eigene Evaluieren führt wieder einmal kein Weg herum.

Oder? Wo ist die Publikation, die genau das aufgreift und Themenentwicklungen verfolgt? Das wäre doch mal etwas! Das wäre eine Dienstleistung ab vom Üblichen, die wirklich etwas bringen können. Die Branche würde sich selbst reflektieren. Ein Zeichen von Erwachsenwerden wäre das. Allerdings: Zu schön, um wahr zu werden, fürchte ich.

Sonntag, 10. August 2008

Lieber grün freuen als schwarz sehen - Zufriedener mit Unit Tests

Grad habe ich den Code für meinen nächsten dotnetpro-Artikel fertiggestellt. Zuerst wollte ich ihn einfach nur so runterprogrammieren. Zwar bin ich ein Freund solider Planung und automatischer Tests, aber ich hatte den Chor der Entwickler im Ohr, die da singen "Aber muss denn das immer sein?" So hatte ich mich entschieden, auf Contract-First Design zu verzichten, keinen DI-Framework einzusetzen und eigentlich auch keine großartigen Unit Tests anzulegen.

Aber: Selten so gelacht. Diese Vorsätze konnte ich keine Stunde einhalten. Echt. Ich konnte nicht. Ich musste eine Architektur zumindest auf 2-3 Blättern skizzieren und Kontrakte andeuten. Die Last, das alles nur im Kopf zu versuchen, war mir einfach zu groß: ich hätte mich dafür anstrengen müssen und ich wäre in Sackgassen bei der ad hoc Implementierung gelaufen, die mich Mehraufwand gekostet hätten.

Das hat mir schonmal gezeigt, dass Architektur eine Gewohnheitssache ist. Am Anfang kann man ohne, weil man sich ihres Wertes nicht so bewusst ist. Dann macht es Mühe, sich auf sie einzulassen. Es tut weh, seine Gewohnheiten zu ändern. Man muss sich schon dazu zwingen. Aber wenn man am Ende dieses Tals der Tränen herauskommt, dann ist der Prozess ganz natürlich. Dann kann man - oder zumindest ich - nicht mehr anders. Dann ist Architektur ein Muss. Sonst fehlt einfach etwas.

Um den Beispielcode aber nicht mit Details zu überfrachten, habe ich trotzdem die Kontrakte nicht in eigene Assemblies gesteckt und auch kein DI-Framework benutzt. Soviel dann doch als Zugeständnis an den Entwicklerchor ;-)

imageDann das Testen. Eigentlich wollte ich nicht. Aber dann konnte ich doch nicht anders. Die Architektur einfach so zu implementieren und dann durch das kleine Frontend zu testen... nein, das ging nicht. Ich habe es echt versucht. Aber ich hab es dann nicht ausgehalten, so lange auf Feedback zu warten. Um herauszufinden, ob eine Infrastrukturfunktionalität des Beispielcodes funktioniert, hätte ich irgendwie künstlich einen Durchstich vom Frontend dorthin machen müssen. Wasfürein Aufwand! Oder ich hätte einfach weiter programmieren müssen, bis irgendwann man ein Durchstich da ist. Welch frustrierende Wartezeit!

Und so habe ich doch wieder alle Bereiche mit Unit Tests abgedeckt. Schritt für Schritt, eine Methode nach der anderen. Das hat sich einfach gut angefühl. Eine Erleichterung war es, nach ein wenig Programmierung mich beim Testen entspannen zu können.

Zweierlei ist mir dabei aufgefallen:

  1. Proaktives Testen geschieht in einem positiven Gefühl. Ich bin ganz aufgeregt, wenn ich den Test schreibe, weil ich ja sehen möchte, ob meine Idee vom Code richtig ist. Wenn ich hingegen auf einen solchen sofortigen Test verzichte und darauf warte, dass irgendwann mal eine Nutzung durch das Frontend auf einen Fehler stößt, dann ist mein Gefühl negativ. Dann teste ich nicht proaktiv, sondern debugge, d.h. ich befinde mich in einem Problemmodus. Schlechte Laune garantiert.
  2. Ein häufigerer Rollenwechsel zwischen Programmierer und Tester schafft Abstand zu Code. So entsteht Raum für Reflektionen. Das kann nur positiv sein, denn dann sehe ich plötzlich Dinge, die ich beim unausgesetzten Programmieren "im Flow" übersehen würde. Solcher Rollenwechsel entschleunigt auch in gewisser Weise. Auch das ist gut: für die Seele und den Code.

Also mal abgesehen von den Vorteilen automatischer Tests in punkto Refaktorisierbarkeit von Code habe ich für mich gemerkt, dass der Gewinn schon davor in einem guten Gefühl, in Entspannung liegt. Positive Einstellung und Entschleunigung machen einfach Spaß. Ich habe mich sozusagen grün gefreut angesichts der wachsenden Zahl kleiner grüner, also positiver Rückmeldungen vom Unit Testing Framework in ReSharper 4.0 (s. Bild oben).

imageSo richtig zufrieden haben mich die grünen Rückmeldungen aber erst werden lassen, als ich zusätzlich noch auf die Codeabdeckung der Tests geachtet habe. Fehlerfreiheit lässt sich ja nicht nachweisen. Also sagt ein grüner Test nicht, dass Code ohne Fehler ist. Tests überprüfen nur Erwartungen. Um ein gutes Gefühl zum Code zu bekommen, muss ich mehr als eine Erwartung haben; jeder Teil des Codes - jede Verzweigung, jede Methode - sollte meine Erwartungen erfüllen. Also müssen meine Tests möglichst viel Code abdecken. Meine Wunschmarke ist 90% Codeabdeckung. 100% sind natürlich noch besser - wenn auch nicht immer ausreichend -, aber ab und an lasse ich mal den Code für einen exotischen Fehlerfall ungetestet.

Mit der Integration von TestDriven und NCover habe mein gutes, grünes Gefühl dann gefestigt. Wie das nebenstehende Bild zeigt, decken meine Tests 97% der Codebasis ab. Das finde ich für den Zweck des Beispielcodes ausreichend.

Und so habe ich mich denn wirklich gut gefühlt, ganz grün vor Spaß, statt vor lauter Bugs schwarz geärgert. Architektur und Testen sind Gewohnheiten. Wenn man sie sich zum Nutzen der Codequalität einmal angeeignet hat, dann ist´s schwer, sie wieder abzustreifen. Aber es sind ja keine schlechten Gewohnheiten. Und sie haben mir ein gutes Gefühl verschafft. Was will ich mehr?

Freitag, 25. Juli 2008

dotnetpro.tv einmal anders

Leider habe ich ja bis auf Weiteres meine Videoserie dotnetpro.tv einstellen müssen. (Das Budget hat einfach auf Dauer nicht gereicht. Schade.) Deshalb freue ich mich umso mehr, nun doch einmal wieder in einem Video "mitgespielt" zu haben:

image

Microsoft hat auf der OOP 2008 eine Reihe von Videos aufgenommen, so auch eines mit meiner Rhetoriktraining-Kollegin Renate Klein und mir. Wir sollten etwas zum Thema "Softwarearchitektur" sagen - haben uns dann allerdings erlaubt, nicht über Technologien und Konzepte zu reden, sondern über Softskills.

Hat Spaß gemacht. Und Damir, der Interviewer, hat auch noch in unser Horn gestoßen. Sehr gut! :-) Das Interview ist jetzt online. Viel Spaß bei dotnetpro.tv der anderen Art!

Montag, 14. Juli 2008

Kaffeehauskonsultation in München

Am Dienstag, den 22. Juli ist es mal wieder soweit: ich lade ein zur Kaffeehauskonsultation in München. Von 8:00h bis 13:30h stehe ich im Café Wörner´s in der Herzogspitalstraße zu kostenlosen Beratungsgesprächen über Ihre Softwareprojekte zur Verfügung.

Wenn Sie einen 75minütigen Gesprächstermin insb. zum Thema .NET Softwarearchitektur vereinbaren wollen, schreiben Sie mir einfach eine kurze Email an: info<klammeraffe>ralfw.de. Ich freue mich auf spannende Problemszenarien.

Donnerstag, 26. Juni 2008

Bound in Space - Skizze der Kommunikation in einem Application Space

Komponenten verteilen, Komponenten parallel arbeiten lassen und überhaupt Software aus Komponenten aufbauen sollte viel, viel einfacher werden. Denn nur dadurch würden sich diese Konzepte breiter durchsetzen. Und nur dadurch würde Software in größerer Menge performanter, skalierbarer, testbarer, evolvierbarer. Dafür habe ich in meinem gestrigen Posting eine Lanze gebrochen.

Heute möchte ich zu dieser Vision eines Application Space (AppSpace) ein Bild zeichnen. Ich stelle mir vor, dass Software in Zukunft sehr einheitlich und ganz grundsätzlich so organisiert sein sollte:

image

Die Grundbausteine von Software sind Komponenten. (Klassen sind auch wichtig, aber unterhalb der Wahrnehmungsschwelle von Softwarearchitekten.) Definieren wir sie einfach mal als binäre Codeeinheiten mit separater Leistungsbeschreibung (Kontrakt). Wenn Sie jetzt "Assembly" denken, liegen Sie nicht falsch.

Diese Komponenten "leben und arbeiten" in etwas, das ich mal Compartment nennen. Solche "Abteile" teilen einen Raum, deshalb finde ich den Begriff ganz passend in Bezug auf die Raum-Metapher, die hinter Application Space steht.

Wie diese Komponenten in den Abteilen aufgehängt sind, ist zunächst einmal nicht so wichtig. Hauptsache, sie haben erstmal ein "Zuhause", in dem manche "zusammen leben" und andere eben in anderen "Zuhauses". (Hm... vielleicht wäre "Appartment" besser als "Compartment"? Dann könnten Komponenten in einer WG leben :-)

Innerhalb der Compartments können Komponenten direkt miteinander kommunizieren, einfach über Methodenaufruf. Sie liegen im selben "Abteil" dicht beisammen (im selben Adressraum) und können daher den Stack nutzen. Alles ist, wie Sie es kennen und lieben. Einzig, die Verbindung zwischen zwei Komponenten wird durch einen DI Framework bzw. Microkernel hergestellt. Den bietet das Compartment als komponentenumfassender Kontext; das ist eine seiner Infrastrukturleistungen.

Wie Sie Compartments auf Prozesse oder Maschienen oder gar Netzwerke verteilen, ist zunächst einmal unerheblich. Das sind Feinheiten des Deployments. In einem AppSpace gilt: Was in einem Compartment gehostet wird, liegt nah beieinander in einem Adressraum, der direkte Kommunikation erlaubt. Deshalb können Sie auch entscheiden, ob Compartments das noch durch eigene AppDomains unterstreichen sollen. Kann sein, muss aber nicht.

Fangen Sie mit 3 Compartments in einem Prozess an und verteilen Sie sie später auf 3 Prozesse. Oder führen Sie Redundanz ein, indem Sie sie auf 7 Prozesse verteilen. Welcher Art die Prozesse sind - also IIS oder NT Service oder Console EXE usw. - ist dabei nicht so wichtig. Diese Compartment Hosts müssen einfach zum Zweck der darin "lebenden" Komponenten passen.

Wegen dieser Flexibilität fehlen im obigen Bild auch Prozesse und Maschinen und Netzwerke. Denn das Credo des AppSpace ist, dass diese Deploymenteinheiten zweitrangig sind. Erstrangig ist hingegen, ob eine Komponente synchron und nah bei anderen läuft oder asynchron und fern von anderen. Darüber gibt ein AppSpace-Diagramm jedoch Auskunft.

Synchrone Komponenten sind einfach - selbst wenn Sie DI benutzen. Asynchrone Komponenten jedoch... da wird es knifflig. Wie sollen die miteinander kommunizieren? Die Asynchronizität beginnt schon, wenn zwei Objekte auf separaten Threads laufen. Sie wird schlimmer, wenn diese Objekte in verschiedenen AppDomains hängen und noch übler wird´s, wenn sie durch Prozesse, Maschinenen oder Netzwerke getrennt sind.

Heute stehen dafür eine Reihen unterschiedlicher Technologien zur Verfügung. Da gibt es eine Pallette von Möglichkeiten, Parallelität einzuführen. Wenn Ihre Anwendung in mehrere Prozesse zerlegt ist, ergibt sich die ganz natürlich. Ansonsten können Sie Threads aber auch von Hand auf die eine oder andere Weise erzeugen. Aber Achtung: nicht zuviele, sonst ist die ganze Asynchronizität kontraproduktiv.

Vielfältiger sind die Möglichkeiten für die Kommunikation zwischen den Threads. Die kann über´s Dateisystem laufen oder gemeinsame Objekte (shared memory), rohe TCP Sockets funktionieren genauso wie WCF.

Diese Vielfalt der Kommunikationsmedien, so verständlich sie ist, halte ich nun inzwischen für nachteilig. Niemand kennt sich mehr aus. Ich plädiere daher für eine radikale Vereinfachung durch Vereinheitlichung, mit der sich vielleicht 80% aller verteilter Kommunikation lösen lässt. Nein, nicht 100%, aber ich sag mal optimistisch 80%. Mir liegt daran, Asychronizität "den Massen" nahezubringen und nicht wirklich jedem zu jeder Zeit die Entscheidung zwischen vielen Optionen aufzubürden.

Die Technologievielfalt kann und soll also erhalten bleiben - unter der Haube. Wer sie braucht, um im Einzelfall mal performanter zu sein, hat sie dann. Aber der allgemeine Fall wird durch Abstraktion einfacher. So wie es schon viele Male in der Geschichte der Softwareentwicklung geschehen ist. Beispiel SQL, Beispiel O/R Mapping, Beispiel GDI+ usw. usf.

Wie soll nun diese Vereinfachung der Kommunikation zwischen asynchronen Komponenten aussehen? Das obige Bild zeigt es: Neben der direkten Kommunikation innerhalb eines Compartments gibt es die indirekte (!) zwischen Compartments. Indirekt ist sie immer, wenn Komponenten asynchron zu anderen laufen. (Insofern kann auch innerhalb desselben Compartments indirekt kommuniziert werden.)

Indirekte Kommunikation bedeutet, dass eine Client-Komponente nicht (!) direkt mit einer Service-Komponente spricht. Client und asynchroner Service sind vielmehr immer (!) durch einen Vermittler verbunden. Im einfachsten Fall ist dieser Vermittler eine Warteschlange, eine Queue. Aber das ist nur der einfachste oder übliche Fall. Ein AppSpace ist nicht darauf beschränkt. Zwischen Client und Service können alle möglichen Vermittlerformen stehen: Queue, Stack, Baum, relationale Tabelle, Liste, Array, Dictionary... you name it.

Gemeinsam ist allen Vermittlern nur, dass sie Datenstrukturen sind. Die eine Komponente manipuliert sie, die andere beobachtet sie. Und dann können die Rollen wechseln. Beobachtung ist entweder aktiv (polling) oder passiv (Ereignisgesteuert, Stichwort: EDA).

Diese Datenstrukturen existieren virtuell im AppSpace und sind für alle Komponenten in allen Compartments grundsätzlich zugänglich. Deshalb liegen die Winkel der asynchronen Kommunikation im obigen Bild im Space.

Implementationstechnisch sieht das jedoch anders aus. Da liegen die Vermittlerdatenstrukturen ebenfalls in Compartments, sozusagen in einem Gürtel um sie herum.

image

Letztlich ist es aber egal, wo genau diese Datenstrukturen, die Container hängen. Sie können nach dem Prinzip "write remote, read local" immer beim asynchronen Service angesiedelt sein (also im Compartment der Pfeilspitze einer Verbindung zwischen zwei Komponenten). Oder sie können auch in einem ganz anderen Compartment liegen:

image

Container werden im AppSpace gegenüber Anwendungscode nicht durch eine Adresse repräsentiert, sondern durch einen AppSpace-globalen Namen.

Der AppSpace macht Komponenten insofern nicht nur durch DI implementationsunabhängig voneinander, sondern auch unabhängig in Bezug auf Ort und Zeit. Durch Container als Vermittler stehen Komponenten nicht mehr direkt in Kontakt, d.h. beide müssen nicht mehr gleichzeitig "anwesend" sein im AppSpace. Und AppSpace-weite Containernamen verbergen den genauen Ort (Compartment, AppDomain, Prozess, Maschine, Netzwerk), an dem eine asynchrone Komponente "lebt".

Nach der grundlegenden Entscheidung, ob Komponenten synchron oder asynchron kommunizieren sollen, kann die Topologie sich bei asynchronen Komponenten sehr weitreichend ändern, d.h. wechselnden Bedürfnissen anpassen, ohne dass Client-Anwendungscode davon betroffen wäre.

Der AppSpace soll also die Vorteile von EDA verallgemeinern (nicht nur Queues, sondern auch andere Datenstrukturen, auf die man lauschen kann) und mit Komponentenorientierung wie Computing in/via the Cloud verschmelzen. Ich sehe ihn als Aggregat vieler existierender Technologien, die er unter einer Abstraktionsdecke versteckt.

Insofern bietet ein AppSpace einen schönen Migrationspfad: Ich kann mit ihm anfangen und erstmal nur Komponenten in einem Compartment verbinden. Dann kann ich in die Asynchronizität klein einsteigen, indem ich mal im selben Compartment eine Komponente asynchron mache. Und dann kann ich die Asynchronizität ausdehnen auf mehr Komponenten. Und dann kann ich diese Komponenten verteilen. Schließlich laufen sie auf vielen Rechnern übers LAN oder gar Internet verteilt. Ohne, dass mein Applikationscode davon etwas merken würde. Denn sobald ich einmal mit der asynchronen, containervermittelten Kommunikation angefangen habe, skaliert die von der cross-thread bis zur cross-firewall Kommunikation. Das Geheimnis liegt darin, dass sie nicht nur nachrichtenorientiert, sondern eben datenstrukturvermittelt ist.

Mittwoch, 25. Juni 2008

Asynchrone Architekturen einfacher machen - Umriss eines Application Space

In meiner vorherigen Posting habe ich ja eine Lanze für mehr Asynchronizität gebrochen. Die halte ich für wünschenswert und notwendig - aber einfach herzustellen ist sie heute noch nicht. Man kann es, es gibt viele "Splittertechnologien", aber die sind nicht "at our fingertips".

Bezüglich asynchroner Architekturen sind wir noch in einer "vor-bequemen Zeit", also etwa wie bei der Persistenz so um 2002 herum. Da gab es noch keine O/R Mapper für .NET und kein noch einfacher zu nutzendes db4o oder Persistor.Net.

Der ThreadPool, der AsyncOperationManager, die CCR, WCF... diese und andere Technologien machen es uns heute schon einfacher, asynchron sowohl lokal wie verteilt zu arbeiten. Dennoch: insgesamt sind sie mir noch zu kompliziert. Das liegt zum Teil an den APIs - die wollen jeder für sich one-size-fits-all Lösungen sein -, andererseits liegts am Konzept dahinter. WCF mag zwar nachrichtenorientiert sein - sehen tut man das aber nicht wirklich. Da helfen auch keine Data Contracts.

Um die gläserne Decke der Synchronizität nicht zu durchbrechen, sondern sie schlicht auf dem Weg zu mehr Performance und Skalierbarkeit zu umgehen, wünsche ich mir deshalb eine Infrastruktur, die Asynchronizität viel einfacher macht. Hier mal ein paar Punkte, die sie für mich minimal erfüllen müsste:

  • Einfache lokale Kommunikation zwischen Komponenten; Stichworte: Contract-first Design, Dependency Injection und Microkernel
  • Einfache verteilte Kommunikation; Stichworte: Nachrichtenorientierung, Event-Driven Architecture, "Datenstrukturorientierung" statt Serviceorientierung
  • Einfache Änderung der Topologie von Netzen verteilter Komponenten; Stichworte: Service Discovery, Unterstützung verschiedener Transportprotokolle, location transparency
  • Einfache Verteilung "in the cloud"; Stichworte: Tunnelung von NAT und Firewalls
  • Einfaches Hosting von Komponenten in unterschiedlichen AppDomains
  • Einfache Verteilung der Arbeit auf mehrere Threads

Dahinter stehen für mich ein paar Ansprüche, wie ich arbeiten/denken können möchte:

- Ich glaube, dass echte Komponentenorientierung durch Infrastruktur unterstützt werden muss. Sie stellt sich nicht von allein ein, sondern muss quasi durch Technologie "nahegelegt" werden. Visual Studio oder der .NET Framework tun das bisher nicht.

- Eine komponentenorientierte Infrastruktur soll das Laden von Komponenten einfach machen und auch die Kommunikation zwischen Komponenten.

- Bei der Komponentenorientierung möchte ich zunächst nicht zwischen lokalen und entfernten Komponenten unterscheiden. Beide sollten also durch dieselbe Infrastruktur unterstützt werden.

image - Für die Kommunikation zwischen Komponenten glaube ich allerdings fest daran, dass sie erstens zwischen lokalen und verteilten Komponenten ganz anders (!) aussehen sollte (s. dazu auch meinen Beitrag in "SOA Expertenwissen"). Und zweitens glaube ich, dass sie für verteilte Komponenten viel einfacher und einheitlicher werden sollte, als mit .NET Remoting oder auch WCF der Fall. Diese Einheitlichkeit und Einfachheit mag dann nicht 100% aller Probleme lösen, aber 80% sollten damit viel leichter zu erschlagen sein.

- Verteilte Komponenten möchte ich genauso einfach auf verschiedenen Threads wie in verschiedenen AppDomains oder auf verschiedenen Rechnern laufen lassen. Selbst wenn das Internet - the cloud - zwischen ihnen stehen sollte, darf sich nichts an ihrer Kommunikation ändern.

- Für einfache Verteilung ist es auch wichtig, dass ich die Wahl habe zwischen Push und Pull, um an Informationen zu kommen. Ich möchte selbst Last ganz einfach verteilen können, also nicht auf eine Cluster-Infrastruktur angewiesen sein. Warum kann ich nicht einfach heute einen Rechner als Server irgendwo in der Welt hinstellen und morgen 2, 3, 4 weitere irgendwo anders in der Welt - und alle teilen sich die Last? Klar, das geht schon... aber nicht so einfach. Da kann ich nicht nur schnell mal 'nen Service Contract mit WCF aufsetzen. Solche Flexibilität möchte ich aber haben.

Bottom line: Für mich müssen "Komponentendenke", Verteilung auf kurze und weite Distanz und Parallelität in einem einfachen 80%-Werkzeug zusammenkommen. Dabei gehts nicht darum, ein Rad neu zu erfinden, sondern vorhandene Ränder (endlich) zu einem Auto für jedermann zusammenzustecken. Bekanntes, Vertrautest, Bewährtes soll in Synergie zusammenarbeiten und in der Summe mehr sein, als die Teile vermuten lassen.

image Alles beginnt dabei mit... Einfachheit. Wir brauchen heute nicht leistungsfähigere Technologien, sondern vor allem einfachere. Sonst bleiben zuviele Entwickler zurück. Sie haben nicht die Zeit, sich in Kompliziertes einzuarbeiten. Kommen die Aspekte Komponentenorientierung, Verteilung und Parallelität nicht zusammen, dann nutzen nur wenige das Potenzial, das in Multi-Core Prozessoren und dem universalen Internet steckt.

Mit einem Application Space (AppSpace) wie oben skizziert, sähe das jedoch anders aus. (Ich könnte ihn auch Integration Space oder Component Space nennen. Application Space klingt für mich aber im Augenblick handlicher, weniger out-spaced ;-) Er wäre für komponentenorientierte, asynchrone Software das, was db4o für die Persistenz ist: ein "Ermöglicher" (enabler), der für viele erstmals etwas in Reichweite rückt, was ihnen bis dahin akademisch oder schwierig erschienen oder gar unbekannt war.

Dienstag, 24. Juni 2008

Synchronizität - Die gläserne Decke der Softwareentwicklung

Im Grunde kann man alle Probleme durch synchrone und sequenzielle Verarbeitung lösen. Das dauert dann manchmal zwar ein wenig, aber es geht. So bringt es uns auch jeder Programmierkurs bei. Ein Befehl folgt auf den anderen. Ein Unterprogramm ruft das nächste und wartet auf dessen Ergebnis, bevor es selbst weitermacht. Unterprogramme sind insofern fast nur syntactic sugar, denn ihr Code könnte auch am Aufrufort eingesetzt stehen. Compiler tun auch genau das, wenn es ihnen angemessen erscheint. Das heißt dann Inlining.

Was aber, wenn so eine sequenzielle Verarbeitung zu langsam ist? Na, dann macht man sie halt schneller, indem man den Algorithmus verbessert (z.B. Quick Sort statt Bubble Sort) oder einen schnelleren Prozessor kauft. Das ist dann so, als würde man einen schmächtigen Maurer ins Fitnessstudio schicken, damit er in Zukunft schneller Steine schleppen kann. Oder man ersetzt ihn gleich durch ein Förderband.

Wie effizient kann eine Implementation aber werden? Irgendwann ist der beste sequenzielle Algorithmus implementiert. Wie schnell können Prozessoren werden? Irgendwann ist da eine physikalische Grenze erreicht - wie es gerade so um die 3-6 GHz zu sein scheint.  Genauso ist es mit der Kraft bei Maurern und der Schnelligkeit von Föderbändern. Da gibt es einfach ein Ende der Fahnenstange. Dann hilft nicht mehr Quantität. Nein, dann muss sich die Qualität ändern.

Angekommen ist das in der Programmierausbildung aber eher noch nicht, würde ich sagen. Denn es herrscht immer noch das Denken in synchronen und sequenziellen Abläufen vor, die man eben versucht durch quantitative Einflussnahme zu beschleunigen. Weniger Instruktionen oder schnellere Prozessoren stehen als Wunschvorstellung hinter den Klagen darüber, dass mal wieder der Aufbau eines Fensters zu lange dauert oder die Datenbank zu langsam ist.

image Dabei wussten schon die alten Ägypter, dass man Pyramiden nicht schneller baut, indem man die Arbeiter erstmal ins Bodybuilding-Studio schickt. Die Pyramiden wurden von normalen Einwohnern Ägyptens gebaut. Genauso wie der Otto Versand vor allem Durchschnittsmenschen zur Bewältigung aller durch Menschen durchzuführenden Tätigkeiten einstellt. Sowohl die Ägypter wie der Otto Versand müssen halt damit leben, dass es vor allem normale Menschen gibt. Das liegt in der Definition von normal. Exzeptionelle Menschen - besonders starke, schnelle oder kluge - gibt es nur vergleichsweise wenige. Man kann sie daher nicht in größerer Menge rekrutieren und schon gar nicht bezahlen.

Die Lösung für Geschwindigkeitsprobleme liegt daher nicht im scale-up, in der Effizienzsteigerung von etwas Einzelnem, d.h. einem Arbeiter, einem Förderband, einem Rechner.

Die wahre Lösung liegt vielmehr in der Parallelisierung. "Normale" Ressourcen gleichzeitig an einer Aufgabe arbeiten zu lassen - z.B. viele normale Ägyptische Bauern an einer Pyramide oder Hamburger Bürger beim Otto Versand -, ist letztlich der weiter führende Weg, um dauerhaft schnell zu sein.

Beim scale-out stehen viele billige Ressourcen nebeneinander und arbeiten parallel an der Bewältigung einer Gesamtaufgabe. Statt viel Zeit in die Optimierung von Algorithmen zu stecken oder viel Geld in eine noch schnellere Hardware, sollten Sie überlegen, inwiefern die abzuarbeitenden Schritte in Ihrer Software parallelisierbar sind.

Das geht nicht, glauben Sie, weil Sie keine riesige ERP-Software für komplexe Geschäftsprozesse schreiben? Sie entwickeln doch nur ein kleines CRM - bei dem allerdings der Programmstart und auch der Wechsel zwischen Bearbeitungsmaske und Suchdialog an Performance zu wünschen übrig lassen.

Aber warum soll den nicht auch in solcher Software, die scheinbar nur aus sequenziellen, synchronen Verarbeitungsschritten besteht, etwas Parallelität möglich sein?

  • Beispiel Programmstart: Alles, was beim Programmstart nicht visuell ist (z.B. das Laden von Lookup-Daten), kann parallel zur Anzeige eines ersten Dialogs im Hintergrund passieren.
  • Beispiel Suchen: Abfragen können im Hintergrund laufen, während das User Interface weiterhin bedienbar bleibt. Abfrageergebnisse können häppchenweise ans UI geliefert werden; niemand sollte darauf warten müssen, dass auch noch der tausendste Datensatz dem Grid hinzugefügt ist, bevor er weiterarbeiten kann.
  • Beispiel Bearbeitungsende: Wenn am Ende der Bearbeitung Daten zu speichern sind, dann kann das parallel zur weiteren Bedienung geschehen. Warum sollte ich als Anwender darauf warten, dass die Daten auch vollständig und korrekt in der Datenbank angekommen sind? Das ist doch selbstverständlich.

Falls Sie diese Beispiele nicht überzeugen, biete ich an, dass ich Ihre Anwendung daraufhin untersuche, wo es Parallelisierungspotenzial gibt. Ich bin sicher, da gibt es einiges zu entdecken.

Warum nutzen wir das große scale-out Potenzial denn aber nicht in unseren Anwendungen? Gelegentlich mag es daran liegen, dass sich ein Algorithmus nicht so leicht parallelisieren lässt. Das halte ich aber für ein Detailproblem. Vor allem sehe ich den Grund nämlich in einem Mangel an 1. Bewusstsein und 2. einfacher (!) Technologie.

Sie können Programmteile heute selbstverständlich parallel machen. Asynchronizität ist natürlich möglich. Aber sie ist trotz aller Bemühungen der .NET-Framework-Entwickler noch nicht wirklich, wirklich einfach. Auch sind die Lösungen verteilt: ein bisschen steckt in System.Threading, ein bisschen in der CCR, ein bisschen in WCF usw.

Ich halte daher eine Konsolidierung für geboten. Das, was an Technologieschnipseln vorhanden ist, sollte unter einem intuitiven, einfach einzusetzenden Dach zusammengefasst werden. Dazu müsste dann aber wohl noch etwas Theorie kommt. Wir müssen Software erstmal aus grundsätzlich asynchronen Bausteinen zusammengesetzt denken.

image Aber ich sehe Licht am Horizont... :-) Mit einem Architekturansatz wie "Systemorientierten Programmierung" (SOP) und einer Zusammenführung von DI Frameworks mit Enterprise Service Bus (ESB) und Space Based Computing/Architecture (SBC/SBA) könnte es besser werden. Bei SOP stehen Softwarezellen als asynchrone und verteilte Codeeinheiten am Anfang, so dass das Denken in Richtung mehr Parallelität und flexibler Architekturen geleitet wird. Und eine Vereinigung von EDA-Konzepten (Event Driven Architecture) könnte das Hosting und die Kommunikation von Softwarezellen vereinfachen - im Großen wie im Kleinen (innerhalb von Prozessen).

Aber davon ein andermal... Einstweilen Achtung vor der gläsernen Decke "Synchronizität"! Synchrone Verarbeitung durch scale-up zu beschleunigen, führt nicht immer so weit, wie Sie denken mögen.