Foundations for Professionals .NET Professionals im Profil guide to C# guidgen.de

Blog

Nutze den Augenblick
und teile der Welt mit, was Du zu sagen hast.

Räumliche Nähe wird überbewertet, Teil 2

Freitag, 20. August 2010, 18:25 Uhr
Permalink | Kommentare (3) | Kommentare als RSSRSS

Gestern habe ich in meinem Blogeintrag Räumliche Nähe wird überbewertet über die häufig betonte – meiner Meinung nach überbewertete – Bedeutung von räumlicher Nähe für Entwickler geschrieben.

Besonders interessant fand ich die zahlreichen Reaktionen, die mich größtenteils in Form von Kommentaren in meinem Blog erreicht haben. Diese reichen von absoluter Zustimmung wie

Schön, dass du diesen Mythos mal aufs Korn nimmst. Ich sehe es genauso: Colocation ist überbewertet.

bis hin zu gänzlich gegensätzlichen Ansichten:

Deswegen kann ich nur allen raten die von “Colocation” und der Unbedeutsamkeit der Örtlichkeit reden sich ins reelle Leben zu begeben […] Think global. Face reality.

Besonders gefreut hat mich, dass Ilker Cetinkaya das Thema aufgegriffen und seine Sicht der Dinge in dem Blogeintrag Räumliche Nähe wird unterschätzt beschrieben hat. Dort widerspricht er mir mit den Worten:

Nun, dem muss ich deutlich widersprechen. Genau das Gegenteil ist der Fall – und sowohl sein Artikel als auch die Kommentare dazu sind in meinen Augen eine Bestätigung: Räumliche Nähe wird einfach noch viel zu oft unterschätzt!

Das ist eine spannende Ausgangslage – zwei Blogeinträge zum gleichen Thema, aber gänzlich verschiedene Meinungen.

Da ich in meinem Blogeintrag Bezug auf die gute Zusammenarbeit von Peter Bucher und mir genommen habe, und Ilker diese nicht kennen kann, traf er einige Annahmen – diese möchte ich zunächst mit Fakten unterlegen, bevor ich auf die übrigen Argumente eingehe.

Das Beispiel ist ein Zeugnis von professioneller Software-Entwicklung. Da arbeiten offensichtlich zwei Profis zusammen. Sehr gut. Golo leitet daraus ab, dass es nicht zwingend notwendig sei, Teammitglieder an einem Ort, in einem Gebäude, in einem Raum zu haben, um effektiv ein “Team” zu sein. Nun, das mag ja schön und gut sein. Aber Golo: Hast Du denn Vergleiche? Und wenn ja, hast Du gemessen, wie “effektiv” denn beides ist? Ich befürchte nein. Wäre dem so, dann wäre mit großer Wahrscheinlichkeit Dein Fazit anders ausgefallen.

Was ich damit zum Ausdruck bringen will, ist, daß Golo und Peter wohl nie in einem Raum über längere Zeit zusammen gearbeitet haben. Ich unterstelle das jetzt einfach einmal Golo & Peter; bitte korrigiert mich und verzeiht mir, wenn dem nicht so ist. Es dient mir zur Illustration meiner Perspektive zum Thema.

Die kurze Antwort lautet: Deine Vermutung ist richtig. Da Peter und ich uns auf Grund der Entfernung eher selten – und wenn, dann zumeist im Rahmen von Usergroups und ähnlichen Treffen – sehen, haben wir noch nicht zusammen an einem Tisch gearbeitet.

Also, wäre dem so, so hätte Golo gar keinen Vergleich der Effektivität & Effizienz des Teamgefüges zwischen ihm und Peter. Beide sind ausgewiesene Profis in der Software-Entwicklung und schaffen aus der Distanz gemeinsam gute und effiziente Teamarbeit. Der Artikel von Golo beweist es. Man stelle sich nur vor, welche Produktivität und Ingenieursleistung möglich wäre, wenn zwei solche Profis an einem Tisch arbeiten würden. Wow.

Der Schluss von Ilker ist korrekt – ich habe keinen Vergleich bezüglich der Effektivität und Effizienz des Teamgefüges zwischen Peter und mir. Nun fehlen mir an dieser Stelle aber zwei Punkte:

Erstens: Ilker impliziert, dass unsere Produktivität und Ingenieursleistung noch viel höher sein könnte, wenn Peter und ich an einem Tisch arbeiten würden. Zweitens: Ilker sieht die Frage, ob ich den Vergleich überhaupt ziehen kann, als relevant an.

Ich habe in meinem vorigen Blogeintrag geschrieben, dass erfolgreiche Teams zwei Aspekte als Grundlage bedürfen: Vertrauen und gegenseitige Wertschätzung. Zwei Metriken, die auf Grund ihrer emotionalen und allzu menschlichen Natur nur schlecht in harte Zahlen gefasst und messbar gemacht werden können.

Warum auch immer, im Lauf der Zeit sind zwischen Peter und mir großes Vertrauen und hohe gegenseitige Wertschätzung entstanden und gewachsen. Dazu war die fehlende räumliche Nähe unerheblich – das Verständnis könnte heute nicht besser sein, auch wenn die räumliche Nähe gegeben gewesen wäre.

Natürlich kann ich das nicht im wissenschaftlichen Sinne beweisen, aber wie gesagt, dies sind ohnehin Metriken, die nur schlecht in Zahlen gefasst und gemessen werden können.

Daher bezweifle ich auch nicht nur, dass unsere Produktivität und Effizienz höher sein könnte, wenn wir an einem Tisch sitzen würden, sondern auch, dass die Frage nach der Vergleichbarkeit relevant ist.

Damit sind wir auch schon an einem wichtigen Punkt. Agile Software-Entwicklung und deren Verfahrensweisen behaupten nicht, dass es notwendig sei, ein Team an einem Fleck zu haben, um (gut) Software zu entwickeln.

Agile Methoden behaupten das vielleicht nicht durch die Bank, aber es gibt doch einige Fälle, in denen dies postuliert wird:

  • Extreme Programming (XP): Einer der Werte von XP ist Communication, in dessen Definition unter anderem “[…] and we communicate face to face daily” enthalten ist. Da face to face ein persönliches Gespräch von Angesicht zu Angesicht bedeutet, erfordert XP de facto räumliche Nähe.
  • Crystal Clear: Alistair Cockburn, der Erfinder der Crystal-Familie, geht in seinem Buch Crystal Clear: A Human-Powered Methodology for Small Teams sogar so weit, zu behaupten, dass Crystal Clear für verteilte Entwicklung nicht geeignet sei und räumliche Nähe zwingend erforderlich ist.

Selbst das Agile Manifest hebt in den zwölf agilen Prinzipien den Wert persönlicher Gespräche von Angesicht zu Angesicht als eigenen Punkt hervor:

The most efficient and effective method of conveying information to and within a development team is face-to-face conversation.

Insofern wird zwar nicht explizit verlang, dass sich das Entwicklungsteam an einem gemeinsamen Standort befinden muss – implizit ist diese Forderung aber sehr wohl enthalten.

Weiter schreibt Ilker:

Doch nicht nur gute Kommunikation ist ein Ziel der Co-location. Ein aus meiner Sicht wesentlich wichtigerer Faktor ist der Faktor der Transparenz.

Durch die Nähe zueinander sieht man auch, was der andere so macht. Soll heissen, man lernt seinen Tagesablauf, seine Aufgaben, seine Tools, seine Herausforderungen – na eben das ganze Arbeitsumfeld. Umgekehrt sehen andere, mit welchen Dingen man selbst tagtäglich konfrontiert ist und was es tatsächlich bedeutet, Software zu entwickeln. Durch die Transparenz entsteht mehr Verständnis zu der Aufgabe und der Leistung des anderen Teammitglieds. Man versucht sich gegenseitig zu helfen und aufzubauen. Das wiederum hilft der Kommunikation und der Produktivität im Team.

Das, was Ilker als Transparenz bezeichnet, erachte ich ebenfalls als wichtig. Doch auch hierfür ist meiner Erfahrung nach räumliche Nähe nicht zwingend erforderlich: Wenn die Frage Na, wie geht’s? nicht nur eine Floskel ist, sondern ihr ernsthaftes Interesse am Wohlbefinden des Gegenübers widerspiegelt, ist es gleich, ob ich das in Skype, im Windows Live Messenger oder eben persönlich frage.

Räumliche Nähe fördert dies nicht zwingend – wie viele Entwickler werden zu Beginn der Woche von ihrem Chef gefragt, wie das Wochenende war – aber nicht aus wirklichem Interesse, sondern als Smalltalk, als Floskel. Wenn es aber kein Smalltalk und keine Floskel ist, sondern es um den Menschen an sich geht, dann funktioniert das auch, wenn sich beide Gesprächspartner am jeweils anderen Ende der Welt befinden.

Spricht man darüber hinaus nicht nur über fachliche Belange im Sinne der aktuellen anstehenden Aufgaben, sondern auch über alltägliches und persönliches, dann ergibt sich auch ohne räumliche Nähe Verständnis für den anderen. Auch dann versucht man, sich gegenseitig zu helfen, sich aufzubauen, …

Abschließend führt Ilker noch den Wert

Individuals and Interactions over Processes and Tools

des des Agilen Manifests an, als Beleg für die Notwendigkeit räumlicher Nähe. Doch inwiefern ist der Zwang nach räumlicher Nähe ein Eingehen auf Individuen und deren Interaktion? Ist räumliche Nähe als bindende Verpflichtung nicht vielmehr ein Mittel eines Prozesses, der Individuen aufgezwungen wird?

Wenn ein Team sich entscheidet, dass es auch in einem verteilten Szenario dermaßen gut miteinander interagiert, dass es darauf guten Gewissens verzichten kann – ist dann die Forderung nach verbindlicher räumlicher Nähe nicht gerade das Gegenteil dessen, was das Agile Manifest fordert?

Räumliche Nähe wird überbewertet

Donnerstag, 19. August 2010, 03:39 Uhr
Permalink | Kommentare (31) | Kommentare als RSSRSS

Nahezu jede agile Methode propagiert räumliche Nähe auf die eine oder andere Art: In der Regel wird gefordert, dass der Kunde oder zumindest ein Vertreter des Kunden vor Ort verfügbar ist. Als wie essenziell dies angesehen wird, zeigt ein Blick auf Extreme Programming (XP) und Scrum:

  • XP: Extreme Programming enthält die Regel Customer On-Site, die besagt, dass der Kunde idealerweise persönlich und dauerhaft für Rückfragen der Entwickler zur Verfügung steht.
  • Scrum: Auch Scrum vertritt diese Position, geht aber noch einen Schritt weiter, indem mit dem Product Owner gar eine eigene Rolle definiert wird – unter der Auflage, dass auch der Product Owner vor Ort verfügbar ist.

Letztlich erfüllen so wohl XP wie auch Scrum damit ein Prinzip des Agilen Manifests, das de facto kaum Alternativen zu räumlicher Nähe zulässt:

The most efficient and effective method of conveying information to and within a development team is face-to-face conversation.

Für die Zusammenarbeit mit dem Kunden mag dies zuträglich sein – doch wie sieht es innerhalb des eigentlichen Entwicklungsteams aus? Häufig wird auch hier räumliche Nähe als unabdingbar propagiert.

Der Erfinder von Crystal Clear, Alistair Cockburn, geht sogar so weit, Crystal Clear als ungeeignet für verstreute beziehungsweise verteilte zu bezeichnen. Doch auch XP und Scrum, die in dieser Hinsicht nicht dermaßen dogmatisch sind, unterstützen verstreute oder verteilte Entwicklung nicht von Haus aus.

Räumliche Nähe, auch unter den Entwicklern, wird bevorzugt – anders seien Techniken wie beispielsweise das Programmieren in Paaren von XP auch gar nicht möglich, wird häufig argumentiert.

Als gewichtigstes Argument wird dann schließlich das Teambuilding angeführt – ohne räumliche Nähe könne ein Team nicht zusammenwachsen und sich zu einer Einheit formen, die mehr zu leisten im Stande ist als die Summe der einzelnen Teammitglieder. Das Problem ist – genau dieses Argument ist falsch. Räumliche Nähe innerhalb eines Entwicklerteams wird überbewertet.

Wie komme ich zu dieser Behauptung?

