Follow my new blog

Dienstag, 7. September 2010

Die Power von Rx für Event-Based Components

Ob Event-Based Components (EBC) auch mit den Reactive Extensions, dem Rx Framework von Microsoft implementiert werden könnten, stand als Frage schon länger im Raum. Jetzt hab ich mich mal daran versucht.

Als Beispiel habe ich die FizzBuzz Kata gewählt. Der EBC-Entwurf ist denkbar simpel:

image

Bisher wurde dann eine EBC-Aktion wie Zahl transformieren so übersetzt:

   1: class Zahl_transformieren_früher
   2: {
   3:     public void In_Zahl(int zahl)
   4:     {
   5:         ...
   6:     }
   7:  
   8:     public event Action<string> Out_Symbol;
   9: }

Output-Pins von EBC-Aktionen waren Events, Input-Pins Methoden. Das ist eine geradlinige Übersetzung. Funktioniert in der Praxis tadellos.

Und doch bleibt etwas zu wünschen übrig. Es gibt einfach keine Unterstützung für Operationen auf Folgen von Events, also Nachrichtenströme. Natürlich kann man Aktionen wie Filter in einen Draht “einklemmen” – doch die müssen relativ umständlich erst entwickelt werden.

Mit Rx geht das jedoch viel einfacher. Damit kann man einen Event-Strom filtern oder verzögern oder zwei Ströme zusammenführen usw. usf. Viele schöne Operationen auf Events – oder eben Observables – gibt es dort. Warum das nicht für EBC nutzen?

Deshalb hier mein Vorschlag für eine Übersetzung von EBC-Flows mit Rx-Mitteln:

* Ein Output-Pin wird in ein IObservable<T> übersetzt
* Ein Input-Pin wird in einen IObserver<T> übersetzt

Mit diesen Interfaces kann man dann wunderschöne Sachen machen.