Zunächst kann man feststellen, dass räumliche Nähe keine hinreichende Bedingung für den Erfolg eines Teams als Team ist – es gibt unzählige Teams, die sich in räumlicher Nähe befinden, aber dennoch nicht als Team fungieren, sondern nur als Ansammlung von einzelnen Teammitgliedern. Die räumliche Nähe bedeutet hier nicht viel mehr als eine zufällige Anordnung.

Wenn räumliche Nähe also keine hinreichende Bedingung für das Zusammenwachsen eines Teams ist, verbleibt die Frage, ob es zumindest eine notwendige Bedingung hierfür ist?

Auch hier lautet die Antwort schlicht und ergreifend: Nein. Es gibt effektive und hoch effiziente Teams, die verstreut sind – und die sich, gerade in den heutigen Zeiten der digitalen Medien, vielleicht noch nie oder nur einige wenige Male begegnet sind.

Peter Bucher und ich sind ein solches Team. David Tielke und ich sind ein solches Team. Beide habe ich in meinem Leben noch nicht all zu oft getroffen – jeden von beiden höchstens fünf Mal. Und dennoch harmoniert die Zusammenarbeit in einem Ausmaß, das für Außenstehende nicht nachvollziehbar ist.

Auch wenn uns hunderte von Kilometern trennen, führen wir regelmäßig Programmieren in Paaren durch – per Skype und Teamviewer. Und die Zusammenarbeit funktioniert einwandfrei. Keiner von uns käme auf die Idee, zu sagen, dass wir effektiver oder effizienter sein könnten, wenn wir gemeinsam vor einem PC arbeiten würden.

Vermutlich wären wir dies noch nicht einmal, weil dann nicht jeder seine vertraute Arbeitsumgebung hätte, in der er sich rundum wohlfühlt.

Natürlich hat räumliche Nähe Vorteile – aber sie ist für das Funktionieren als Team unerheblich. Wichtig hierfür sind andere Aspekte, allen voran jedoch Vertrauen und gegenseitige Wertschätzung. Wenn ein Team diese Aspekte aufweist, ist es unerheblich, wie viele Kilometer zwischen den Teammitgliedern liegen.

Denn dann entsteht eine viel intensivere Nähe als es die räumliche Nähe per se jemals leisten könnte. Diese intensivere Nähe zeichnet sich durch Verständnis, Wertschätzung und gemeinsame Werte aus – und das ist es, was ein Team als Team erfolgreich macht.

Dann funktioniert Programmieren in Paaren, auch wenn man dazu technische Hilfsmittel wie Skype und TeamViewer benötigt.

In den vergangenen dreieinhalb Stunden haben Peter und ich einige Fehler in LightCore behoben – und ich bin mir sicher, das Ergebnis wäre keinen Deut besser, wenn wir in einem Raum gesessen hätten.

Die hohe Kunst ist nun, Teams sich entwickeln zu lassen, dass solche Konstellationen überhaupt erst entstehen können. Hierzu kann das Management nicht viel beitragen, außer Menschenkenntnis, und dem Willen, ein funktionierendes Team nicht für neue Projekte zu trennen – sondern das etablierte Team zu erhalten und es als Team vor neue Aufgaben und Herausforderungen zu stellen.

Anders funktioniert es nicht.

Neue Version von LightCore

Sonntag, 25. Juli 2010, 10:13 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Am 1. Januar 2010 hat Peter Bucher einen eigenentwickelten Dependency Injection-Container namens LightCore vorgestellt. Heute Nacht, nahezu acht Monate später, ist die neue Version 1.4 erschienen, zudem wurde die Webseite grundlegend überarbeitet, ist nun über die eigene Domain www.lightcore.ch erreichbar und wartet unter anderem mit einem neuen Design auf.

Was ist neu in LightCore 1.4?

Bemerkenswertestes Feature ist nach wie vor die hohe Ausführungsgeschwindigkeit, die andere Dependency Injection-Containern im Vergleich weit hinter sich lässt. Abgesehen von klassischer Objekterzeugung per new-Schlüsselwort ist mir kein schnellerer Weg bekannt, Objekte zu instanziieren.

Doch abgesehen davon gibt es auch noch einige echte Neuerungen, die es durchaus wert sind, einzeln genannt zu werden:

  • Unterstützte Plattformen: LightCore 1.4 steht nunmehr für .NET 4.0, .NET 3.5, Silverlight 3 und das .NET Compact Framework 3.5 zur Verfügung.
  • Collections: LightCore 1.4 ist nun in der Lage, mit Collections umzugehen. Das bedeutet: Werden beispielsweise mehrere Implementierungen von IFoo registriert, wird ein Parameter vom Typ IEnumerable<IFoo> automatisch erwartungsgemäß aufgelöst.
  • Dynamische Argumente: Erwartet eine Komponente die Übergabe von Parametern, so stand bislang nur die statische Angabe dieser Parameter während der Registrierung zur Verfügung. LightCore 1.4 ermöglicht nun auch den Einsatz von dynamischen Argumenten, das heißt, von Argumenten, die erst zur Laufzeit übergeben werden.
  • Verzögerte Instanziierung: Zumindest in der für .NET 4.0 kompilierten Version bietet LightCore 1.4 Unterstützung für den in .NET 4.0 eingeführten Lazy<T>-Typ, mit dessen Hilfe die verzögerte Instanziierung von großen Objekten – quasi on Demand – möglich wird.
  • Property Injection: Zwar ist Property Injection auch in LightCore 1.4 nach wie vor nur optional verfügbar – sie ist dafür aber um einiges schneller geworden. Wer also auf Property Injection nicht verzichten kann, erhält zumindest eine deutlich beschleunigte Ausführung.
  • Lizenz: Endlich ist auch die Frage geklärt, unter welcher Lizenz LightCore verfügbar ist. Prinzipiell gilt: Im Binärformat darf LightCore in Projekten jeglicher Art genutzt werden, als Quellcode steht LightCore unter der Ms-RSL für Referenzzwecke zur Verfügung. Änderungen wie auch abgeleitete Werke sind nicht gestattet.

Darüber hinaus enthält LightCore 1.4 noch zahlreiche weitere kleine Neuerungen – wie beispielsweise eine vollständige Abdeckung durch Unittests, im Vergleich zu Version 1.0 deutlich aufgeräumteren und besser refaktorisierten Quellcode und eine bessere Fehlerbehandlung.

Erwähnenswert ist allerdings auch noch ein Breaking Change im Vergleich zu Version 1.0: Benannte Registrierungen sind entfallen. Damit war es möglich, auf eine einzige Schnittstelle verschiedene Implementierungen zu registrieren, die nur durch ein händisch vergebenes Tag unterschieden werden konnten. Zur Ausführungszeit konnte dann mittels dieses Tags angegeben werden, welche konkrete Implementierung instanziiert werden soll.

Die Begründung, warum dieses Feature in LightCore 1.4 entfallen ist, findet sich in meinem Blogeintrag Welche Bedeutung wohnt einer Schnittstelle inne?. Für mich persönlich stellt dieser Breaking Change einen Schritt in die richtige Richtung dar, weshalb ich mit damit gut leben kann.

Insgesamt kann ich mein Fazit für Version 1.0 damit auch für Version 1.4 weiter verwenden:

Alles in allem ist LightCore also eine kompakte, performante, flexible und ausgereifte Komponente, die für meine Projekte zukünftig die Antwort auf die Frage nach einem geeigneten Microkernel darstellt.

Auch weiterhin bleibt LightCore für mich also der Dependency Injection-Container meiner Wahl.

Anmut und Eleganz

Samstag, 1. Mai 2010, 09:37 Uhr
Permalink | Kommentare (9) | Kommentare als RSSRSS

Am 13. Oktober 2008 haben Peter Bucher und ich unter dem Titel Noch Fragen, Bucher? Ja, Roden! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. Mai 2010, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Anmut und Eleganz

So wohl Peter wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Peters Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

Anmut und Eleganz – zwei Begriffe der Ästhetik und der Schönheit. Laut Wikipedia wird Anmut als

Form des Schönen, […] der unwillkürliche Ausdruck einer Harmonie

definiert, wohingegen Eleganz als

Ausdruck von besonderem Stil und Geschmack in Design, Architektur, […]

gilt und nicht nur ein ästhetisches Konzept, sondern gar ein entsprechendes Ideal darstellt.

Intuitiv werden diese Begriffe nicht zwingend mit Code assoziiert, doch spielen sie auch in der Entwicklung eine entscheidende Rolle: Je eher Code eine gewisse Anmut und Eleganz ausstrahlt, desto eher folgt seine innere Form wohlüberlegten Gedanken.

In anderen Worten: Code, an dessen innerer Form gefeilt wurde, ist nicht nur wart- und evolvierbarer als über die Zeit emergierter Code – er folgt zudem einer eigenen Ästhetik: Der des im mathematischen Sinne Eleganten.

Jeder Entwickler, der in seiner Ausbildung mit Mathematik in Berührung gekommen ist, kennt den Term einer schönen Beweisführung. Der Term schön bedeutet in diesem Kontext, dass der Beweis elegant geführt und kompakt ist wie auch auf überflüssige Haken und Wendungen verzichtet wird.

Dies klingt zunächst nach dem Konzept der minimalistischen Architektur: Ein Entwurf ist dann gelungen, wenn kein weiterer Bestandteil aus dem Gesamtbild entfernt werden kann, ohne dass die Qualität oder die Funktionalität des Entwurfs darunter leidet.

Insofern liegt die Vermutung nahe, dass anmutiger und eleganter Code gleichzusetzen sei mit minimalistischem Code: Dass Code so lange reduziert, Ausdrücke vereinfacht und zusammengefasst, und aufwändige Konstrukte durch kompakte ersetzt werden sollten, bis eine minimalistische Form erreicht ist, die ohne Qualitäts- oder funktionale Einbußen nicht weiter reduziert werden kann.

Gilt also die Gleichung anmutiger und eleganter Code gleich minimaler, kompakter und reduzierter Code?

Während dieses Ziel in der Mathematik durchaus in dieser Form gelten könnte, lässt es einen wesentlichen Aspekt von Code aus: Im Gegensatz zu einem mathematischen Beweis, der – einmal erfolgt – nur noch zu Referenzzwecken nachvollzogen wird, wird Code immer und immer wieder gelesen.

Die gängige Wendung

It was hard to write, so it should be hard to read.

ironisiert diesen Aspekt. Fakt ist, dass Code im Idealfall nur ein Mal geschrieben, aber potenziell unzählige Male gelesen wird – nicht nur von anderen Entwicklern, auch vom ursprünglichen Autor: In der Regel gilt dies spätestens dann, wenn Code auf Grund neuer oder geänderter Anforderungen entweder erweitert oder zumindest angepasst werden muss.

Code sollte also für den Leser leicht verständlich sein. Hierzu trägt zunächst die äußere, optische Form von Code einen essenziellen Teil bei. Entsprechende Fragen lauten beispielsweise:

  • Tragen Bezeichner verständliche und beschreibende Namen, die semantisch zum Inhalt passen?
  • Erläutern Kommentare, aus welchen Gründen der kommentierte Code auf diese Art und nicht anders entwickelt wurde?
  • Hielt der Entwickler formale Kriterien wie Verwendung von Elementen wie Einrückung und Leerzeilen ein?
  • Folgen so wohl Code wie auch Kommentare und Dokumentation der gültigen Rechtschreibung und Grammatik?

Doch neben dieser äußeren Form spielt auch die innere Form für die Verständlichkeit eine bedeutende Rolle. Die entscheidende Frage hierzu lautet, ob dem jeweiligen Zweck angemessene Sprachkonstrukte angewandt wurden. Ausgewählte Beispiele dafür bieten folgende Aspekte:

  • Werden LINQ-Abfragen an Stelle von aufwändigen Schleifen mit zahlreichen, unter Umständen verschachtelten if-Anweisungen genutzt?
  • Wie werden Schnittstellen, abstrakte Basisklasse und konkrete Klassen miteinander kombiniert?
  • Werden Konstrukte wie beispielsweise das yield-Schlüsselwort dort eingesetzt, wo sie sinnvoll sind und Arbeit ersparen können?

Um diese Sprachkonstrukte angemessen einsetzen zu können, ist es erforderlich, sein Werkzeug zu beherrschen – sprich, die dargebotenen Mittel der persönlich gewählten Programmiersprache nicht nur zu kennen, sondern sie zu beherrschen.

Zur Verbesserung der inneren Form gibt es zahlreiche Regeln – bereits Werkzeuge zur statischen Codeanalyse wie FxCop oder die in Visual Studio integrierte Codeanalyse können hierbei eine wesentliche Hilfestellung leisten.

Je länger man sich jedoch als Entwickler mit diesen Themen beschäftigt und versucht, während der Entwicklung bewusst so wohl auf die innere wie auch auf die äußere Form von Code zu achten, desto eher stellt sich – auf Basis der nach und nach verinnerlichten Regeln – ein Gespür für ebenjene Anmut und Eleganz ein, die wart- und evolvierbaren Code auszeichnet.

Da dieses Gespür für ästhetisch gelungenen Code von handfesten Regeln getragen wird, besteht keine Gefahr, Initiativen wie beispielsweise Clean Code Developer zuwider zu handeln: Im Gegenteil – da dem Gespür für Ästhetik und Eleganz die Internisierung der Regeln zu Grunde liegt, erleichtert es letztlich den Umgang mit ebendiesen.

Eine Anmerkung in eigener Sache: Dieser Beitrag ist der – inzwischen achtzehnte – Kommentar im Rahmen von Noch Fragen, Bucher? Ja, Roden!. Unsere Reihe geht nun vorerst in eine Kreativpause, in der wir am Konzept und neuen Themen feilen werden. Auf absehbare Zeit werden wir diese Reihe fortsetzen.

Funktional oder objektorientiert?

Donnerstag, 1. April 2010, 09:37 Uhr
Permalink | Kommentare (8) | Kommentare als RSSRSS

Am 13. Oktober 2008 haben Peter Bucher und ich unter dem Titel Noch Fragen, Bucher? Ja, Roden! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. April 2010, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Funktional oder objektorientiert?

So wohl Peter wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Peters Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

Nicht-objektorientierten Sprachen wurde in den vergangenen Jahren eine deutlich höhere Aufmerksamkeit als davor zuteil: JavaScript als dynamische Sprache hat im Rahmen von AJAX daran ebenso Anteil gehabt wie F# als funktionale Sprache und Ableger von OCaml und ML.

Doch auch außerhalb von .NET haben beispielsweise Ruby im Rahmen von Rails und Python – jeweils als dynamische Sprachen – zunehmende Verbreitung erfahren. Inzwischen stehen mit IronRuby und IronPython auch entsprechende .NET-Vertreter dieser Sprachen auf Basis der Dynamic Language Runtime zur Verfügung.

Darüberhinaus führt der Tiobe-Index statische Sprache nur noch bei rund 60 Prozent, und auch der Anteil der objektorientierten Sprachen ist im Vergleich zum Vorjahr um knapp drei Prozent gefallen.

Auf den ersten Blick scheinen funktionale Sprachen also kräftig auf dem Vormarsch zu sein, ebenso wie dynamische Sprachen. Ist es also ratsam, sich mit diesen Sprachen auseinanderzusetzen?

Bevor man diese Frage beantworten kann, soll noch einmal deutlich darauf hingewiesen werden, dass funktionale Sprachen ein anderes Paradigma verfolgen als objektorientierte Sprachen – nämlich das funktionale an Stelle des objektorientierten Paradigmas – , dynamische Sprachen aber nicht per se: Während sich das funktionale von funktionalen Sprachen auf die Art der Entwicklung bezieht, weisen dynamische Sprachen einfach nur ein flexibles Typsystem auf.

Das bedeutet, dass eine dynamische Sprache sehr wohl objektorientiert sein kann, aber nicht muss, und dass eine funktionale Sprache nicht zwingend dynamisch sein muss – aber kann.

Über die Bedeutung von dynamischen Sprachen haben Peter und ich uns schon in unserem ersten Streitgespräch unter dem Titel Dynamic Language Runtime: .NET, quo vadis? Gedanken gemacht – funktionale Sprachen bislang allerdings noch nicht. Spätestens seit F# als neue Sprache in Visual Studio 2010 enthalten ist, wird es aber Zeit, sich die Frage zu stellen, inwiefern sich die Beschäftigung mit funktionalen Sprachen lohnt.

Liest man die Definition von funktionaler Programmierung, die in Wikipedia gegeben wird, ist die Nähe zur Mathematik unübersehbar. Auch die Sprachkonstrukte von F#, wie beispielsweise explizite Unterstützung von Rekursion, die Unterstützung von Funktionen höherer Ordnung und die Fähigkeiten zur Listenverarbeitung und –manipulation, legen nahe, dass der Fokus von F# auf der Verarbeitung von Datenstrukturen liegt.

Da für funktionale Sprachen die Turing-Vollständigkeit bewiesen wurde, ist klar, dass sich jedes programmatisch lösbare Problem auch in einer funktionalen Sprache lösen lässt – die Frage ist nur, wie elegant: Natürlich können grafische Oberflächen auch in funktionalen Sprachen entwickelt werden – wirklich Spaß macht das aber nicht. Hierbei merkt man allzu häufig, dass F# andere Schwerpunkte setzt.

So gesehen stellt F# also eine hochspezialisierte und äußerst gut geeignete Sprache für algorithmische Probleme dar – als Allzwecksprache für den tagtäglichen Einsatz eignet sie sich jedoch weniger. Andere funktionale Sprache verhalten sich in dieser Eigenschaft ähnlich wie F#.

Ein interessanter Aspekt ist zudem, dass das objektorientierte Paradigma mehr bietet als nur eine Art, wie Code geschrieben wird. Die Objektorientierung gibt von Haus aus nämlich außerdem bereits ein hierarchisches Modell vor, wie Code organisiert wird – in Methoden, Klassen und Namensräume, die auf Grund von Vererbung und Komposition miteinander in Beziehung stehen und durch abstrakte Basisklassen und Schnittstellen mit Kontrakten versehen werden können.

Funktionaler Programmierung fehlt ein solcher Ansatz: Hier ist schlichtweg alles eine Funktion – was auf der einen Seite gerade die Stärke funktionaler Programmierung begründet, auf der anderen Seite aber auch viel mehr Disziplin und die Existenz eines eigenen Ordnungssystems erfordert.

So gesehen kann man mit Fug und Recht behaupten, dass objektorientierte Sprachen für den tagtäglichen Einsatz die geeigneteren Sprachen sind, da sie universeller und besser strukturierend sind. Für algorithmische Spezialaufgaben mag es aber durchaus lohnenswert sein, eine funktionale Sprache zur Problemlösung in Betracht zu ziehen.

Insofern fällt mein Fazit daher ähnlich wie zu den dynamischen Sprachen aus: Die Koexistenz beider Welten dürfte .NET insgesamt voranbringen, da es besser möglich wird, das am besten geeignete Werkzeug für die jeweilige Aufgabe zu wählen, ohne eine ausgereifte Plattform wie .NET verlassen zu müssen.

Abstraktion

Montag, 1. März 2010, 09:37 Uhr
Permalink | Kommentare (1) | Kommentare als RSSRSS

Am 13. Oktober 2008 haben Peter Bucher und ich unter dem Titel Noch Fragen, Bucher? Ja, Roden! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. März 2010, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Abstraktion

So wohl Peter wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Außerdem nimmt diesen Monat auch Roberto Bez an unserem Streitgespräch teil.

Peters und Robertos Kommentare finden sich zeitgleich in den entsprechenden Blogs, folgend nun mein Kommentar zu diesem Thema:

Das Selbstverständnis der Informatik, anderen Fachgebieten eine Hilfswissenschaft zu sein, impliziert die Anwendung von Abstraktion bereits: Letztlich werden deren Objekte, Daten und Prozesse nämlich in geeinete informationstechnische Strukturen abstrahiert.

Abstraktion an sich wird dabei beispielsweise von Wikipedia als

induktiven Denkprozess des Weglassens von Einzelheiten und des Überführens auf etwas Allgemeineres oder Einfacheres

definiert. Es geht also letztlich darum, gegebene Probleme besser lösen zu können, indem für die Lösung unnötige Komplexität verringert wird – in der Regel, indem diese durch eine einfachere Darstellung verborgen wird.

Bereits im schulischen Mathematikunterricht findet Abstraktion in dieser Form statt, wenn beispielsweise die Lösung von Textaufgaben gefordert wird, die die eigentliche mathematische und abstrakte Aufgabe in einer mehr oder weniger anschaulichen Beschreibung verpacken.

Ohne Abstraktion wäre die Lösung mancher Aufgaben nicht nur bedeutend schwieriger und komplexer oder gar unmöglich, ohne sie wäre auch die Kommunikation mit anderen Disziplinen bedeutend erschwert.

Für einen Informatiker spielt es in der Regel nämlich zunächst keine Rolle, ob zum Beispiel Gepäckstücke an einem Flughafen oder aber Reagenzgläser in einem medizinischen Labor automatisiert befördert und verarbeitet werden sollen – beide Aufgaben können zu einem Logistikproblem abstrahiert werden, das mit entsprechenden Verfahren lösbar oder zumindest annäherbar ist.

Abstraktion nimmt also eine essenzielle Rolle in der Informatik ein – doch Abstraktion ist nicht per se als positiv zu bewerten. Abstraktionen verbergen nämlich nicht nur unnötige Komplexität, sondern neigen bewusst oder unbewusst auch dazu, Details zu verbergen, die für die Lösungsfindung allerdings relevant sind.

Joel Spolsky bezeichnet diesen Umstand als The Law of Leaky Abstractions. Er fasst seine Erkenntnisse bezüglich Abstraktion in dem Satz

All non-trivial abstractions, to some degree, are leaky.

zusammen, was letztlich so viel bedeutet, dass Abstraktionen nicht nur dabei helfen, Probleme zu lösen, sondern ihrerseits lecken und neue Probleme verursachen, weil die notwendigen Details nicht mehr bekannt sind – ironischerweise desto eher, je stärker abstrahiert wird.

Beispiele für Abstraktionen und die dazugehörigen Lecks sind – wie in der gesamten übrigen Entwicklung von Software – auch in .NET ohne weiteres zu finden:

So abstrahiert die Garbage Collection von .NET die Notwendigkeit, Speicher selbst zu verwalten – dies geschieht implizit. Doch sie löst nicht alle Probleme, zudem verursacht sie neue Probleme, derer sich Entwickler zumindest in kleinen Anwendungen nicht bewusst werden.

Denn nach wie vor haben Entwickler mit hängenden Referenzen zu kämpfen, die beispielsweise dadurch entstehen, dass Ereignisse nicht auch wieder abgemeldet werden.

Ebenfalls problematisch ist das vermeintlich günstige Erzeugen neuer Objekte im Verlass darauf, dass die Garbage Collection die erzeugten Objekte wieder entfernen wird – Speicherverschwendung und damit Performanceprobleme auf Grund falsch angewandter Instanziierung sind eher die Regel denn die Ausnahme.

Auch der grafische, in Visual Studio enthaltene Designer für WPF stellt eine solche Abstraktion dar: Er entledigt den Entwickler von der lästigen Aufgabe, WPF-Code per Hand schreiben zu müssen – kann aber natürlich nie die wahre Absicht des Entwicklers erahnen und muss daher gewisse Kompromisse zwischen Generalität und vernünftigem, das heißt performanten und wohlstrukturierten Code eingehen.

Last but not least stellt selbst der Konkatenationsoperator für Zeichenketten, dargestellt durch ein vermeintlich harmloses +, eine Abstraktion über die Vorgänge dar, die im Speicher stattfinden, um zwei Zeichenketten miteinander zu einer neuen zu verbinden. Die Speicher- und Performanceimplikationen dürften hinlänglich bekannt sein.

All diese Probleme werden durch Abstraktionen verursacht, die ursprünglich angetreten sind, bestehende Probleme zu lösen. Natürlich kann man auf Abstraktionen auch nicht verzichten – zu essenziell sind sie für die überschaubare Lösbarkeit eines gegebenen Problems.

So erspart beispielsweise ASP.NET als Abstraktion über das HTTP-Protokoll – das seinerseits übrigens nichts anderes als eine weitere Abstraktion über den Datenverkehr im Netzwerk darstellt – dem Entwickler eine ganze Menge an Aufgaben, die in der Regel ohnehin immer nach dem gleichen Schema erledigt werden.