Aber es sind eben nur Interfaces und so brauchen wir noch Implementationen. Ich nenne die mal OutputPin<T> und InputPin<T>. Ist doch irgendwie naheliegend, oder?


   1: class Zahl_transformieren
   2: {
   3:     public InputPin<int> In_Zahl { get; private set; }
   4:     public OutputPin<string> Out_Symbol { get; private set; }
   5:  
   6:     public Zahl_transformieren()
   7:     {
   8:         this.In_Zahl = new InputPin<int>(Transform);
   9:         this.Out_Symbol = new OutputPin<string>();
  10:     }
  11:  
  12:  
  13:     private void Transform(int zahl)
  14:     {
  15:         if (IsFizzBuzz(zahl))
  16:             this.Out_Symbol.Post("fizzbuzz");
  17:         ...
  18:     }

Um die eigentlichen Transformationsroutine Transform() wird nun ein IObserver<T> gewickelt in Form eines InputPin<T>. Und die Ergebnisse werden an einen OutputPin<T> geschickt, der ein IObservable<T> ist. Den können dann folgende Aktionen abonnieren.

Die Zahlengenerierung sieht analog aus. Kein Hexenwerk. Beide Aktionen verdrahte ich dann noch in einer Aktionsgruppe zu einem Flow, um die Details der Verarbeitung gegenüber einem Client zu verbergen:


   1: public class FizzBuzzer
   2: {
   3:     public InputPin<Unit> In_Generate { get; private set; }
   4:     public OutputPin<string> Out_Symbole { get; private set; }
   5:  
   6:  
   7:     public FizzBuzzer()
   8:     {
   9:         var ze = new Zahlen_erzeugen();
  10:         var zt = new Zahl_transformieren();
  11:  
  12:         this.In_Generate = ze.In_Generate;
  13:         ze.Out_Zahl.Subscribe(zt.In_Zahl);
  14:         this.Out_Symbole = zt.Out_Symbol;
  15:     }
  16: }

Dem Input-Pin der Aktionsgruppe kann der Input-Pin von Zahlen erzeugen zugewiesen werden. Sie braucht also keinen eigenen. Dito für den Output-Pin der Aktionsgruppe.

Und die konsumierende Aktion abonniert die produzierende Aktion, s. Zeile 13. Das entspricht der bisherigen Zuweisung einer Input-Pin Methode als Eventhandler an einen Output-Pin Event.

Formal ist der Unterschied zwischen bisheriger Übersetzung und der nach Rx minimal, finde ich. Das setzt sich dann bis zum Client fort, der die Aktionsgruppe nutzt:


   1: var fb = new FizzBuzzer();
   2:  
   3: fb.Out_Symbole.Subscribe(Console.WriteLine);
   4: fb.In_Generate.OnNext(new Unit());

Ein kleinwenig gewöhnungsbedürftig mag allerdings sein, dass Rx keine Kommunikation ohne Wert kennt. Deshalb muss die Zahlengenerierung mit einem Parameter angestoßen werden. Dankenswerterweise gibt es dafür Unit, sozusagen einen “wertlosen” Typ. Er ist der Funktionalen Programmierung entlehnt und findet sich auch in F# wieder.

Und jetzt zum Gewinn durch eine Umstellung der Übersetzung von EBC mit Rx:


   1: fb.Out_Symbole
   2:   .Where(s => s[0] < '@')
   3:   .Select(s => int.Parse(s))
   4:   .SkipWhile(i => i < 42)
   5:   .Subscribe(Console.WriteLine);

Wir können ganz leicht in den “Eventfluss” eingreifen. Es gibt viele Standardoperationen dafür, allen voran die bekannten und beliebten Linq-Operatoren.

Wie klingt das? Ich finde das sehr vielversprechend. Damit experimentiere ich weiter. Mein Gefühl ist, dass mit Rx die Übersetzung noch systematischer wird und gleichzeitig die Möglichkeiten zum Umgang mit Event-Strömen wachsen. Also ein doppelter Gewinn.

Abhängigkeiten bewusster wahrnehmen

Abhängigkeiten sind eine der größten Geißeln der Softwareentwicklung. Wenn es ein allgemein anerkanntes Prinzip gibt, dann ist es, Abhängigkeiten zu minimieren. Jede Hilfe ist da willkommen. Was können Sie also tun, um Abhängigkeiten im Code los zu werden?
Für die Suche nach einer Lösung und die Beurteilung von Hilfsangeboten ist es nützlich, Abhängigkeiten zu kategorisieren. Nicht alle Abhängigkeiten sind gleich. Worüber reden wir also eigentlich?

Abhängigkeit

Eine Abhängigkeit ist vorhanden, wenn einer etwas braucht, ohne dass er seine Arbeit nicht verrichten kann. Eine Funktionseinheit kann eine andere Funktionseinheit brauchen; oder sie braucht nur eine Datei an einem bestimmten Ort. Oder sie ist von einem bestimmten Format von Daten abhängig, die als Zeichenkette an sie übergeben werden.
Abhängigkeiten lauern also überall.
Mir geht es allerdings vor allem um Abhängigkeiten zwischen den grundlegenden Funktionseinheiten Assembly, Klasse und Methode.

Statische Abhängigkeit

Wenn eine Funktionseinheit (FE) schon zur Compilezeit von einer anderen abhängig ist, dann nennt man das statische Abhängigkeit oder auch statische Kopplung. Hier ein Beispiel: Klasse Client ist von Klasse Service statisch abhängig.

   1: class Client
   2: {
   3:     private Service s;
   4: }
   5:  
   6: class Service
   7: {}

Oder hier eine Methode, die mit einer anderen statisch gekoppelt ist:



   1: void Foo()
   2: {
   3:     Bar();
   4: }
   5:  
   6: void Bar()
   7: {}

Oder hier eine statische Abhängigkeit von Client nicht zur Klasse Service, sondern auch noch zu einem Member von Service:



   1: class Client
   2: {
   3:     private Service s; 
   4:     
   5:     void Foo()
   6:     {
   7:         s.Text = "hello";
   8:     }
   9: }
  10:  
  11: class Service
  12: {
  13:     public string Text;
  14: }


Oder hier statische Kopplung zwischen Assemblies:

image

Ohne, dass die Funktionseinheiten, zu denen eine Kopplung besteht, bei der Compilation vorhanden sind, ist keine fehlerfreie Übersetzung möglich. Das macht statische Abhängigkeiten vergleichsweise zahm. Es fällt einfach schnell auf, ob eine Abhängigkeit nicht erfüllt ist.

Ted Faison hat in seinem Buch “Event-based Programming” für Abhängigkeiten eine Notation eingeführt, die ich hier auch benutzen möchte. Abhängigkeiten bezeichnet er dabei so:

image

Der Pfeil zeigt vom Abhängigen zum Unabhängigen und das Symbol (Zeichen Ou des erweiterten Lateinischen Alphabets) macht klar, dass der Pfeil ein Abhängigkeitspfeil ist.


Eine statische Abhängigkeit kann dann so qualifiziert werden:

image 


Dynamische Abhängigkeit


Dynamisch ist eine Abhängigkeit, wenn erst zur Laufzeit die eigentliche unabhängige Funktionseinheit verfügbar ist; der abhängige Code kann zur Compilezeit dann nämlich nur Annahmen darüber treffen, wie das zur Laufzeit “nachgereichte” Unabhängige aussieht.

Ein typsiches Beispiel sind Abhängigkeiten von Klassen, die zur Compilezeit durch Interfaces vertreten werden:

   1: class Client
   2: {
   3:     public IService s; 
   4:     
   5:     void Foo()
   6:     {
   7:         s.Text = "hello";
   8:     }
   9: }
  10:  
  11: interface IService
  12: {
  13:     string Text { get; set; }
  14: }
  15:  
  16: class Service : IService
  17: {
  18:     public string Text
  19:     {
  20:         get { ...  } 
  21:         set { ... }
  22:     }
  23: }

Hier ist die Klasse Client von der Klasse Service dynamisch abhängig. Vom Interface IService und dessen Property Text hingegen ist sie statisch abhängig.

image

Das macht klar, wie Komponentenorientierung funktioniert: Die Assembly, die Client implementiert, muss die referenzieren, die IService implementiert – aber nicht die Assembly von Service. Client und Service können also getrennt von einander entwickelt werden. Aber die IService-Assembly, der Kontrakt von Service,muss vorher da sein, weil die Client-Assembly wie die Service-Assembly daran statisch gekoppelt sind.

Statische Abhängigkeiten erfordern mithin Colocation von Code in derselben Assembly oder zumindest, dass Assemblies einander statisch referenzieren. Die unabhängige Funktionseinheit muss dann vor der abhängigen implementiert werden.

Ein typisches Muster, um diese Form der Abhängigkeitskonstellation zwischen Klassen und Interfaces auszudrücken, ist die Inversion of Control mit der ctor-Injection:

   1: class Client
   2:     {
   3:         private IService s; 
   4:         public Client(IService s)
   5:         {
   6:             this.s = s;
   7:         } 
   8:         
   9:         void Foo()
  10:         {
  11:             s.Text = "hello";
  12:         }
  13:     }
  14:  
  15:     class Program
  16:     {
  17:         static void Main(string[] args)
  18:         {
  19:             Client c = new Client(new Service());
  20:         }
  21:     }

So werden Klassen entkoppelt, um einfacher testbar zu werden, um Implementationen austauschen zu können und um produktiver bei der Entwicklung zu sein.

Dynamische Kopplung ist loser als statische – das ist vorteilhaft. Allerdings werden Fehler erst zur Laufzeit sichtbar. Die Entscheidung zwischen statischen und dynamischen Abhängigkeiten ist also eine zwischen Flexibilität/Entkopplung und Gewissheit.

Wer flexibel sein will, der setzt z.B. die neue dynamische Typisierung ein:

   1: class Client
   2: {
   3:     public dynamic s; 
   4:     
   5:     public void Foo()
   6:     {
   7:         s.Text = "hello";
   8:     }
   9: }

Zur Entwicklungszeit muss man sich dann nicht entscheiden, wie die Instanzen von s aussehen sollen. Sie müssen lediglich eine Property oder ein Feld Text bieten.

Das funktioniert dann gut mit dem bisherigen Service:

   1: static void Main(string[] args)
   2: {
   3:     Client c = new Client(); 
   4:     c.s = new Service(); 
   5:     c.Foo();
   6: }

Ohne Kontrolle durch den Compiler kann aber auch jeder andere Typ zugewiesen werden:

   1: static void Main(string[] args)
   2: {
   3:     Client c = new Client(); 
   4:     c.s = 42; 
   5:     c.Foo();
   6: }

Und das funktioniert dann gar nicht mehr gut.

Also: Vorsicht mit dynamischen Abhängigkeiten. Flexibilität, Entkopplung hat ihren Preis. Dynamische Abhängigkeiten lassen sich schlechter kontrollieren als statische. Sollte sich am Unabhängigen bzw. bei der Bedienung einer Abhängigkeit etwas ändern, ist erst viel später klar, auf welche Abhängigen das eine Auswirkung hat.


Logische Abhängigkeit


Der Compiler meldet kein Problem und Sie haben auch Ihre dynamischen Abhängigkeiten im Griff? Das ist gut – aber Ihre Software ist dann noch nicht in trockenen Tüchern, was die Abhängigkeiten angeht. Denn da sind noch die logischen Abhängigkeiten. Und das sind die fiesesten.

Hier eine kleine Denksportaufgabe. Welche Abhängigkeiten enthält diese Funktion, die die Nachkommastellen einer Zahl abtrennen soll:

   1: static double Trunc(double n)
   2: {
   3:     var s = n.ToString(); 
   4:     s = s.Substring(0, s.IndexOf(',')); 
   5:     return double.Parse(s);
   6: }

Gibt es statische Abhängigkeiten? Klar. Der Code ist ja streng typisiert.

Gibt es dynamische Abhängigkeiten? Nein. Hier wird zur Laufzeit nichts konkretisiert, nachgereicht.

Dennoch gibt es im Code eine Abhängigkeit. Und ob die Erwartung daran erfüllt wird, zeigt sich auch erst zur Laufzeit.

Trunc() ist davon abhängig, dass die String-Repräsentation  einer double-Zahl ein Komma enthält, das die Nachkommastellen abtrennt. Zahlen müssen diesem Format folgen:

Zahl ::= Vorkommastellen [ “,” Nachkommastellen ].

Vorkommastellen, Nachkommastellen ::= Ziffer { Ziffer }.
Das ist eine legitime Annahme für eine Software, die nur in Deutschland laufen soll – für eine internationale Software hingegen sollte sie nicht getroffen werden.

Eine solche Abhängigkeit nennt man logische Abhängigkeit. Sie besteht immer dann, wenn zwei Funktionseinheiten Annahmen übereinander machen, insbesondere Annahmen über ihre Funktionsweise. Deshalb drücken sich logische Abhängigkeiten oft in Daten aus, da die das Bindeglied zwischen Funktionseinheiten sind.

Hier besteht die Annahme der Funktionseinheit Trunc() darin, dass die Funktionseinheit double.ToString() ein Komma vor die Nachkommastellen setzt.

image

Als Kontrast ein Beispiel logischer Unabhängigkeit:

   1: static int GetIndexOf(string item, string[] list)
   2: {
   3:     for (var i = 0; i < list.Length; i++)            
   4:         if (list[i] == item) return i; 
   5:     return -1;
   6: }

Die Funktion ist nicht (!) abhängig davon, dass die Einträge in der Liste in einer bestimmten Reihenfolge stehen. Sie bestimmt mit und ohne Ordnung der Listenelemente den Index des gesuchten korrekt. So ist diese Funktion logisch unabhängig von potenziellen Quellen für Listen.

Keine Annahme über die Reihenfolge der Einträge zu machen, dient also der Entkopplung. Das ist gut für die Evolvierbarkeit (oder auch Wiederverwendbarkeit), hat jedoch seinen Preis. Die Performance dieses Verfahrens ist nicht optimal. Ob das allerdings schlimm ist… das hängt vom Verwendungszusammenhang ab. Im Sinne der Prinzipien KISS und Beware-of-Premature-Optimization (BoPO) mag es sinnvoll sein, keine Annahmen zu machen und die lose Kopplung einzustreichen – bis das an eine Grenze stößt.

Logische Abhängigkeiten machen die Softwareentwicklung wahrhaft komplex. Denn erstens wird erst zur Laufzeit sichtbar, ob sie erfüllt werden. Und zweitens führt ihre Nichterfüllung nicht immer und sofort zu einem Fehler.

Trunc() kann während der Entwicklung alle automatisierten Tests bestehen. Software, die die Funktion einsetzt, kann bei Hunderten Kunden fehlerfrei laufen. Doch dann, eines Tages, kommt es zu einem Fehler. Warum? Weil ein Anwender erstmalig auf die Idee gekommen ist, die Software auf einem Rechner mit anderer default Einstellung für das Dezimaltrennzeichen laufen zu lassen.

Logische Abhängigkeiten können sehr subtil sein. Kein Compiler zeigt sie an. Kein Laufzeitsystem deckt sie auf. Womöglich werden sie nur sporadisch nicht erfüllt.

Vorsicht also an den Schnittstellen zwischen Funktionseinheiten. Dort lauern immer wieder Annahmen über empfangene Daten, die die Funktionseinheiten mehr oder weniger offensichtlich und oft unerwartet stark miteinander logisch koppeln.


Zusammenschau


Abhängigkeiten machen das Softwareentwicklerleben schwer. Denn wo Abhängigkeiten bestehen, besteht immer die Gefahr, dass sich Änderungen am Unabhängigen auf Abhängige auswirken.

Sind die Abhängigkeiten nur statisch, dann zeigt ein Übersetzungslauf an, wo Erwartungen bei Abhängigen enttäuscht werden. Insofern sind statische Abhängigkeiten unkritisch, was die Entdeckung von Nichterfüllung angeht. Diese Effizienz, diese Sicherheit wird jedoch durch Inflexibilität erkauft. Statische Abhängigkeit bedeutet immer enge Kopplung.

Dynamische Abhängigkeiten koppeln loser – lassen sich jedoch erst zur Laufzeit entdecken. Durch die heutzutage sehr einfach mögliche Automatisierung von Tests lassen sich Probleme bei der Erfüllung dynamischer Abhängigkeiten jedoch auch fast so schnell erkennen wie bei statischen Abhängigkeiten.

Programme geschrieben in dynamischen Sprachen wie Python oder Ruby leiden daher auch nicht unter größerer Fehlerhäufigkeit als Programme geschrieben in einer streng typisierten Sprache wie C#. Wer mit einer dynamischen Sprache entwickelt, stützt sich einfach nur weniger auf den Compiler und setzt stattdessen mehr auf eine Sammlung von automatisierten Tests.

Die Komponentenorientierung hilft ebenfalls weiter, da sie dynamische Abhängigkeiten bewusst plant und Kontrakte auf beiden Seiten der Abhängigkeit für Stabilität sorgen. So kann lose Kopplung als Vorteil dynamischer Abhängigkeit eingestrichen werden, ohne die Sicherheit statischer Kopplung aufzugeben.

Logische Abhängigkeiten teilen mit den dynamischen Abhängigkeiten, dass sie erst zur Laufzeit festgestellt werden können. Sie gehen in ihrer Gefährlichkeit jedoch darüber hinaus. Sie sind oft unsichtbar, sie sind oft subtil, sie machen womöglich nur sporadisch Probleme. Es hilft aber nichts: Wir müssen mit ihnen leben.

Ohne logische Abhängigkeiten keine Zusammenarbeit. Wer sich nicht mindestens logische abhängig macht, steht allein.

Bei Planung, Test und Review Ihres Codes achten Sie daher besonders auf logische Abhängigkeiten. Wo Sie sie erkennen, dokumentieren Sie sie. Am besten mit einem Test. Beispiel:

   1: [Test]
   2: public void GetIndexOf_does_not_require_the_list_to_be_sorted()
   3: {
   4:     Assert.AreEqual(2, 
   5:                     Helpers.GetIndexOf("x", 
   6:                                        new[] { "k", "f", "x", "s" }));
   7: }

Das kann ein Unit Test wie hier sein. Das kann aber auch ein Integrationstest sein, der live die Funktionseinheiten, die Annahmen übereinander machen, zusammenspielen lässt.

.NET 4 Code Contracts könnten auch helfen, um logische Abhängigkeiten zu dokumentieren.

In jedem Fall gilt jedoch: zentralisieren Sie logische Abhängigkeiten. Lassen Sie möglichst nur eine Funktionseinheit in einer bestimmten Hinsicht logisch von einer anderen abhängig sein.

Ein Beispiel dafür ist ein Proxy in der verteilten Kommunikation. Der zentralisiert die logische Abhängigkeit zwischen Client und Server in Bezug auf das Datenformat auf der Leitung. Der Client nimmt z.B. an, dass der Server SOAP-Nachrichten versteht. Statt nun aber diese Annahme an vielen Stellen im Code zu treffen, zentralisiert man sie im Proxy. Sollte sich das Datenformat ändern, trifft die Annahme also nicht mehr zu, dann ist nur eine Funktionseinheit betroffen.

Der Proxy ist sozusagen dafür zuständig, eine logische Abhängigkeit in eine statische zu verwandeln. Denn wo der Proxy von SOAP-Nachrichten logisch abhängt, da hängt Code, der ihn nutzt, z.B. nur von einem POCO ab, das der Proxy umwandelt in einen Teil einer SOAP-Nachricht.

Seien Sie also wachsam, was die Abhängigkeiten in Ihrer Software angeht. Unterscheiden Sie zwischen statischen, dynamischen und logischen. Werden Sie sich der Abhängigkeiten in Ihrem Code bewusst. Wählen Sie die eine oder andere Form mit Bedacht. Prüfen Sie die Erfüllung von Abhängigkeiten automatisiert. Dann sind Sie auf einem guten Weg, die Komplexität Ihrer Software zu verringern.

Sonntag, 5. September 2010

Lesen heute für Softwareentwickler

Neulich wurde ich gefragt, ob das CCD-Wiki es ernst meine mit der Empfehlung, 6 Fachbücher pro Jahr zu lesen. Das sei doch wohl etwas viel verlangt.

image Hm… ist das wirklich viel, ja, zuviel verlangt von geplagten Softwareentwicklern? 6 Fachbücher lesen pro Jahr, also alle 2 Monate ein anderes. Oder wenn wir mal 400 Seiten pro Fachbuch annehmen, knapp 7 Seiten pro Tag lesen. Jahrein, jahraus… Das würde bei 2 Minuten Lesezeit, oder sagen wir 3, damit das Lesen gründlich ist, pro Tag 21 Minuten kosten, also immerhin 1,5% der Tageszeit und wackere 2,5% der Wachzeit.

Tja, wie sieht es aus? Sind 21 Minuten Fachbuchlektüre pro Werk-, Sonn- und Feiertag jahrein, jahraus zuviel verlangt? Dazu kommen ja noch andere Quellen, die wir auch empfehlen zu lesen. Blogartikel, Zeitschriftenartikel, Twitternachrichten… Wenn wir da mal annehmen, CCD würde empfehlen, pro Woche – horribile dictu! – auch noch 4 Fachartikel zu lesen… - Moment, ich rechne, 4 * 5 Seiten à 3 Minuten = 60 Minuten pro Woche oder knapp 9 Minuten pro Tag… - Wir wären am Ende bei 21 + 9 = 30 Minuten pro Wochentag jahrein, jahraus… Puh… Ist das nicht ein bisschen viel?

Das ist die Frage: Sind 6 Fachbücher und 208 Fachartikel pro Jahr zuviel für einen Softwareentwickler? Setzt das sein Zeitbudget unter inakzeptablen Stress? Bringt das seine Aufnahmefähigkeit an ihre Grenzen? 3% der Wachzeit hingegeben an Fachlektüre?

Meine persönliche kurze Antwort: Nein. Das ist natürlich nicht zuviel.

Das kostet weder zuviel Zeit, noch sollte es einen erwachsenen Menschen an sein Aufnahmelimit führen. Oder wenn, dann erwäge man ein Umsatteln auf den ehrenwerten Beruf des Bäckereifachverkäufers.

Und jetzt die lange Antwort:

Verantwortung

30 Minuten Fachlektüre pro Tag dürfen nicht zuviel sein. Denn jede Minute Fachlektüre ist eine Investition in die Erhaltung und sogar die Verbesserung der Vermittlungsfähigkeit. Angesichts von Entity Framework, Code Contracts, TPL, WF, Scrum, WPF, dynamische Typen, Rx, Fit, EBC, WCF, TDD, AppSpace, Complex Event Processing, Kanban und was der Technologien/Konzepte/Methoden noch mehr sein mag, angesichts dieses Sperrfeuers konstanter Neuerungen und Veränderungen sind 30 Minuten täglich ein Tropfen auf den heißen Stein, um auch nur annähernd uptodate zu bleiben.

Muss man denn aber uptodate bleiben? Ja, aus zwei Gründen:

Erstens sollte es jedem am Herzen liegen, seine Kompetenz hoch zu halten. Denn hohe Kompetenz bedeutet Freiheit. Wer kompetent ist, kann sich im Notfall oder auch ohne Not den Job aussuchen. Wer mit veraltetem Wissen auf seinem Sessel sitzt, der hat diese Freiheit nicht.

Das ist wie mit alten Leuten, die stolpern. Warum brechen die sich so leicht etwas? Weil sie keine “Freiheit” haben. Sie sind nicht mehr reaktionsschnell, sie haben keine elastischen Knochen mehr und sie haben wenig Kraft. Das Resultat ist ein ungebremster Fall, der direkt auf einen spröden Knochen trifft.

Dasselbe gilt für “eingerostete” Kompetenz. Statt eines gebrochenen Knochens, der in einigen Wochen wieder heilt, ist das Ergebnis hier jedoch schlimmer, weil es viel länger anhält. Die Unsicherheit nimmt zu, die Motivation nimmt ab, alles dauert länger, die Zahl der Konflikte mit anderen steigt, Veränderungen werden schwieriger. Bottom line: “Eingerostete” Kompetenz erzeugt Stress. Garantiert.

Zweitens ist die Erhaltung bzw. Vergrößerung eine Frage des Verantwortungsgefühls gegenüber Kunde und Arbeitgeber. Bezahlt wird für gute Arbeit. Und das bedeutet nicht nur, dass die Software irgendwie funktioniert. Das bedeutet auch, dass sie auf der Höhe der Zeit ist. Oder zumindest, dass der Entwickler weiß, was state-of-the-art ist, wenn er sich gegen eine Lösung auf der Höhe der Zeit entscheidet. Für etwas anderes würde ich als Kunde oder Arbeitgeber jedenfalls kein Geld ausgeben. Und ich denke, denselben Anspruch stellen Sie an einen Bauingenieur, Architekten, Elektrotechniker oder Landwirt.

Wer sich also nicht fit hält, der erfüllt den Anspruch an ihn nicht. 30 Minuten Lektüre pro Tag – die selbstverständlich in der Arbeitszeit liegen – sollten also nicht auf Widersprich stoßen.

Anspruch

Zähneknirschend mögen Sie sich nun in die Lektür schicken. Bei allem Verantwortungsgefühl erscheinen Ihnen die knapp 3400 Seiten pro Jahr “Zwangslesen” aber immer noch enorm.

Dazu kann ich nur sagen: Sehen Sie das Lesen nicht so eng.

image Sie mögen noch “Großvaters Anspruch” ans Lesen im Kopf haben. Zu dem gehört, dass jedes Buch ganz gelesen werden muss, dass man ein Buch zur Zeit liest, dass man ein Buch fertig liest und erst dann das nächste anfängt.

Vergessen Sie diesen Anspruch. Er gehört wie Großvater einer vergangenen Epoche an. Er ist entstanden in einer Mangelsituation. Früher gab es einfach nicht soviel zu lesen. Und Lesestoff war vergleichsweise teuer.

Heute haben wir Fachlektüre im Überfluss und zu kleinen Preisen. (6 * 35 EUR = 210 EUR/Jahr für Fachbücher – wenn Sie die denn selbst bezahlen müssen – halte ich für nicht viel Geld angesichts dessen, was auf dem Spiel steht: ihr Gehalt bzw. ihre Gehaltserhöhung. Kosten für Artikel setze ich mal gar nicht an. Davon gibt es soviele kostenlos im Internet; aber selbst wenn Sie noch das eine oder andere Zeitschriftenabo haben sollten, ändert sich die Größenordnung der Fachlektüreausgaben nicht.) Also können und sollen wir anders mit ihr umgehen.

Hier einige Tipps, nach denen ich lese:

  • Lesen Sie, was Sie interessiert. Versuchen Sie nicht, überall uptodate zu sein. Fokussieren Sie sich besonders auf 2-3 Schwerpunkte, die Sie besonders mögen. Aber lesen Sie auch sonst, was Ihr Interesse weckt.
    Und lassen Sie soweit es geht aus, was Sie langweilt. Denn nur bei hoher Lesemotivation nützt das Lesen etwas. Außerdem geht es dann schneller.
    Glauben Sie übrigens nicht, dass Sie auf diese selektive Leseweise etwas Wichtiges verpassen. Was wirklich, wirklich wichtig ist, kommt wieder und drängt sich früher oder später auch in Ihren Interessenshorizont.
  • Lesen Sie soweit es Sie interessiert, soweit Sie mitkommen. Zwingen Sie sich nicht, einen Artikel oder ein Buch zuende zu lesen. Geben Sie dem Text eine Chance, halten Sie durchaus auch einen Moment aus, wenn es mal zäh wird – aber prügeln Sie sich nichts rein. Brechen Sie also Lektüre guten Gewissens ab. Vielleicht kommen Sie später wieder zurück und lesen weiter. Vielleicht aber auch nicht. Manche Themen müssen sich in Ihnen erst entwickeln. “Ist der Schüler bereit, kommt der Lehrer” heißt es.
  • Lesen Sie quer. Springen Sie im Text. Verschaffen Sie sich einen Überblick (Inhaltsverzeichnis, Bilder, Überschriften) und lesen Sie dann, was Sie anzieht. Lesen Sie in der Mitte oder am Ende. Oder von allem ein bisschen.
  • Lesen Sie mehrere Fachbücher parallel. 50 Seiten in einem, dann 100 Seiten im anderen, dann wieder 70 Seiten im ersten, 150 Seiten in einem Dritten. Wie es Ihnen Spaß macht. Die Lektüre kann sich gegenseitig befruchten. Und Sie gewinnen Inkubationszeit: Nach 50 Seiten im ersten Buch mögen Sie erstmal genug haben. Das Thema muss sich bei Ihnen setzen. Schieben Sie es in den Hinterkopf und fangen Sie etwas Neues an. Später hat sich das erste Thema in Ihnen weiterentwickelt, dann lesen Sie ab Seite 51 weiter.
  • Verfolgen Sie viele Quellen (Blogs, Zeitschriften, Fachbücher), aber lesen Sie nicht alles. Vertrauen Sie auf Ihr Unterbewusstsein, dass es aus dem Informationsstrom für Sie Relevantes hervorhebt. Lassen Sie sich von Themen “anspringen”. Blättern Sie durch und verweilen Sie, wo es Sie hält.
  • Haben Sie nicht den Anspruch, alles, was Sie lesen, auch auszuprobieren. Tun Sie das, wenn es Ihnen wirklich wichtig erscheint oder Spaß verheißt. Ansonsten lassen Sie es sein und beobachten ggf. das Thema weiter.

Wenn Sie sich ein Thema konkret “draufschaffen” wollen, müssen Sie Ihren Modus natürlich etwas verändern. Um jedoch uptodate zu bleiben, reicht der hinter diesen Tipps stehende Anspruch: Lesen Sie selektiv. Hoffen Sie nicht auf das eine Buch, das es bringt. Surfen Sie die Welle des Überflusses.

Texte durcharbeiten, sich an ihnen abarbeiten, sie bis ins Letzte zu verstehen… das war gestern. (Nein, das kann natürlich auch heute noch sein. Aber solche Texte sind relativ selten. Setzen Sie stattdessen darauf, dass am Ende mehrere Texte zu einem Thema Ihnen den Durchblick mit weniger Mühe verschaffen, den Sie sich früher mit dem “Studium” eines Textes hätten mühsam erarbeiten müssen. Sie haben heute den Vorteil, Themen von vielen Autoren beleuchtet sehen zu können, wo früher der Quellenmangel sie auf einen festgenagelt hat.)

Was denken Sie nun? Ist die CCD-Forderung von 6 Fachbüchern pro Jahr wirklich so gewaltig? Oder sah sie nur so groß aus, weil Sie sie im Lichte eines veralteten Leseanspruchs verstanden haben?

Ich hoffe, Sie können nun entspannt(er) denken: “Alles halb so schlimm.” Und vor allem: “Das Lesen bringt mich weiter. Ich übernehme damit Verantwortung für meine Kompetenz. Es macht mich zu einem besseren Softwareentwickler. Und besser zu werden, das macht auch Spaß.”