Doch – wer nicht weiß, was bei ASP.NET unter der Haube geschieht, neigt schnell dazu, die vorgegebenen Pfade als das Nonplusultra anzusehen und dabei auftretende Probleme zu ignorieren, schlichtweg, weil sie auf Grund der Abstraktion nicht wahrgenommen werden.

Das Paradoxe an dieser Situation ist, dass es – je mehr und mächtigere Abstraktionen verfügbar sind, desto schwieriger wird es, zu wissen, was man eigentlich macht. Damit schließe ich mich nahtlos Joel Spolsky an, wenn er in seinem eingangs erwähnten Artikel schreibt:

[…] all this means that paradoxically, even as we have higher and higher level programming tools with better and better abstractions, becoming a proficient programmer is getting harder and harder.

Schlussendlich lässt sich zusammenfassen, dass es für hervorragende Entwickler nicht genügt, sich mit ihrer jeweils präferierten Technologie auseinanderzusetzen, sondern dass sie sich – um wirklich verstehen und nachvollziehen zu können, was geschieht – auch mit den zu Grunde liegenden Technologien auseinandersetzen müssen.

Das bedeutet, dass auch – oder erst recht – in Zeiten von Hochsprachen, Frameworks, Designern und Assistenten die Berechtigung oder gar die Pflicht gegeben ist, sich mit den Grundlagen zu beschäftigen.

Dass dies nicht zuletzt auch eine große Herausforderung für die Ausbildung darstellt, sei es in Firmen, an Hochschulen oder in sonstigen Formen, liegt auf der Hand.

Werden die Grundlagen nämlich zu Gunsten schnellerer und vermeintlich besserer Lernerfolge systematisch ignoriert, bedeutet das, Oberflächlichkeit zu fördern und verstehendes Wissen unter Anwendung der Root Cause Analysis, die unter anderem auch von der Clean Code Developer-Initiative wertgeschätzt wird, zu missachten.

LightCore signiert

Dienstag, 9. Februar 2010, 22:39 Uhr
Permalink | Kommentare (1) | Kommentare als RSSRSS

Am 1. Januar 2010 hat Peter Bucher die erste Version seines neu entwickelten Microkernels LightCore veröffentlicht. Seitdem sind bereits wieder einige Features hinzugekommen, die allerdings derzeit nur in der Entwicklerversion zur Verfügung stehen, die als Quellcode aus dem zugehörigen Subversion-Repository abgerufen werden kann.

Nach wie vor halte ich LightCore für einen äußerst bemerkenswerten Microkernel und Dependency Injection-Container, auch die intensive Nutzung hat meiner Begeisterung keinen Abbruch getan – im Gegenteil.

Und dennoch – es gibt eine Kleinigkeit, die mich bislang gestört hat: Peter stellt LightCore lediglich als nicht-signierte Assemblies bereit. Das bedeutet, dass jeder, der wie ich eine signierte Variante benötigt, sich zunächst den Quellcode herunterladen, ihn anpassen und neu übersetzen muss.

Peter und ich haben daher beschlossen, dass ich zukünftig den jeweils aktuellen Stand von LightCore als signierte Version zur Verfügung stellen werde. Signiert sind dabei die folgenden drei Assemblies:

  • LightCore.dll
  • LightCore.Configuration.dll
  • LightCore.Integration.Web.dll

Nicht signiert ist die LightCore.CommonServiceLocator.dll, da diese von einer zumindest derzeit ebenfalls nicht signierten Assembly von Microsoft abhängt. Da es in .NET aus Sicherheitsgründen nicht möglich ist, aus einer signierten Assembly auf eine nicht-signierte Assembly zu verweisen, kann die LightCore.CommonServiceLocator.dll vorerst nicht signiert werden.

Der Download der signierten Variante von LightCore findet sich als ZIP-Archiv unter http://www.goloroden.de/Downloads/LightCore.aspx, wobei darin ebenfalls die nicht-signierte vierte Assembly enthalten ist.

In den nächsten Tagen wird dieser Link dann auch auf der offiziellen Webseite von LightCore verfügbar sein, so dass auch zukünftig alle verfügbaren und offiziellen Downloads dort aufgelistet sind.

Felder vs Eigenschaften

Montag, 1. Februar 2010, 09:37 Uhr
Permalink | Kommentare (3) | Kommentare als RSSRSS

Am 13. Oktober 2008 haben Peter Bucher und ich unter dem Titel Noch Fragen, Bucher? Ja, Roden! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. Februar 2010, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Felder vs Eigenschaften

So wohl Peter wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Peters Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

Im Gegensatz zu klassischen Sprachen wie C++ oder Java verfügt C# seit der ersten Version über das Konzept der Eigenschaften: Ein für den Entwickler leichtgewichtiges und syntaktisch komfortables Konzept zum kontrollierten Zugriff auf Felder.

Gerade im Vergleich zu den in den genannten Sprachen üblichen zahlreichen Get- und Set-Methoden, die zum Zugriff auf Felder notwendig sind, bieten Eigenschaften einen enormen Vorteil, insbesondere im Hinblick auf die einfachere Lesbarkeit und damit auch bessere Verständlichkeit von Code.

Allerdings wird – beispielsweise in reinen Datenklassen – für den Zugriff auf Felder kein aufwändiger Zugriff benötigt, so dass die Definition einer Eigenschaft in der Regel zum einen immer nach dem gleichen Schema abläuft, zum anderen aber auch syntaktisch aufwändig und redundant ist.

Diesem Umstand trägt C# seit der Version 3.0 mit automatisch implementierten Eigenschaften Rechnungen, wodurch bereits deutlich weniger Code zu schreiben ist – allerdings immer noch deutlich mehr, als für ein Feld, das mit dem Zugriffsmodifizierer public ausgestattet wird.

Nun könnte man meinen, dass man an Stelle einer ohnehin leeren Eigenschaft durchaus ein öffentliches Feld verwenden könnte – doch es gibt einige Gründe, warum man dies unterlassen sollte:

  • MSIL-Code: Felder und Eigenschaften werden in unterschiedlichen MSIL-Code übersetzt, sind also binär nicht kompatibel zueinander. Im Nachhinein lässt sich ein Feld also nicht ohne weiteres durch eine gleichnamige Eigenschaft ersetzen, ohne abhängigen Code – beispielsweise in anderen Assemblies – zu brechen.
  • Referenzparameter: Felder können im Gegensatz zu Eigenschaften einer Methode als Referenzparameter übergeben werden. Deshalb kann ein Ersetzen eines Feldes durch eine Eigenschaft dazu führen, dass Methodenaufrufe nicht mehr kompiliert werden können, bei denen die Übersetzung zuvor ohne weiteres möglich war.
  • Reflection: Die Arbeit mit Feldern und Eigenschaften gestaltet sich per Reflection unterschiedlich, so dass ein Ersatz eines Feldes durch eine Eigenschaft ebenfalls wiederum abhängigen Code brechen könnte.
  • OOP: Es widerspricht dem objektorientierten Konzept der Informationskapselung, Felder als nicht-private zu markieren.
  • Debugger: Eigenschaften ermöglichen das Setzen eines Haltepunktes beim Auslesen beziehungsweise Schreiben, für Felder ist dies nicht möglich.
  • Datenbindung: Eigenschaften können im Gegensatz zu Feldern für die Datenbindung an Steuerelemente herangezogen werden.

Kurzum: Felder sollten ausschließlich mit dem Zugriffsmodifizierer private versehen werden, für alle anderen Anforderungen sind Eigenschaften in jedem Fall die bessere Wahl.

this oder kein this

Freitag, 1. Januar 2010, 09:37 Uhr
Permalink | Kommentare (6) | Kommentare als RSSRSS

Am 13. Oktober 2008 haben Peter Bucher und ich unter dem Titel Noch Fragen, Bucher? Ja, Roden! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. Januar 2010, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

this oder kein this

So wohl Peter wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Peters Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

Zumeist sind Schlüsselwörter innerhalb einer Programmiersprache mit einer eindeutigen Semantik belegt; auch C# stellt keine Ausnahme dieser Regel dar. Jedoch gilt dies zumindest in C# wohlgemerkt nicht für alle Schlüsselwörter, denn einige haben – je nach Kontext – verschiedene Bedeutungen.

Ein bekanntes und weit verbreitetes Beispiel hierfür ist das Schlüsselwort using, das so wohl als Direktive wie auch als Anweisung verwendet werden kann:

  • Als Direktive dient es zunächst dazu, andere Namensräume bekannt zu machen, so dass die darin enthaltenen Typen ohne Angabe ihres vollqualifizierten Namens genutzt werden können.
  • Die Direktive dient zusätzlich jedoch auch der Bildung von Aliasnamen, wobei dies wiederum so wohl für Namensräume wie auch für einzelne Typen möglich ist.
  • Als Anweisung schließlich definiert using in Verbindung mit der IDisposable-Schnittstelle einen Gültigkeitsbereich, an dessen Ende automatisch die Dispose-Methode des gekapselten Objekts aufgerufen wird.

Doch nicht nur using ist mit verschiedenen Bedeutungen belegt – ein weiterer Kandidat mit zahlreichen Varianten ist das this-Schlüsselwort:

  • In Verbindung mit Konstruktoren dient das this-Schlüsselwort dazu, den Aufruf an einen überladenen Konstruktor der gleichen Klasse weiterzuleiten.
  • Mit this ist es zudem möglich, Indexer zu definieren – also Eigenschaften, die über keinen eigenen Namen verfügen, allerdings parametrisiert mit einem Index aufgerufen werden können.
  • Seit C# 3.0 dient this als Kennzeichner für Erweiterungsmethoden, indem mit Hilfe dieses Schlüsselworts festgelegt wird, welches Argument dem aufrufenden Objekt entspricht.
  • Schließlich dient this – wie bereits in C++ und Java – auch als Referenz auf das eigene Objekt. Auf diese Art kann auf Elemente der Klasse zugegriffen werden, deren Namen durch gleichnamige Parameter ansonsten ausgeblendet und damit nicht zugreifbar wären.

Im Gegensatz zu using, dessen Angabe immer erforderlich ist, kann this gegebenenfalls entfallen: Während seine Verwendung bei der Verkettung von Konstruktoren, Indexern und Erweiterungsmethoden obligatorisch ist, kann die Eigenreferenz entfallen – sofern der Name des betroffenen Elements nicht ausgeblendet wird.

Die Frage lautet allerdings, ob es sinnvoll ist, this in den optionalen Fällen zu streichen. Zunächst scheint es so, schließlich erspart man sich einige Zeichen zu tippen, zu lesen und vermeidet unnötige Redundanz. Auch ReSharper empfiehlt bei Verwendung der Standardeinstellungen, redundante this-Schlüsselwörter zu entfernen.

Neben diesen Vorteilen birgt das Entfernen von this jedoch auch einen essenziellen Nachteil: Es ist nicht mehr auf einen Blick ersichtlich, ob ein Zugriff auf ein Element des aktuellen Objekts stattfindet. So ist bei der Zeile

_foo = new Foo();

nicht klar, ob es sich um eine statisches Feld oder ein Instanzfeld handelt. Wird das Schlüsselwort this jedoch konsequent verwendet, ist auf den ersten Blick eindeutig, dass es sich um ein statisches Feld handelt, ansonsten müsste die Zeile nämlich

this._foo = new Foo();

lauten. Während diese Mehrdeutigkeit bei Feldern noch zu vertreten sein mag, wird es bei Eigenschaften bedeutend schwieriger: So bestehen für die Zeile

Foo.Bar();

bereits drei Möglichkeiten, welcher Aufruf sich dahinter verbirgt: Zum einen könnte Foo eine Eigenschaft sein, an der eine Methode namens Bar aufgerufen wird. Es ist jedoch nicht eindeutig, ob es sich bei der Eigenschaft um eine statische oder eine instanzbehaftete Eigenschaft handelt.

Neben diesen zwei Möglichkeiten könnte es zudem sein, dass Foo gar keine Eigenschaft, sondern eine statische Klasse bezeichnet, die ihrerseits wiederum eine Methode Bar() enthält.

Der Unterschied zwischen einer statischen Eigenschaft und einer statischen Klasse lässt sich ohne weiteres nicht auflösen – zumindest der erste Fall kann aber durch Verwendung von this explizit gemacht werden. Bei der Zeile

this.Foo.Bar();

ist nämlich auf den ersten Blick ersichtlich, dass eine Methode an einer Eigenschaft aufgerufen wird, die instanzbehaftet ist.

Diese Beispiele zeigen, dass die konsequente Verwendung von this durchaus ihren Teil zu einer besseren Verständlichkeit des Quellcodes beitragen kann.

Da Code in der Regel derart geschrieben wird, dass er für die Zukunft wie auch für andere Entwickler gut lesbar ist, kann es durchaus eine vernünftige Entscheidung sein, this generell zu verwenden – insbesondere auch in den redundanten Fällen, um die Semantik des Codes explizit zu machen.

Für welche Variante auch immer ein Entwickler beziehungsweise ein Team sich entscheidet, wichtig ist – wie so oft, wenn es um das Thema Coderichtlinien geht – dass die einmal getroffene Entscheidung konsequent eingehalten wird. Alles andere führt über kurz oder lang zu Missverständnissen.

LightCore

Freitag, 1. Januar 2010, 00:00 Uhr
Permalink | Kommentare (1) | Kommentare als RSSRSS

Vor ungefähr neun Monaten habe ich in meinem Blogeintrag Microkernel im Eigenbau beschrieben, wie mit wenigen Zeilen ein eigener Microkernel unter .NET realisiert werden kann. Auch wenn diese Variante durchaus für einfache Szenarien genügt, bestehen in der Praxis häufig weitere Anforderungen.

Da in der Regel kein eigener Microkernel entwickelt werden soll, bleibt die Wahl der Qual, welcher Microkernel zum einen die eigenen Anforderungen erfüllt und zum anderen aber zugleich dem persönlichen Gusto am ehesten entspricht. Erschwert wird die Wahl dabei durch die Vielzahl der verfügbaren Systeme.

Wie viele andere Entwickler konnte auch ich mich in der Vergangenheit nicht eindeutig für ein einziges System entscheiden:

  • So hat mir autofac zwar mit seinem an C# 3.0 angelehnten Programmiermodell mit umfangreicher Nutzung von Lambdaausdrücken zugesagt, nicht jedoch von der Performance.
  • Unity hingegen gefällt mir ebenfalls von dem verwendeten Programmiermodell, jedoch habe ich mit den Microsoft Application Blocks in der Vergangenheit keine all zu guten Erfahrungen gemacht, weshalb ich bei Unity sehr skeptisch bin.
  • Spring.NET ist als Framework empfehlenswert, wenn nicht nur ein Microkernel, sondern auch Logging, O/R-Mapping, aspektorientierte Funktionen und ähnliches gewünscht werden – für alles andere ist es jedoch zu umfangreich und komplex.

Diese Liste könnte ich noch problemlos fortsetzen und um einige weitere Microkernel ergänzen.

Mit LightCore hat Peter Bucher nun einen weiteren Microkernel entwickelt, der zudem als Dependency Injection-Container dient. LightCore ist vollständig in C# geschrieben, folgt dabei den Richtlinien der Clean Code Developer-Initiative und ist weitestgehend mit Unittests abgedeckt.

Für sich genommen wecken diese Aspekte durchaus Interesse an dem zu Grunde liegenden Code – doch wirklich interessant wird LightCore durch seine Eigenschaften beziehungsweise Fähigkeiten, die LightCore von anderen Microkerneln abheben:

  • LightCore ist leichtgewichtig und besteht im Wesentlichen aus einer 27 KByte großen Assembly. Je nach Anforderungen kommen noch einige Assemblies hinzu, doch über 50 KByte wächst der Platzbedarf in keinem Fall.
  • LightCore ist verdammt schnell: Im Vergleich zu nativem .NET-Code ist die Erzeugung von 100.000 Objektinstanzen in LightCore lediglich um 7% langsamer – autofac bedarf dafür hingegen ein Vielfaches der Zeit.
  • LightCore ist einfach und flexibel zu konfigurieren: Zum einen kann die Konfiguration vollständig im Code erfolgen, zum anderen wird XAML als Konfigurationssprache unterstützt, wobei nahezu beliebige Datenquellen genutzt werden können.
  • LightCore unterstützt die Instanziierung von Objekten nicht nur per Reflection, sondern auch per Lambdaausdruck, wodurch die Performance im Vergleich zu der Reflection-basierten Variante deutlich zunimmt.
  • LightCore kann mit verschiedenen Lebenszyklen von Objekten umgehen: Transiente Objekte können dadurch ebenso erzeugt werden die Singletons, threadaffine Singletons und HttpRequest-bezogene Objekte.
  • LightCore ermöglicht die Registrierung von Typen an Hand eines Kontrakts, eines Namens oder einer Klasse. Zusätzlich können Argumente spezifziert werden, die während der Instanziierung an den jeweiligen Konstruktor übergeben werden.
  • LightCore unterstützt die Gruppierung von Registrierungen, so dass auf einfache Art zwischen verschiedenen Konfigurationen von Registrierungen gewechselt werden kann.
  • LightCore integriert sich in klassisches ASP.NET und ASP.NET MVC, so dass Dependency Injection auch in diesen Technologien ohne zusätzlichen Aufwand genutzt werden kann.
  • LightCore unterstützt das CommonServiceLocator-Projekt, das seinerseits eine Abstraktion über verschiedene Microkernel ermöglicht.

Dieser umfangreichen und bemerkenswerten Featureliste zum Trotz verfügt LightCore über ein ausgesprochen schlichtes API, so dass zur tatsächlichen Verwendung nur einige wenige Zeilen Code erforderlich sind.

Alles in allem ist LightCore also eine kompakte, performante, flexible und ausgereifte Komponente, die für meine Projekte zukünftig die Antwort auf die Frage nach einem geeigneten Microkernel darstellt.

Für die Zukunft wünsche ich LightCore daher alles Gute.

Reflection – Fluch oder Segen?

Dienstag, 1. Dezember 2009, 09:37 Uhr
Permalink | Kommentare (1) | Kommentare als RSSRSS

Am 13. Oktober 2008 haben Peter Bucher und ich unter dem Titel Noch Fragen, Bucher? Ja, Roden! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. Dezember 2009, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Reflection – Fluch oder Segen?

So wohl Peter wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Peters Kommentar findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:

Reflection ist ein zentrales Element in .NET: Die Tatsache, dass in MSIL eben nicht nur der eigentliche Code, sondern auch die dazugehörigen Metadaten enthalten sind, zeigt dies. Und – das ist auch gut so.

Denn Reflection bildet die Grundlage für zahlreiche Szenarien, die von vielen Entwicklern tagtäglich genutzt werden: Sei es der Einsatz eines Microkernels und damit verbunden das dynamische Laden und späte Instanziieren von Typen, sei es der Einsatz einer dynamischen Sprache oder der dynamischen Sprachfeatures von C# 4.0. All dies basiert letztlich auf Reflection.

Auch die Verwendung von Attributen zur Laufzeit wäre ohne Reflection nicht möglich, ebensowenig wie die Analyse von Assemblies, um an Hand der enthaltenen Typen und deren Methoden, Eigenschaften und Feldern gewisse Entscheidungen zu treffen.

Reflection ist also fraglos ein Segen – ohne sie wären all die genannten Punkte in .NET nicht oder zumindest nicht mit dermaßen geringem Aufwand möglich. Doch woher stammt dann der schlechte Ruf, der in Gesprächen über Reflection zugegebenermaßen häufig den Unterton bestimmt?

Prinzipiell sind gleich zwei Gründe für den schlechten Ruf von Reflection verantwortlich, wobei einer der beiden systemimmanent ist, der zweite jedoch zumindest teilweise nur die konkrete Implementierung in .NET betrifft:

  • Reflection ermöglicht den wahlfreien Zugriff auf Code, auch auf geschützte Elemente wie beispielsweise Variablen, die als private gekennzeichnet wurden.
  • Reflection ist langsam – so wohl die Analyse von Code wie auch dessen Ausführung betreffend.

Dass Reflection den wahlfreien Zugriff auf beliebigen Code ermöglicht, ist Fluch und Segen zugleich: Auf der einen Seite wird auf diese Art sämtliche objektorientierte Programmierung ausgehebelt, auf der anderen Seite kann es ungemein nützlich sein, von außen auch auf private Elemente zugreifen zu können – man denke nur an diverse Unittests.

Nebenbei bemerkt: Einen interessanten Ansatz, wie Unittests ohne den Einsatz von Reflection auskommen und dennoch Interna testen können, hat Ralf Westphal in seinem Blogeintrag Zustand als Abhängigkeit beschrieben.

Generell gilt für den Einsatz von Reflection zum Zugriff auf geschützte Elemente also die gängige Empfehlung: So viel wie nötig und so wenig wie möglich – mit Betonung auf zweiterem.

Doch all dies wohnt Reflection bereits vom Ansatz her inne: Dabei spielt es keine Rolle, ob es sich um Reflection auf Basis von .NET, Java oder einer beliebigen anderen Plattform handelt: Diese Nachteile und die damit einhergehenden Überlegungen gelten generell.

Mit dem Umstand, dass Reflection unter .NET ausgesprochen langsam ist, sieht es jedoch ein wenig anders aus: Reflection an sich ist immer langsamer als statisch kompilierter Code – das ist logisch, wenn man weiß, wie beispielsweise Aufrufe von Funktionen in beiden Fällen funktionieren: Der Lookup per Reflection kostet schlicht und ergreifend mehr Zeit.

Doch Reflection kann auch dazu genutzt werden, um Code zu analyiseren – und hier ist es tatsächlich die Implementierung von .NET, welche die Schuld dafür trägt. Zudem zeichnet sich die Reflection von .NET in diesem Zusammenhang auch dadurch aus, wenig flexibel zu sein.

So ist es beispielsweise nicht möglich, aus einer auf dem Desktop ausgeführten .NET-Anwendung die Assembly mscorlib.dll von Silverlight zu öffnen – es kann nämlich jeweils nur eine Instanz der mscorlib.dll in einen Prozessraum geladen werden, was die Analyse einer anderen als der eigenen mscorlib.dll unter .NET immer scheitern wird.

Abhilfe – so wohl für die Geschwindigkeit wie auch für die Flexibilität – schafft der Einsatz einer anderen Komponente für Reflection, wie beispielsweise Mono Cecil, die unter anderem auch von Werkzeugen wie dem Mono Migration Analyzer oder dem Mono Debugger eingesetzt wird.

Zusammenfassend kann man also sagen, dass Reflection per se so wohl Fluch wie auch Segen ist – und dass es vom konkreten Kontext abhängt: Für Latebinding, dynamische Sprachen und Analyse von Code ist Reflection unverzichtbar, doch für den darüber hinausgehenden Einsatz von Reflection sollte man sehr gute Gründe haben.

Ein Ordnungssystem für Unittests

Sonntag, 22. November 2009, 07:06 Uhr
Permalink | Kommentare (2) | Kommentare als RSSRSS

Auf den Tag drei Wochen sind vergangen, seit Ralf Westphal mich in Reaktion auf den Blogeintrag Wie viel Sinn machen Unittests? ermuntert hat, mich nochmals intensiv mit diesem Thema auseinanderzusetzen:

Unit Tests […] konsequent und kompetent mal für einen Monat einzusetzen. Mit der Kompetenz mag es da schwierig sein, weil man sich dann eine Veränderung der Handlungs- und Denkgewohnheit selbst beibringen muss... aber das ist zumindest ein Anfang.

Ich bin seinem Rat gefolgt und habe bemerkt, dass die wesentlichen Probleme für mich nicht technischer, sondern gedanklicher Natur waren. Zu viele unbeantwortete Fragen standen im Raum – Fragen, die zunächst einfach erscheinen, deren Beantwortung aber viel Nachdenken voraussetzt.

Man könnte mein Problem als die Herausforderung bezeichnen, ein tragfähiges Konzept für Unittests zu finden, das sich nahtlos in meine bisherige Arbeitsweise integriert, sie zwar erweitert, aber nicht von Grund auf umkrempelt.

Bemerkenswert fand ich, dass die wenigsten Entwickler, die ich mit diesen Fragen konfrontiert habe, ihre Antworten fundiert begründen konnten. Einige haben sich sogar daran gestört, dass ich diese Fragen überhaupt stelle, und haben eher dazu geraten, einfach loszulegen – frei nach dem Motto: TDD ist keine Wissenschaft!

Doch ohne fundierte Antworten auf grundlegende Fragen zu haben, die den eigenen Wissensdurst stillen, verbleibt immer ein ungutes Gefühl – ob das, was man macht, tragfähig für zukünftige Erweiterungen sein wird. Diese zukünftige Tragfähigkeit bedeutet mir sehr viel – anders wäre ich vermutlich auch von Extreme Programming nicht so begeistert.

Außerdem ist die Beantwortung solcher Fragen entscheidend, um klare Regeln und Richtlinien zu haben, auf deren Basis man Entscheidungen treffen kann: Je genauer diese Regeln und Richtlinien formuliert werden, desto größer wird die persönliche Liebe zum Detail, desto besser wird die Accuracy – meines Erachtens eine essenzielle Eigenschaft eines guten Enwicklers.

Die erste dieser Fragen, ob Code zum Zwecke einer besseren Testbarkeit angepasst werden darf, wurde bereits in Auch Kirchen haben Kragsteine diskutiert: Wie so oft ist es Ralf Westphal gelungen, eine anschauliche Analogie zur realen Welt zu finden, die das digitale Problem schlagartig löst.

Die zweite dieser Fragen, wie Unittests sinnvoll und übersichtlich organisiert werden können, wurde inzwischen auch beantwortet: Auch hierzu hat Ralf Westphal einen wesentlichen Teil beigetragen, aber auch Neno Loje, Bernd Marquardt und Peter Bucher haben mein nun gefundenes System beeinflusst.

Die grundlegende Erkenntnis ist zunächst, dass sich eine Testklasse nicht zwingend auf eine zu testende Klasse beziehen muss – dass also nicht zwingend eine 1:1-Beziehung zwischen beiden vorliegt. Statt dessen bezieht sich eine Testklasse auf ein sogenanntes System under test (SUT). Ein solches SUT kann durchaus eine Klasse sein – genausogut kann es aber auch eine einzelne Methode sein.

Die zweite Erkenntnis ist, dass ein Unittest ein SUT immer in einem gegebenen Kontext testet. Bei einer Methode ist das fast immer der Aufruf derselben, bei einer Klasse hingegen kann es verschiedene Ausgangssituationen geben: Ein Kontext wäre das Initialisieren der Klasse, ein weiterer die Arbeit mit der gerade initialisierten Klasse, wieder ein weiterer das Disposen einer bereits verwendeten Klasse.

Da sich in der Regel mehrere Unittests auf einen solchen Kontext beziehen, macht es durchaus Sinn, diese in einer Testklasse zusammenzufassen, deren Name den Kontext beschreibt, und deren Namespace den Platz des SUTs angibt.

Auf diese Art könnten Unittests, die den Aufruf der Methode ToDictionary in der Klasse goloroden.de.Common.ExtensionMethods testen, beispielsweise in der Testklasse

  • goloroden.de.Tests.Common.ExtensionMethods.WhenToDictionaryIsCalled

abgelegt werden. Die einzelnen Unittests beschreiben dann mit ihrem Namen innerhalb dieser Klasse nur noch die übergebenen Parameter sowie das erwartete Ergebnis:

  • WithNull_AnArgumentNullExceptionIsThrown()
  • WithAnEmptyString_AnArgumentExceptionIsThrown()

Der Vorteil dieser Terminologie liegt klar auf der Hand: Schlägt ein Unittest fehl, lassen sich aus Testklasse und –methode auf einen Blick alle relevanten Informationen ablesen: Der Aufruf welcher Codestelle verursacht in welchem Kontext mit welchen Parametern ein Problem?

Zudem fördert diese Terminologie das Prinzip von TDD, pro Unittest nur einen einzelnen Aspekt zu testen – versucht man, mehrere Aspekte in einem Unittest zusammen zu fassen, führt dies unweigerlich zu unhandlichen Methodennamen.

Nachdem ich auf dieser Basis nun eine Reihe von Unittests geschrieben habe, bemerke ich, dass sie mir bereits sehr ans Herz gewachsen sind. Interessant fand ich vor allem, dass ich allein durch das nachträgliche Schreiben von Unittests einen Fehler in einer zehnzeiligen Methode entdeckt habe, der trotz dem häufigen Einsatz dieser Methode in den vergangenen zwölf Monaten nicht aufgefallen ist.

Allein das gute Gefühl, zukünftig Änderungen und Erweiterungen an dieser Methode durchführen zu können, ohne Angst haben zu müssen, diesen Fehler unbemerkt wieder einzubauen, war die vergangenen drei Wochen allemal wert.

Insofern kann ich – auch wenn das Schreiben von Unittests für mich noch nicht intuitiv geschieht – guten Gewissens behaupten, dass Unittests sehr wohl Sinn machen. Dies zugebenermaßen sogar mehr, als ich noch vor vier Wochen gedacht hätte.

Auch Kirchen haben Kragsteine

Samstag, 7. November 2009, 18:41 Uhr
Permalink | Kommentare (3) | Kommentare als RSSRSS

Vor knapp einer Woche habe ich mit meinem Blogeintrag Wie viel Sinn machen Unittests? – der im Rahmen der monatlichen Streitgespräche von Peter Bucher und mir erschienen ist – für einige Verwunderung gesorgt. Viele haben sich gefragt, warum gerade ich Unittests dermaßen skeptisch gegenüberstehe.

Ralf Westphal hat in seinem Kommentar vermutet, dass

etwas hinter den drei “problematischen Aspekten” das Grundproblem zu sein [scheint]

und ich muss zugeben, dass diese Vermutung nicht von der Hand zu weisen ist. Als problematisch habe ich an Unittests im Wesentlichen die Tatsache betrachtet, dass Code zum Zweck einer besseren Testbarkeit potenziell angepasst werden muss, da sich einige Konstrukte ansonsten nur schlecht oder gar nicht testen lassen.

Als Beispiel seien an dieser Stelle abstrakte Klassen angeführt, die per Definition nicht instanziiert und deshalb nicht direkt getestet werden können. Prinzipiell kann dieses Problem auf zwei Arten gelöst werden:

  • Entweder werden die bereits bestehenden abgeleiteten Klassen getestet – immerhin ist die abstrakte Basisklasse in diesen implizit enthalten.
  • Alternativ wird eine dedizierte abgeleitete Klasse erstellt, welche die abstrakte Basisklasse beerbt – die Basisklasse ist also auch in dieser Variante implizit enthalten.

Der relevante Unterschied zwischen beiden Varianten liegt in der Tatsache, dass die bereits bestehenden abgeleiteten Klassen eigene Funktionalität mitbringen, die den Test nicht unbedingt verfälschen, aber zumindest beeinflussen könnte: Es wird schwierig, den Test auf die abstrakte Basisklasse zu beschränken.

Eine dedizierte Klasse verfügt nicht über diese Nachteile: Sie sorgt lediglich dafür, dass die abstrakte Basisklasse in möglichst natürlicher Form instanziiert werden kann.

Technisch ist dieses Vorgehen unproblematisch, aber mental bereitete es mir Probleme: Seit Jahren wird Entwicklern vor allem in der akademischen Welt impliziert, dass Code nur dann “gut” ist, wenn er kompakt, elegant und im mathematischen Sinne schön ist.

Wie sind nun Änderungen, die nur um einer besseren Testbarkeit durchgeführt werden, mit diesem Paradigma vereinbar? Die kurze, aber schmerzhafte Antwort lautet: Gar nicht.

Wie bereits in Wie viel Sinn machen Unittests? angesprochen, beschreibt auch Roy Osherove in seinem Buch The Art of Unit Testing in dem Abschnitt Overcoming the encapsulation problem dieses Problem:

Some people feel that opening up the design to make it more testable is
a bad thing because it hurts the object-oriented principles the design is
based on. I can wholeheartedly say to those people, “Don’t be silly.”

Seine zugegebenermaßen sehr pragmatische Lösung

“Don’t be silly.”

dieses Problems stellt für mich keine zufriedenstellende Antwort dar – schließlich kommen die Bedenken nicht von ungefähr, und diese Antwort spiegelt dies in keinerlei Hinsicht wieder.

Auch die im Gespräch mit einigen anderen Entwicklern geäußerte Ansicht

Das ist halt so.

ist nicht zufriedenstellend. Diese Aussage beschreibt nämlich lediglich den Status Quo, begründet aber nicht, warum es in Ordnung oder vielleicht sogar gut ist, auf diese Art vorzugehen. Letztlich spiegelt diese Aussage lediglich das resignierte Abfinden mit einer – potenziell – unbequemen Tatsache dar.

Doch nun hat Ralf Westphal eine Begründung geliefert, die den Ansprüchen der Skeptiker – zumindest aus meiner Sicht – genügt:

das ist völlig ok. maschinen haben ja auch revisionsklappen für wartungsarbeiten. die haben sonst keine funktionalität. oder denk an kragsteine an alten kirchen, damit man gerüste anbringen kann.

Nach einigen Jahren des Zweifels und der Skepsis empfinde ich diese Analogie als eine wahre Erlösung – endlich wird begründet, warum ein solches Vorgehen in der Software in Ordnung ist.

Man mag einwenden, dass dieser Vergleich zur realen Welt doch offensichtlich und naheliegend sei, doch war in den vergangenen Jahren sonst niemand, mit dem ich mich über dieses Thema unterhalten habe, in der Lage, einen ähnlich klaren, einleuchtenden Vergleich zu ziehen.

Persönlich bemerke ich, dass diese Erkenntnis eine Art Wendepunkt darstellt – die mentale Barriere, mit der ich mich bislang gegen Unittests gesträubt habe, weil sie Anpassung von Code erfordern, ist aufgelöst. Alles, was bleibt, sind technische Probleme – und diese können mit Sicherheit gelöst werden.

Als Konsequenz werde ich das umsetzen, was Ralf Westphal vorgeschlagen und empfohlen hat:

Unit Tests […] konsequent und kompetent mal für einen Monat einzusetzen. Mit der Kompetenz mag es da schwierig sein, weil man sich dann eine Veränderung der Handlungs- und Denkgewohnheit selbst beibringen muss... aber das ist zumindest ein Anfang.

Wie es mir damit ergeht – darüber werde ich berichten …

Wie viel Sinn machen Unittests?

Sonntag, 1. November 2009, 09:37 Uhr
Permalink | Kommentare (22) | Kommentare als RSSRSS

Am 13. Oktober 2008 haben Peter Bucher und ich unter dem Titel Noch Fragen, Bucher? Ja, Roden! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:

Heute, am 1. November 2009, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:

Wie viel Sinn machen Unittests?

So wohl Peter wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Außerdem nimmt diesen Monat auch Christian Wenz wieder an unserem Streitgespräch teil.

Peters und Christians Kommentare findet sich zeitgleich in den entsprechenden Blogs, folgend nun mein Kommentar zu diesem Thema:

Unittests sind sinnvoll – nicht ohne Grund wird ihr konsequenter Einsatz empfohlen, unter anderem im Rahmen der Clean Code Developer-Initiative und des Extreme Programmings, von der dediziert auf Tests optimierten Entwicklungsmethode Test-Driven Development (TDD) ganz zu schweigen.

In der Theorie hören sich diese Ansätze zunächst auch sehr vielversprechend an. Schließlich ermöglicht der konsequente, durchgängige Einsatz von Unittests eine gefahrlose Änderung von bestehendem Code.

Unbeabsichtigte Seiteneffekte werden durch die Ausführung der Unittests sofort aufgedeckt und können somit vermieden werden. Zudem können gefundene Bugs unmittelbar durch neu geschriebene Unittests abgesichert werden, so dass sie sich nicht erneut einschleichen können.

So weit die Theorie, die Praxis sieht leider ein wenig anders aus. So leicht Unittests nämlich erklärt sind, so kompliziert ist ihre tatsächliche Umsetzung – vorausgesetzt, man strebt tatsächlich eine 100%ige Abdeckung an.

Problematisch sind in der Regel drei Aspekte:

  • Der bestehende objektorientierte Aufbau ist unter OOP- und stilistischen Aspekten sauber umgesetzt, läuft einer guten Testbarkeit allerdings zuwider.
  • Es bestehen zahlreiche Abhängigkeiten zu externen Komponenten, die sich nicht oder nur mit sehr viel Aufwand simulieren lassen.
  • Es bestehen Abhängigkeiten zur konkreten Laufzeitumgebung, die sich nicht oder nur mit sehr viel Aufwand nachbilden lassen.

Entwickler, die sich an Clean Code Developer orientieren,und die auch ansonsten versuchen, beispielsweise mit Werkzeugen wie FxCop ihren Stil und ihre Codequalität zu verbessen, streben in der Regel ein sauberes und durchdachtes objektorientiertes Design an.

Leider steht manchmal genau ein solches Design der Testbarkeit gegenüber. Häufig müssen beispielsweise im Sinne der Testbarkeit weitere Konstruktoren, zusätzliche Eigenschaften oder Schnittstellen zu einer Klasse hinzugefügt werden – obwohl dies aus einer rein objektorientierten Sicht keinen Sinn ergibt.

Roy Osherove beschreibt in seinem Buch The Art of Unit Testing in dem Abschnitt Overcoming the encapsulation problem dieses Problem:

Some people feel that opening up the design to make it more testable is
a bad thing because it hurts the object-oriented principles the design is
based on. I can wholeheartedly say to those people, “Don’t be silly.”

Leider kann ich mich seiner leichtfertigen Aussage “Don’t be silly” nicht so einfach anschließen – denn ein Unittest ist eben nicht einfach nur ein weiterer Verwender der API. Für den Unittest ist es nämlich im Gegensatz zu realen Entwicklern gleichgültig, ob eine API sinnvoll strukturiert ist oder nicht.

Daher empfinde ich es zumindest als bedenklich, ein durchdachtes, objektorientiertes und sauberes Design zu Gunsten einer besseren Testbarkeit zu ändern, wenn es dafür keine weiteren Gründe gibt.

Neben dem potenziell wenig Unittest-tauglichen objektorientierten Design stellt die Abhängigkeit von externen Komponenten ein weiteres häufiges Problem dar. Zu nennen sind hierbei unter anderem:

  • Datenbanken
  • Dateisysteme
  • Registry

Natürlich können all diese Abhängigkeiten entfernt, umgangen oder verschleiert werden – die Frage ist aber, zu welchem Preis dies geschieht. Es genügt nämlich nicht, Stubs und Mocks einzuführen, statt dessen muss die Umgebung auch nach jedem Test potenziell wieder zurückgesetzt werden: Schließlich soll jeder Test isoliert, das heißt unabhängig von seinen Vorgängern, ausgeführt werden.

Eine Datenbank wieder und wieder zurückzusetzen, ist technisch zwar machbar, kostet allerdings sehr viel Zeit. Damit wird das Ziel, alle Unittests bei jedem Build automatisch auszuführen, ab einem gewissen Punkt unerreichbar: Die zur Ausführung der Unittests benötigte Zeit liegt schlichtweg zu hoch.

Zudem können gar nicht alle externen Abhängigkeiten ohne weiteres aufgelöst werden – vor .NET 3.5 SP1 und der Einführung von System.Web.Abstractions war es zum Beispiel nur eingeschränkt möglich, den HTTP-Kontext einer Webanwendung zu simulieren.

Auch die Abhängigkeit von der Laufzeitumgebung an sich kann problematisch sein. So können sich beispielsweise HTTP-Anforderungen, die an eine Webanwendung gesendet werden, voneinander unterscheiden – je nachdem, welcher Webbrowser auf dem Client eingesetzt wird.

Um Webbrowser-spezifisches Verhalten zu simulieren, müssten die Eigenheiten des jeweiligen Webbrowsers im Unittest nachgebildet werden. Alternativ könnten der entsprechenden gewünschte Webbrowser automatisiert werden – was seinerseits allerdings einen ziemlichen Aufwand nach sich zieht.

Ebenfalls problematisch ist multithreaded Code, sofern bestimmte Konstellationen in den einzelnen Threads getestet werden sollen – da das Umschalten zwischen den Threads vom aktuellen Kontext des Prozessors beziehungsweise des Betriebssystems abhängt, kann sich das Verhalten von Ausführung zu Ausführung unterscheiden.

All diese Beispiele zeigen, dass es unter Umständen sehr aufwändig oder gar unmöglich sein kann, einen Unittest zu schreiben, der unter realitätsnahen Bedingungen ausgeführt wird.

Die Frage ist also nicht, ob eine 100%ige Testabdeckung wünschenswert ist – dies gilt rein von der Theorie her ohne jegliche Zweifel – sondern ob eine 100%ige Testabdeckung mit vernünftigem und vertretbaren Aufwand erreicht werden kann: Diese Frage kann durchaus mit Nein beantwortet werden.

Nichtsdestotrotz gibt es natürlich auch Code, der sich perfekt für Unittests eignet: So fallen beispielsweise sämtliche Algorithmen in diese Kategorie, da diese in der Regel kaum oder gar keine Abhängigkeiten zu anderen Komponenten aufweisen.

Daher ergibt es durchaus Sinn, Unittests zu schreiben – die Frage ist lediglich, wofür. Eine 100%ige Abdeckung mit Unittests erreichen zu können klingt zwar verlockend, der zur Erreichung dieses Ziels notwendige Aufwand lohnt in der Regel jedoch nicht.

Insofern muss im konkreten Kontext abgewogen werden, welche Komponenten als kritisch zu betrachten sind und der Abdeckung durch Unittests bedürfen. Selbst für diese Komponenten ist eine 100%ige Abdeckung noch ein ausgesprochen ehrgeiziges Ziel – daher empfiehlt es sich eher, eine zwar hohe, aber nicht perfekte Abdeckung wie beispielsweise 85% anzustreben.

Wie viel Sinn machen Unittests also nun? Zusammengefasst kann man sagen, dass Unittests – an der richtigen Stelle eingesetzt – durchaus Sinn ergeben, dass diese Stellen aber explizit ausgewählt werden sollten.

.NET Professionals im Profil: Peter Bucher

Donnerstag, 15. Oktober 2009, 09:37 Uhr
Permalink | Kommentare (2) | Kommentare als RSSRSS

Peter Bucher arbeitet als Webentwickler für die Firma cobra in Konstanz am Bodensee und und wurde von Microsoft bereits drei Mal in Folge als MVP für ASP und ASP.NET ausgezeichnet. Nachdem er zunächst mit ASP Classic gearbeitet hat, kam er über DirectX zu C# und .NET. Peter Bucher engagiert sich aktiv in der Community und leitet die .NET Usergroup Konstanz-Kreuzlingen. Sie erreichen ihn über seine Webseite und sein Blog.

Golo Roden: Peter, wie bist Du zur Softwareentwicklung gekommen? Wie und wann hast Du angefangen?

Peter Bucher: Ich habe ursprünglich eine Ausbildung im Bereich Hard- und Softwaresupport gemacht. Jedoch habe ich mich auch schon vor dieser Ausbildung sehr viel mit PCs, das heißt mit Hard- und Software, beschäftigt und wusste daher schon recht viel. Im Laufe der Ausbildung wurde es mir mit der Zeit langweilig, weil sich die Arbeiten und Probleme dort häufig wiederholten, ich schon vieles in diesem Bereich kannte und ich daher nicht mehr sehr viel dazulernen konnte.

Darum habe ich dann angefangen, mich mit Programmierung zu beschäftigen. Das Interessante an der Softwareentwicklung ist, dass sie ein stetiges Lernen von neuen Dingen erfordert, was mir zusagt.

Zuerst habe ich ein wenig mit Visual Basic 6 herumgespielt, danach dann mit VBScript, Classic ASP – einfach deshalb, weil die Webentwicklung beziehungsweise die Beschäftigung mit Design, HTML und CSS ohnehin schon länger ein Hobby war und mir die statischen Seiten nicht mehr genügten. Mein hauptsächliches Themengebiet war also VBScript beziehungsweise Classic ASP, zunächst nur privat, doch dann konnte ich einige kleinere Projekte für meine Lehrfirma erledigen.

Irgendwann stieß ich dann auf ASP.NET Zone, was damals noch ASP Forum hieß, und habe dort technische Fragen gestellt. Später, als ich dann schon mehr wusste, habe ich das wieder an die Community zurückgegeben, indem ich auch anderen Mitgliedern geholfen habe.

Im zweiten Ausbildungsjahr bekam ich zum ersten Mal die Auszeichnung zum MVP für ASP und ASP.NET. Zu dieser Zeit habe ich mich noch nicht all zu viel mit .NET beschäftigt, und meine ersten Gehversuche waren wenig erfolgreich, da es einfach zu viel neue Themen auf einmal waren. Jedoch wollte ich mich dennoch in .NET einarbeiten, da es interessant aussah und es zudem der nächste logische Schritt war.

Daher habe ich mich eines kleinen Tricks beholfen, um in .NET einzusteigen. Weil es mit ASP.NET nicht auf Anhieb geklappt hat, versuchte ich es statt dessen mit Windows Forms und DirectX, um in C# und VB.NET hineinzukommen. Genau genommen war ASP.NET deshalb zu groß für den Anfang, weil mit ASP.NET das ganze Paradigma von Windows Forms wie beispielsweise Events sowie die Statusverwaltung dazu kam, zusätzlich zum Lernen einer neuen Programmiersprache, OOP, ...

Dieses Vorgehen war eine gute Idee, denn damit konnte ich Erfahrungen mit Profilern sammeln, sowie lernen, was beachtet werden muss, damit eine Anwendung schnell und zügig ausgeführt wird. Quasi nebenbei habe ich mich dadurch sehr gut in C# und das .NET Framework eingearbeitet.

Nachdem ich einigermaßen sattelfest in C# und OOP sowie dem .NET Framework war, wagte ich mich wieder an den Umstieg auf ASP.NET, was dann auch wunderbar klappte. Seitdem arbeite ich hauptsächlich mit ASP.NET WebForms, ab und zu mit ASP.NET MVC und – wenn ich die Zeit dazu finde – ein bisschen mit der Manged DirectX API namens XNA.

Golo Roden: Du hast gesagt, dass .NET der nächste logische Schritt gewesen sei – welche Rolle nehmen in dieser Hinsicht Silverlight beziehungsweise RIA-Technologien im Allgemeinen für Dich ein?

Peter Bucher: In dem Moment, als ich mit .NET angefangen habe, überhaupt keine. Erst ein wenig später kam dann der AJAX-Hype, sowie der Flash- und Silverlight- beziehungsweise RIA-Hype. Ich war und bin immer noch konservativ eingestellt, wenn es um RIA-Technologien geht, dasselbe auch bei Javascript: Nur so viel wie nötig und so wenig wie möglich.

Diese Technologien setze ich deshalb nur dann ein, wenn es Sinn ergibt. Komplette Anwendungen mit Silverlight oder Flash machen meines Erachtens in den seltensten Fällen Sinn, vor allem nicht im Web. Dazu habe ich auch schon bei dem Streitgespräch Heißt die Zukunft RIA? von uns eine Meinung abgegeben.

Dabei muss jedoch immer unterschieden werden, ob eine Anwendung im Inter- oder im Intranet ausgeführt werden soll, wie auch, was die Anwendung letztlich leisten muss.

Im Allgemeinen kann ich sagen, das ich sicherlich etwas mit Silverlight entwickeln werde, sobald es erforderlich ist und ich mich in diese Technologie einarbeiten soll. Allerdings denke ich, dass wenn sich jemand bereits auf einen Bereich von .NET spezialisiert hat, sollte er sich möglichst nicht davon wegbewegen. Natürlich ist eine Sicht auf die anderen Technologien wichtig und hilfreich, aber sich ernsthaft einzuarbeiten halte ich nicht für rentabel bei einer schon sehr stark vorhandenen Spezialisierung.

Golo Roden: Wie siehst Du denn die Marktchancen für ASP.NET im Vergleich zu PHP oder Java? Warum sollte jemand, der heute anfängt, sich mit Webentwicklung zu beschäftigen, gerade auf ASP.NET setzen?

Peter Bucher: ASP.NET und Java gehören eher in den Enterprisemarkt, PHP hingegen findet sich meistens bei kleineren Firmen und vor allem bei Hobbyisten.

Das heißt jedoch überhaupt nicht, dass es nur dort hingehört oder nur dort zu finden ist. So sind auch größere Anwendungen bereits mit PHP umgesetzt worden, ebenso gibt es kleinere Anwendungen mit ASP.NET als verwendeter Technologie.

In den letzten Jahren wurde ASP.NET auch für Privatanwender sowie kleinere und mittlere Firmen interessanter. Es gibt sehr gute Angebote von Microsoft wie beispielsweise, dass die Express-Edition von Visual Studio Web Developer kostenlos abgegeben wird. Damit kann schon fast alles erreicht werden. Es fehlt lediglich die Möglichkeit, Addins zu nutzen, was vermutlich die größte Schwäche ist – jedoch hindert das letztlich niemanden daran, etwas in ASP.NET umzusetzen.

Darüber hinaus wurden die Hostinggebühren für Windows Server immer geringer, denn es gibt inzwischen spezielle Versionen von Windows Server, die für das Web optimiert beziehungsweise limitiert sind und daher günstiger zu haben sind. Für Startups oder kleinere Firmen unter zehn Mitarbeitern gibt es zudem seit neuestem sogar ein Programm namens WebsiteSpark, in dessen Rahmen Microsoft kostenlose Lizenzen für alle Entwicklungswerkzeuge, Grafikwerkzeuge sowie Serverbetriebssysteme für ganze 3 Jahre anbietet.

Neben diesen Gründen für ASP.NET gibt es jedoch auch noch einen technischen Aspekt, und dabei werde ich keine Nachteile der anderen Technologien aufzeigen, sondern nur Vorteile von ASP.NET sowie der .NET-Platform: Wer mit ASP.NET entwickelt, entwickelt letztlich mit .NET-Sprachen und dem .NET Framework. Wer mit Windows Forms, WPF oder Silverlight entwickelt, ebenso. Wer mit Windows Mobile entwickelt, arbeitet zumindest mit einem Subset des Frameworks – jedoch sind genau die selben Sprachen nutzbar.

Ich möchte darauf hinaus, das sich Entwickler auf eine oder zwei Sprachen sowie ein einziges Basisframework zuzüglich eines zusätzlichen, spezialisierten Frameworks wie ASP.NET, WPF oder ähnliches beziehungweise auf ein Subset des .NET Frameworks fokussieren müssen – nicht mehr und nicht weniger. Damit können sie theoretisch in allen Bereichen arbeiten, vom .NET Micro Framework bis zum .NET Compact Framework, über ASP.NET bis hin zum großen .NET Framwork.

Für Umsteiger erleichtert das einiges und Einsteiger können nach einer gewissen Lernphase mit dem schon vorhandenen Wissen relativ schnell einen anderen Bereich von .NET kennenlernen. Zusätzlich ist es natürlich so, dass .NET und die dafür verfügbaren Sprachen die Vorteile aus den schon vorhandenen Technologien mitgenommen, deren Nachteile aber möglichst nicht übernommen haben.

Golo Roden: Gibt es in ASP.NET entwickelte Projekte, die Dich besonders fasziniert haben – an denen Du eventuell auch beteiligt warst?

Peter Bucher: Mich faszinieren viele Teile eines Projekts. Ein solcher Teil war beispielsweise eine Lizenzverwaltung für eine Webanwendung. Dabei ging es darum, einen Algorithmus zu erstellen, mit dessen Hilfe Lizenzen erzeugt werden können. Dazu schrieb ich dann auch die API, um den Algorithmus zu kapseln und in der Anwendung zu benutzen, sowie natürlich die Integration in die Windows- und die Webanwendung.

Dieses Projekt war ein sogenanntes Greenfield-Projekt, das heißt, ich konnte von der grünen Wiese aus starten und hatte keinen Legacy Code, den ich erweitern musste.

Daneben habe ich privat viele Greenfield-Projekte aufgezogen, darunter viele kleinere Sachen wie beispielsweise eine Bibliothek, die es ermöglicht, Objektlisten direkt als XML zu speichern, das ganze automatisiert und ohne aufwändige Konfiguration. Mein größtes Übungsprojekt, das ich immer wieder von neuem umbaue und daran neue Technologien evaluiere, ist meine private Webseite. Interessant ist dabei auch, dass ich daran viel am Backend gearbeitet habe, dies nach Außen aber überhaupt nicht sichtbar wurde.

Fremde Projekte können mich auch faszinieren, jedoch natürlich nie so, wie etwas selbst geschaffenes, das so funktioniert, wie ich es mir vorgestellt habe und worin viel Liebe zum Detail steckt. Um ehrlich zu sein, ist mir relativ wenig Code untergekommen, von dem ich behaupten könnte, das er mich fasziniert. Zumindest keine komplette Anwendung, kleinere Snippets oder Komponenten schon eher.

Ich bin jetzt beim Thema Codestyle angekommen, und merke das erst jetzt. Fremde Projekte gibt es eine Menge, die mich von ihrem Aufbau, der Komplexität oder wegen ihres APIs faszinieren. Dazu zählen beispielsweise NHibernate, Rhino.Mocks, autofac und SubText, um nur einige zu nennen.

Golo Roden: Ein wesentliches Merkmal der IT ist, dass man beständig mit neuen Entwicklungen konfrontiert wird, und diesen folgen muss. Woher nimmst Du die Motivation, Dich quasi jeden Tag weiterzubilden und mit Neuem zu beschäftigen?

Peter Bucher: Das ist eine sehr gute Frage. Damit habe ich mich persönlich auch schon beschäftigt, darum fällt mir die Antwort relativ leicht. Wenn wir vorne anfangen, ist es wohl am verständlichsten: Ich hatte schon von klein auf einen angeborenen „Tüftlerinstinkt“, habe also alles, was mich interessierte, angeschaut und damit herumgespielt.

Als mein Vater den ersten Computer kaufte, war ich sofort davon fasziniert. Später kam ein zweiter Computer hinzu und ich durfte den alten 386er für mich haben. Das hatte auch seinen Grund, denn vorher habe ich den Computer meines Vaters immer umgestellt, manchmal sogar so viel, dass er nicht mehr gestartet hat.

Mit meinem eigenen Computer habe ich genau dasselbe gemacht und so mit der Zeit natürlich Dinge gelernt, die ich sonst nie herausgefunden hätte. Nach einer gewissen Zeit war es kein Problem mehr, meine gemachten Fehler wieder rückgängig zu machen, also beispielsweise Windows neu zu installieren. Wichtig ist: Ich bin eher praktisch angehaucht, sehr interessiert, probiere sehr viel aus und lerne aus Fehlern.

Ich habe, wie bereits gesagt, mit der Programmierung angefangen, weil mich der Computersupport langweilte und er für mich ausgeschöpft war. Das kann bei der Softwareentwicklung nie passieren – ich weiß, man soll niemals nie sagen, aber hier und jetzt schon. Meine Motivation kommt also aus meinem sehr großen Interesse an der Sache, meinem Tüftlerinstinkt und schlussendlich aus dem Wissensdurst, den ich in mir habe.

Es ist ganz klar, dass irgendwann, irgendwo eine Grenze gezogen werden muss, nämlich dann, wenn sich die Frage stellt: Was lerne ich? Wie lange? Und wofür? Auf Grund meiner Berufslaufbahn, bei der auch mit der MVP-Auszeichung nochmals die Weichen gestellt wurden, habe ich mich komplett auf die Webentwicklung mit ASP.NET spezialisiert und vernachlässige jetzt beispielsweise die Spieleentwicklung, obwohl sie mich nach wie vor noch interessiert.

Zusammengefasst kann ich also sagen: Das Lernen fällt mir in diesem Bereich sehr leicht, weil eben das große Interesse da ist. Ich würde sogar sagen, dsas ich implizit lerne. Kinder lernen sehr schnell und mit großer Leichtigkeit, aber wieso? Weil es sie interessiert, sie haben ein riesengroßes Interesse an fast allem, darum lernen sie alles sehr gut und sehr schnell.

Bei mir ist dieses Interesse auf bestimmte Bereiche im Leben fixiert, bei denen mir das Lernen sehr leicht fällt und auch keine wirkliche Anstrengung bedeutet, im Sinne von: Weil ich es gerne mache. Durch diese Tatsache kann ich mir auch erklären, wieso ich zum Autodidakten geworden bin und mir das meiste selbst beigebracht habe, wobei ich bei anderen lernenden Aktivitäten, wie beispielsweise einem Fach in der Schule, durch den Lehrer beigebracht, niemals eine solche Motivation aufgebracht habe.

Die Motivation kommt also aus den Interessen und aus den vielen kleinen und großen Erfolgserlebnissen, sowie auch durch das wiederum implizite Lernen, während ich anderen in der Community weiterhelfe.

Einen sehr wichtigen Punkt möchte ich noch erwähnen: Das Filtern. Im heutigen Informationszeitalter werden wir geradezu mit Informationen überschüttet. Dabei genügt schon die Informationsflut in der IT-Industrie, privates kann man dabei außen vor lassen. Dort ist es wichtig, am besten das herauszufiltern, was interessiert und einen Nutzen bringen könnte. Wenn das nicht gemacht wird, zerbricht man an der Flut und irgendwann platzt der Kopf, oder man bringt keine Energie für die wirklich wichtigen Dinge mehr auf.

Golo Roden: Wenn sich ein Anfänger heute mit dem Thema Softwareentwicklung befassen will – welche Voraussetzungen sollte er Deiner Meinung nach dafür mitbringen, und was siehst Du als No-Go an?

Peter Bucher: Wenn es darum geht, die Softwareentwicklung als Berufsziel zu haben, kann ich mich eigentlich auf mein Posting vom Streitgespräch Woran erkennt man einen guten Entwickler? beziehen.

Einen Weg, wie man im Lauf der Zeit besser werden kann, wurde auch bereits in einem Streitgespräch, Die Forderung nach Softwarequalität, thematisiert.

Grundsätzlich bin ich der Meinung das nicht jeder Kandidat als Softwareentwickler geeignet ist, sowie ich auch nicht unbedingt beziehungsweise nur bedingt beispielsweise als Schreiner oder Koch geeignet wäre. Das liegt nicht daran, das es jemand nicht könnte, sondern wie er es kann und vorallem wie sehr es ihn interessiert.

Interesse alleine genügt nicht, da von irgendwoher auch die Motivation zur Lernen aufgebracht werden muss. Wie das in meinem Fall aussieht, habe ich bereits beschrieben, aber das kann von Kandidat zu Kandidat natürlich unterschiedlich aussehen.

Ein No-Go wäre die zu einseitige Fixierung auf Theorie oder Praxis. Das kann auf Dauer nicht gut gehen. Wichtig ist also, das schon von Anfang an Theorie und Praxis miteinander verbunden werden, um weiterzukommen.

Zu hohe Anfangsziele beeinträchtigen die Motivation erheblich, auch der Blick zu anderen Entwicklern, die um einiges weiter sind, mit einem Vergleich zu sich selbst, sind meistens ernüchternd. Darum ist es am besten, sich mit sich selbst vor einem Monat oder einem Jahr zu vergleichen, und sich klar zu machen, dass erstens jeder einmal klein angefangen hat und es, egal was man tut, immer jemanden gibt, der besser auf dem Gebiet ist.

Golo Roden: Bei der Vielzahl an Technologien, die es heute gibt: Womit sollte ein Anfänger heutzutage anzufangen?

Peter Bucher: Ich gehe an dieser Stelle nicht auf die Technologien ein, da sonst garantiert irgendjemand nicht zufrieden ist, mit dem was ich sage. Der Anfänger sollte jedoch damit anfangen, womit er ein gutes Gefühl hat und womit er seine ersten Projekziele erreichen kann. Am besten ist es, vor einer Entscheidung für eine bestimmte Technologie ein paar kleinere Prototypen bauen und diese zu vergleichen, denn auf diese Art sieht man schnell, wie sich das Arbeiten mit der Technologie anfühlt und ob sie den Anforderungen standhält.

Wenn es darum geht, in die Softwareentwicklung einzusteigen, sollte im weiteren Verlauf eine Ausrichtung auf den aktuellen Markt stattfinden, das heißt, man sollte sich entscheiden, in welche Richtung man gehen möchte und welche Technologien dafür eingesetzt werden. Schlussendlich ist dann noch die mehr oder weniger finale Entscheidung zu fällen, worauf man sich spezialisieren möchte. Ohne Spezialisierung wird man schließlich zu keinem Spezialisten, und eine Spezialisierung auf alles ist nicht realistisch.

Dennoch ist es nützlich, zumindest zu einem gewissen Grad auch ein Generalist zu sein, der sich später spezialisiert und zum Spezialisten wird, der allerdings über ein breit gefächertes Allgemeinwissen verfügt.