Automatische Spaltenbreite

19. August 2016


media-20160818

Viel gibt es zu lesen in Blogs und Foren zu Spaltenbreiten von Standardlisten im Sharepoint. Das meiste davon die Frage, wie man sie einstellen kann. Scharen von Sharepoint-Nutzern scheinen über Listen zu stolpern, bei denen sich eine Zeile über viele Bildschirmseiten erstreckt. Eine Spalte, oft heißt sie „Beschreibung“ enthält viel formatierten Text, wird aber von Sharepoint als eine der schmalsten Spalten formatiert.

Die Antworten reichen von „das geht gar nicht“, über „das geht automatisch“ bis zu dubiosen Anleitungen, um CSS-Attribute über in die Seite eingebettete JavaScript-Webparts umzusetzen.

Alles weicht überraschend stark von der marktüblichen Lösung (ziehen an der Spaltenkopfgrenze) ab. Und zwar auf der Aufwandsskala weit nach oben.

Microsoft selbst schreibt, die Spaltenbreite werde automatisch bestimmt. Aber warum so unsinnig? Nein, nicht primär unsinnig! Vor allem billigst möglich.

Bitte im Screenshot oben genau kucken, wie man die Spalte breit bekommt! Tipp: der Trick liegt im Spaltenkopf 😁

Advertisements

IPv6 und private cloud

25. März 2013


Ich weiß nicht, ob es viele Menschen gibt, die ihre gesammelten persönlichen Informationen, wie Kontakte, Kalender-Einträge, E-Mails und Dateien aller Art bei Fremden ablegen. Mir würde das ja Angst machen und ehrlich gesagt finde ich es auch ziemlich banal, daheim die Zentrale der persönlichen Information und Kommunikation aufzuziehen.

Mein Szenario sieht im Moment wie folgt aus:

  • Clients sind alle mobil: 2 MacBooks, diverse Android-Geräte
  • Server aus dem Standardportfolio von Debian, eGroupare, Dovecot, Exim4
  • Protokolle dazwischen IMAP, SMTP, CardDAV, CalDAV, WebDAV
  • ein multidrop-Postfach für die Domäne bei einem Provider (ein Überlebender des bayerischen Bürgernetz-Progamms der späten 90er)
  • dyndns.org zum Abfrühstücken der dynamischen IP
  • eine eigene CA, deren Zertifikat als trust root auf allen Devices aufgespielt ist.

Funktioniert soweit wunderbar, wohlgemerkt auch völlig problemfrei zwischen den iApps und der Android-Welt.

Dann kommt, im Zuge eines notwendigen Providerwechsels, das, wovor ich in meinem professionellen Leben seit 2 Jahren warne: der neue unterstützt nur noch IPv6!

Naja, zugegeben, nicht ganz, Webseiten und andere Daten über v4 abzurufen geht schon, aber besagter Provider hat schon eine etwas eingeschränkte Vorstellung von dem, was seine Kunden tun:

Für Sie bleibt alles wie gewohnt, einzig der Zugriff von Aussen auf Ihren Unitymedia Internet-Anschluss unterliegt aktuell noch Einschränkungen.

VPN Tunnel Verbindungen funktionieren nicht bzw. eingeschränkt (da beide Seiten den gleichen IP Standard unterstützen müssen)

Fernwartung der Fritz!Box nicht möglich

Na, und der ganze Rest halt auch nicht =-O

Nun ist es eigentlich ein ziemlicher Spaziergang, die Services unter IPv6 zur Verfügung zu stellen. Keine Frickelei mit Pre- und Postrouting und Masquerading mehr; das IP-Präfix scheint statisch zu sein (nicht, dass ich dazu eine klare Aussage bekommen hätte) und so muss man einfach die entsprechenden Ports auf den Diensteserver freischalten und das war’s.

Jetzt gibt es aber ein ganz kleines Problem: außer Unitymedia, die nur IPv6 unterstützen, unterstützt in Deutschland genau gar kein mir bekannter Provider IPv6. Insbesondere die Mobilfunktelkos bleiben ihrer Seriosität treu und sehen anscheinend überhaupt keinen Anlass sich zu bewegen.

Kommt der etwas meta anmutende Tunnelprovider Sixxs in’s Spiel. Für die Android-Geräte gibt es Androicciu. Das erfordert root-Rechte, die dank immernoch ungepatchem exynon-mem-bug auf einem Samsung Galaxy SIII per Knopfdruck zur Verfügung stehen. Installation von Busybox nicht vergessen!

Seit heute funktioniert das Abenteuer tatsächlich. Mal sehen wie es sich bewährt – und ob ich das Xoom auch noch roote.


Warum zum Henker gibt es eigentlich immer noch SQL-Injection-Vulnerabilities? Kann es daran liegen, dass sich ein im Ansatz ziemlich bescheuertes Paradigma weitgehend ungestraft als der Standard schlechthin durchsetzen konnte?

Ich meine damit in der Tat Objekt-relationales Mapping an und für sich. Ich sehe in diesem Ansatz gut 30 Jahre nach dem Durchbruch der Objektorientierung den immer noch andauernden Kniefall vor Menschen, die ihre Weltsicht und ihre biologische Nische bislang erfolgreich gegen die Realität verteidigt haben. Die Prister des Glaubens, dass das (irrtümlich) als generell anwendbar bejubelte Muster, beliebig strukturierte Daten in Tabellenzeilen und deren Beziehungen untereinander abzubilden, der seelig machende Ansatz jeglicher Persistenz sei. Der Kniefall vor den Datenbanklern!

Aber damit nicht bescheuert genug. Als Kommunikationsprotokoll für dieses Muster des Gottstehunsbei hat sich doch wahrhaftig der unstrukturierte Freitext-String durchgesetzt. Eine der natürlichen (englischen) Sprache nachempfundene Abfrage mit gewachsener Syntax aber ohne klare technische Struktur ist das Maschine- (Applikationsserver) zu-Maschine (Datenbank)-Protokoll schlechthin geworden: SQL. Ursprünglich konzipiert als die Abfragesprache, mit der seniorige Manager, CEOs insbesondere, ihre Fragen zum Zustand des Geschäfts gleich direkt an die datenführenden Systeme schicken können sollten. Sozusagen die BI-Lösung von IBM in den 80er Jahren. Welcher verdammte Volltrottel ist eigentlich auf die Idee gekommen, den Mist maschinell zu generieren? Warum machen es alle nach? Warum ist es sogar in objektorientierten Frameworks und Standards wie Hibernate und JPA das default-Muster, Anfragen und Kommandos an das Persistenzsystem aus Textbausteinen aufzubauen? Und warum ist eine vernünftige Abfrage-API verschämt in der zweiten Reihe bzw. unter der Ladentheke zu haben? Wer kennt überhaupt z.B. die Condition-API von Hibernate, geschweige denn, nutzt sie? Und warum zum Henker ist diese mutmaßlich immernoch ein domänenspezifischer Stringbuilder, sprich, ein SQL-(fast-)Freitext-Generator?

Sagen nicht Datenbankler selbst, dass die Verwendung der Methode substring() nach todeswürdigen Vergehen wider angemessene Datenmodellierung riecht? Unter dem Gesichtspunkt schonmal den Anfrageparser eurer Lieblingsdatenbank gereviewt?

Auf dem vergeigten Ansatz blüht natürlich eine ganze Wissenschaft, die sich damit beschäftigt, wie man ihn auf eine abstraktere Art vernünftig einsetzen soll. Da gibt es die Forderung, immer denselben Abfragestring zu verwenden mit Platzhaltern, die dann von Anfrage zu Anfrage mit unterschiedlichen Variablen gebunden werden: Prepared Statements. Der Datenbanktreiber kann dann anhand des Hashcodes des Anfragestrings (…eigentlich Abfragestring-Templates) erkennen, dass es diese Anfrage schon gab und den Server über diesen Hashcode auf die für diesen Client im Server-Cache gehaltene Abfrage-Objektstruktur verweisen. Man spart sich damit die tatsächliche erneute Übertragung des Anfrage-Strings und das server-seitige Parsen. Suuuuuper!

Dass man mit syntaxbeladenem Freitext, in den syntaxfreier Freitext (vulgo Nutzdaten) eingebettet werden muss, notwendigerweise auf ein Escaping-Problem läuft, liegt auch in der Natur des Designs, nicht? Das kennen wir alle schon von, sagen wir, CSV. Mein Vorschlag wäre ja: Lasst es einfach bleiben! Aber nein, unser schöner SQL-Ansatz ist ja heilig. Aber auch hier ist man ja irgendwann mal auf die fantastische Idee gekommen, die Nutzdaten API-technisch von der Abfragesyntax zu trennen. Also so halb. Denn man kann natürlich niemandem zumuten, diesen Ansatz für verbindlich und verpflichtend zu erklären. Nein, Datenbänkler sind traditionsbewusst. Der datentragende Anfragestring war die Ursprungsidee, ist heilig und wir selbstverständlich weiter unterstützt. Prepared Statements bleiben optional, ihre Verwendung einzuklagen kann natürlich nur organisatorisch gelöst werden. Toooll!

Was soll ich denn eigentlich Entwicklern vorwerfen, die aus Unerfahrenheit in den von ihnen entwickelten Systemen SQL-Injection-Vulnerabilities eingeführt haben? Dass sie an irgendeiner Stelle eben den StringBuilder ihrer Programmierplattform statt des domänenspezifischen StringBuilders ihres Persistenzframeworks bzw. den Templating-Mechanismus ihres Datenbankproduktes genutzt haben? Dass sie dem Sprachumfang von SQL und dessen ursprünglicher und nach wie vor klaglos unterstützter Syntax, nein sogar dessem nativen Anwendungsmuster auf den Leim gegangen sind? Was für eine Art von Vorwurf ist das eigentlich?

Natürlich hätte der sicherheitsproblemverursachende Entwickler wissen können – wissen müssen! – was für Fallgruben in seinem Tätigkeitsfeld so zu beachten sind, keine Frage. Nur wage ich hier mal zu fragen: wer ist eigentlich der verantwortungslosere Tölpel: der, der in eine ungesicherte Baugrube hineinfällt, oder der, der dem Entstehen einer sinnlosen Baugrube zusieht und nicht einmal auf den Gedanken kommt, diese abzusichern?

Im Klartext: das eigentliche Problem sind ausnahmsweise mal nicht schlecht geschulte Programmierer. Das Problem ist eine schelchte Informationsarchitektur, ein verantwortungsloses Protokoll und ein kollektiver Unwille, Dinge besser zu machen, weil man sie dann anders machen müsste.


In der freien Wildbahn tobt ein ewiges Ringen: Heerscharen von Programmierern rudern gegen die NullpointerException. Eine der Ursachen, vielleicht die wichtigste: inkonsequentes Verhalten von Methoden.

Bevor Exception Handling in Programmiersprachen eingeführt wurde, mussten Fehlerzustände über die Rückgabewerte abgebildet werden. Da lag es bei Pointern natürlich nahe, null als magischen Wert für ein Problem zu verwenden. Dass man das so machte, weil es anders nicht ging, ist nicht Jedem bewusst, und so wird dieses Verhalten auch heute noch nach gemacht.

In Java und den meisten anderen obektorientierten Programmiersprachen gehört der exceptional flow und ein Klassenmodell von Exceptions zum Design, die Deklaration „fachlicher“ Exceptions zur Methodensignatur.

Nehmen wir also einen typischen Finder z.B. einer persistierten Entität:
DingsEntity findByID (EntityID id)

Es erscheint völlig nahe liegend, dass ein Finder mal nichts findet. Also bitte auch so designen:
DingsEntity findByID (EntityID id) throws EntityNotFoundException

Hier gehen wir davon aus, dass völlig zuverlässig sicher gestellt ist, dass id eindeutig ist, z.B. auch „in depth“ über ein dahinter stehendes Datenbankconstraint. Sonst lieber noch eine …NotUniqueException spendieren!

Das Schöne an diesem Konstrukt ist, dass man als Aurufer die Behandlung nicht vergessen kann. Der eine oder andere Tastaturanschlagsoptimierer wird dieser Einstufung als schön möglicherweise widersprechen, aber die meisten Leser dürften wissen, dass der Aufwand von Software nicht durch das Tippen von Sourcecode entsteht. In diesem Fall hilft natürlich auch eine IDE, zumindest wenn man sich helfen lässt.

Ach so, fast hätte ich vergessen, das Wichtigste zu erwähnen, weil es gar so klar ist: wenn unsere findById das verlangte Dings nicht findet, gibt es einfach null zurück.

Nein, tut es nicht, verdammt noch mal, sowas tut man überhaupt nicht, es wirft eine …NotFoundException!

Nun könnte die Implementierung unserer Methode ja mit externen APIs konfrontiert sein, von deren Wohlverhalten man nicht überzeugt ist. In diesem Fall lieber überprüfen: konservativ selbst auf null prüfen und ggf. die Exception werfen. Oder, wenn die API eigentlich schon richtig vereinbart ist, ein vorsorgliches assert (fail early!). Defensiv programmieren.

Äh, und wenn jetzt noch jemand seinen Kollegen bei folgendem Konstrukt erwischt:

DingsEntity dings ;
try {
dings = am.findById (id);
} catch (EntityNotFoundException enf) {
dings = null;
}

– der bringt den Kollegen bitte bei mir daheim zur Besprechung des Sachverhalts vorbei. Dafür hier ausnahmsweise meine Adresse: Tigergehege 1, Tierpark Hellabrunn, München.

Wenn unsere Methode ohnehin mehrere Objektinstanzen zurückgeben kann, also eine Collection oder eine deren Spezialisierungen, wirft man im Fall von 0 gefundenen Objekten natürlich keine Exception, gibt um Himmels willen auch niemals null zurück, sondern eine leere Collection! Auf ein …if (retVal== null || retVal.isEmpty())… kann man dann in der gesamten Applikation getrost verzichten.

Wichtig bei Collections: „Nichts passt“ ist eine völlig andere Aussage als „konnte nicht suchen“! Damit sollte klar sein: wenn die Suche scheitert, wird eine geeignete Exception geworfen. Ob eine checked oder eine unchecked, hängt dabei vom Kontext ab: ist die Suche von fachlichen Voraussetzungen abhängig, ist es sinnvoll, zu Verletzungen der Vorbedingungen checked Exceptions zu designen. Das Scheitern einer Verbindung zur Datenbank wird man i.A. unchecked realisieren.

Berücksichtigen Sie das, wird auch für Ihre Anwender die Meldung „keine passenden Obekte oder interner Fehler“ der Vergangenheit angehören. Und der Code-Umfang um die Hälfte sinken, weil die Prüfungen auf unterschiedliche Arten, den Mangel eines Ergebnisses auszudrücken, der Vergangenheit angehören.

Wie man da hin kommt, wenn das System schon verfriemelt ist? Ganz einfach: Aufräumen, und zwar von unten her. Und bei der Gelegenheit das Wohlverhalten der niedrigen Schichten über Unit-Tests sicher stellen. Anschließend in den höheren Schichten die alten Fallunterscheidungen eliminieren: radikal sein! Nur Mut, es passiert nichts mehr, das Gespenst NullpointerException geistert ab sofort … woanders. Aber das ist eine andere Geschichte und soll ein andermal erzählt werden.


Ganze Systeme für Geschäftsanwendungen schreien förmlich aus ihren Sourcen, wie ihre Entwickler tagein tagaus gegen die NullpointerException anrudern.

Die Ursache ist, dass jedes Attribut, jeder Getter, jederzeit neben sinnvollen Resultaten auch null liefern kann – niemand weiß, wann es zuschlägt. Bei String-Attributen kommt erschwerend hinzu, dass sie leer oder null sein können, oder auch mal mit Leerzeichen gefüllt.

Erkennbar versuchen die anwendenden Programmierer von solchen, spontan null-befüllbaren Objektattributen, ohne jegliche logische Richtschnur zu erraten, was ein null, ein Leerstring, oder gerne auch mal ein 0 eines Zahlenattributes denn zu bedeuten haben könnte, und was für ein Systemverhalten dem angemessen sein kann.

Um in dieser Sisyphos-Aufgabe zu unterstützen, wurden schon ganze Heerscharen von Normalisierungs-Hilfsfunktionen geschrieben (nullToEmpty, emptyToNull, truncEmptyToNull…). Sieht man sich Aufrufhierarchien an, die über Code von verschiedenen Entwicklern oder Teams, oder auch nur desselben Entwicklers an verschiedenen Tagen, laufen, sieht man nicht selten, dass null’s zu Leerstrings, dann wieder zu null’s und vielmals hin und her konvertiert werden.

Schließlich gibt es auch Verwirrung, wie man die Abwesenheit von einem komplexen Attribut darstellen möchte. Ich habe wahrhaftig Code gesehen, in dem ein Objekt-Attribut als nicht gesetzt dargestellt wurde, in dem ein leer konstruiertes Objekt (also seinerseits mit lauter null-Attributen belegtes) gesetzt wurde.

Warum eigentlich so kompliziert?

Ich möchte an dieser Stelle dazu einladen, im Kern von Business-Anwendungen grundsätzlich nach der fachlichen Logik zu operieren (und diese auch in den Settern oder Konstruktoren durchzusetzen):

  • null sollte man genau dann verwenden, wenn ein Objekt eine Eigenschaft nicht hat, oder diese Eigenschaft nicht bekannt ist,
  • Leerstrings sollte man genau dann verwenden, wenn die Eigenschaft bekannt und definiert ist, aber eben leer.

Nehmen wir mal als weitgehend beliebiges Beispiel einen Vorgang, der einen Bearbeiter haben kann (wenn er schon bearbeitet wurde). Ein Bearbeiter ist eine Person, die wiederum einen Vornamen und einen Nachnamen haben möge. Wir könnten nun einen noch unbearbeiteten Vorgang auf folgende Arten repräsentieren:

  1. issue.setAssignee(null)
  2. issue.setAssignee(new Assignee())
  3. issue.setAssignee(new Assignee("",""))

Sorry, aber ausschließlich die erste Variante repräsentiert die korrekte Situation: der Vorgang hat keinen Bearbeiter. Der Vorgang hat insbesondere nicht

  • einen Bearbeiter ohne Vor- und Nachnamen
  • einen Bearbeiter, der auf den Namen   getauft ist und aus der Familie   stammt.

So, sagt nun der aufmerksame Beobachter, aber dann würde ja so etwas wie issue.getAssignee().getSurname() gerade auf eine NullpointerException laufen!?

Nun ja, richtig, und diese Aufrufkette ist ja auch fachlich falsch, wenn ein Vorgang nicht notwendigerweise einen Bearbeiter haben muss. Es erscheint durchaus angemessen, genau dies zu korrigieren, anstatt ein leer-namiges Personen-Objekt dort hinzuhängen und das Problem der armen Sau an den Kopf zu werfen, die mit den Namen von Personen umgehen muss. Diese Sau ist nämlich vor allem deswegen so arm, weil nun wirklich niemand damit rechnen muss, dass eine existierende Person keinen Namen hat (naja, vielleicht ist das gewählte Beispiel tatsächlich nicht international wasserdicht: http://www.guardian.co.uk/notesandqueries/query/0,,-7635,00.html).

Woher kommt eigentlich die ganze Verwirrung? Meiner Beobachtung nach von Systemschnittstellen, an denen sich das Konzept nicht konsequent durchhalten lässt. Die prominenteste davon: die graphische Benutzerschnittstelle. Eingabefelder lassen sich typischerweise nicht auf „unbelegt“ stellen, sondern sie drücken ihr Nichtgesetztsein als Leere aus. Will der Anwender ein Attribut löschen, löscht er nicht das Eingabefeld, sondern den Text darin. Das ist unlogisch, aber so sind Anwender eben, und die Benutzeroberflächen folgen ihnen darin.

Systeme sind oft nicht besser. Da mag es z.B. welche geben, deren Schnittstellenspezifikationen die Abwesenheit von Attributen wahrhaftig durch eine Ansammlung von Leerzeichen wiederzugeben suchen. (Solche Designs stammen aus Zeiten, als man sich den zusätzlichen Speicherplatz oder Netzwerkverkehr für das Bit „ist leer“ einfach nicht leisten konnte. Heute dagegen kann man sich vor allem eines nicht leisten: verkorkste Software.)

Nunja, man kann nicht die ganze Welt allein retten, zumindest nicht mit einem einzelnen Release. Daher würde ich doch vorschlagen, die unlogische Darstellungsform genau an diesen Schnittstellen zu behandeln. Wenn es z.B. die von den Anwendern eingeklagte Konvention ist, dass das leer Lassen von Vor- und Nachname bedeuten möge, dass es den Bearbeiter nicht gibt, dann sollte man genau diese Konvention in die Darstellung der Benutzeroberfläche und Verarbeitung von Eingaben ziehen:
if (forename.isEmpty() && surname.isEmpty()) {
issue.setAssignee (null);
} else {
...
}

Nicht schön, aber die (oft gewählte) Alternative ist, sich mit diesen Sonderregeln in der gesamten Business-Logik und auf allen Schichten der Anwendung herumzuschlagen.

Neubau versus Renovierung

6. August 2011


Fangen wir mal mit einem einfachen Thema an. Schließlich muss ich mich auch erst warm schreiben.

Im Lebenszyklus von Applikationen kommt irgendwann mal der Zeitpunkt, an dem eine qualifizierte Mehrheit von damit Beschäftigten zu dem Schluss kommt, sie sei am Ende desselben. Oft ist das nur zu verständlich:

  • die Struktur der Software weist keinerlei Ähnlichkeit mehr mit der Struktur des zu lösenden Problems auf (dazu kommt bestimmt noch ein eigener Artikel!),
  • die technische Dokumentation wurde zuletzt zu Zeiten von Kaiser Barbarossa aktualisiert (in Form einer Delta-Doku, in der die Umstellung von Rauchzeichen auf Pergament beschrieben wurde),
  • der letzte Mitarbeiter mit Durchblick hat sich auf eine 1-Stunden-Woche mit vollem Lohnausgleich optimiert, in der er zudem von einem Geschäftsführer persönlich durchgehend gebenedeit wird,
  • das restliche Team von 59 Personen benötigt regelmäßig ein Jahr, um ein Attribut eines Geschäftsobjektes von float auf double umzustellen.
Gut – also weg damit! Was tun wir? Wir schreiben das System neu! Wir fangen ganz von Anfang an! Jetzt machen wir alles richtig! Die Geschäftsführung ist dabei! Das Team von 59 Personen ist dabei!
Der letzte Mitarbeiter mit Durchblick sagt, dass er gerne dabei wäre, aber leider mit dem Altsystem schon so im Stress ist, dass er nur ab und zu mal in einem Meeting dabei sein kann und dort etwas sagen wird, wenn ihm etwas einfällt. Die Kollegen sind begeistert. Das Projekt läuft los.
Das Projekt? Was ist das eigentlich genau? Wir wollen ja alles richtig machen, also auch das Projektmanagement! Wie geht das eigentlich? Na, das kann so schwer nicht sein, da malen wir mit Powerpoint doch mal ein paar Balken über eine Zeitachse und ein paar Punkte dazu, die beschriften wir – natürlich mit einem Datum – und nennen sie Meilensteine. Und in 3 Monaten sind wir fertig. Doch ja, bestimmt. Vielleicht kann der Kollege M. noch etwas mithelfen, dann müsste es eigentlich auch in 2 gehen.
4 Monate sind in’s Land gegangen. Die Euphorie ist ein wenig getrübt. Gerüchte machen die Runde, dass am neuen System gar nicht gearbeitet wird. Nein, das stimmt so nicht, kontert die Geschäftsführung, es gebe nur vorübergehend recht viel am Altsystem zu tun, dringend, deswegen ist das Neusystem etwas zurückpriorisiert. Aber nach wie vor ist das neue System die Zukunft.
1 Jahr später. Es ist schon viel passiert! Ein Praktikant hat ein paar Dialoge einer radikal überarbeiteten, futuristischen Benutzeroberfläche skizziert. Ein Team der unerschrockensten Experten hat unter Mithilfe des letzten Mitarbeiters mit Durchblick ein technisches Konzept erarbeitet, wie das Nebenläufigkeitsproblem im Sumpfadapter II durch ein sauberes technisches Design ein für alle mal gelöst werden kann! („das ist im Altsystem gar nicht realisierbar, aber jetzt haben wir endlich die Möglichkeit, das mal richtig zu durchdenken!“)
Zu diesem Zeitpunkt erwächst dem Team ein Visionär: ein Artikel ist ihm erschienen, kurz bevor sein Browser abstürzte. Dort stand geschrieben: die Anforderungen müssen spezifiziert werden! Der Visionär spezifiziert die Anforderungen:
  1. Das System muss flexibel auf beliebige fachliche Anforderungen konfigurierbar sein.
  2. Diese Konfigurationsänderungen können durch einen technisch unerfahrenen Fachadministrator an der Benutzeroberfläche durchgeführt werden.
  3. Die Benutzeroberfläche muss futuristisch sein.
  4. Die Konfiguration muss als XML speicherbar sein, wobei die spitzen Klammern durch eckige ersetzt werden, um Anzeigefehler im Internet Explorer 5.5 zu vermeiden.
  5. Benutzer sind Rollen zugeordnet.
  6. Benutzer haben Rechte oder auch nicht. Manche mehr, manche weniger, das muss konfigurierbar sein. Die Rollen müssen dabei berücksichtigt werden.
  7. Fachliche Prozesse werden durchlaufen, das System unterstützt dabei.
  8. Der Sumpfadapter II darf keine Nebenläufigkeitsprobleme verursachen.
  9. Vollautomatische Selbstkonfigurierung des Systems ist erst in Realisierungsstufe 2 erforderlich, muss aber unbedingt im Design berücksichtigt werden.
An dieser Stelle ist der Visionär von seiner schöpferischen Tätigkeit ermattet. Der Praktikant korrigiert die zahlreichen Rechtschreibfehler und arbeitet den Text in das in der Firma verbindliche Dokumententemplate ein.
Die Geschäftsführung ist begeistert. 15 Entwickler werden abgestellt, um das Neusystem auf Basis der Anforderungen zu entwickeln. Sie fangen schonmal mit dem Sumpfadapter II / neu an. Dabei evaluieren sie EJB 3.0, Spring, Hibernate, Visual Basic .net und No-SQL-Datenbanken. Ein Teilteam überprüft die Möglichkeit, beliebige Fachanforderungen konfigurativ abzubilden über die Programmiersprache F#, ein weiterer Programmierer prüft unter Anleitung des letzten Mitarbeiters mit Durchblick die Eignung der Programmiersprache Brainfuck 2D für denselben Zweck.
Ein in der Hackordnung niedrig angesiedelter Teamleiter übernimmt mit einem kleinen Team von langweiligen Entwicklern für 3-4 Monate die Wartung des Altsystems, bis das Neusystem zur Verfügung steht.
2 Jahre später:
Der in der Hackordnung niedrig angesiedelte Teamleiter samt Team hat – ungestört vom anderweitig abgelenkten letzten Mitarbeiter mit Durchblick – vor 23 Monaten das Prinzip durchgesetzt, dass alle Wartungsanforderungen an das Altsystem mit einer strukturierten Beschreibung der fachlichen Zielsetzung versehen werden müssen. Vor 22 Monaten fiel ihm auf, dass die Putzfrau vor 10 Jahren den Auftrag erhalten hatte, alle Releases des Altsystems zu testen und die Testresultate im Inneren des Putzmittelschranks aufzuhängen. Um diesem Auftrag gerecht zu werden, führte sie ein Notizbuch, in dem sie die gesamte Fachfunktionalität des Altsystems fortgeschrieben hatte. Vor 18 Monaten war ein Praktikant mit der Aufgabe fertig, die Notizen der Putzfrau in ein Fachkonzept zu konsolidieren. Vor 16 Monaten konnte der Teamleiter durchsetzen, dass die Putzfrau in seinem Team als Testmanagerin arbeitete, ein unvorsichtig am Büro vorbeilaufender Passant ohne aktuelles Arbeitsverhältnis wurde stattdessen mit der Reinigung des Büros beauftragt. Zu ungefähr demselben Zeitpunkt hatte ein Subteam von 2,5 langweiligen Entwicklern eine experimentelle Version des Altsystems fertig, aus dem 90% des existierenden Codes entfernt waren. Eine Gruppe von 30 Pilotanwendern erklärte geschlossen, keinerlei Unterschied zwischen dieser Version und der „Vollversion“ entdecken zu können. Vor 10 Monaten wurde – nach einem halben Jahr Regressionstests durch ein Team der Ex-Putzfrau – grünes Licht gegeben, die 10%-Version produktiv einzusetzen. Das Team von 2,5 langweiligen Entwicklern hatte inzwischen den Code in 3 Schichten refaktoriert. Die Präsentationsschicht erwies sich als so trivial, dass ein Student sie im Rahmen seiner Masterarbeit durch eine futuristische Web-GUI mithilfe eines etablierten Frameworks ersetzen konnte. Inzwischen sind die historischen Datenbankzugriffsobjekte durch einen O/R-Mapper ersetzt und die verwobenen Objektreferenzen in der Geschäftslogik analysiert, nach fachlichen Zuständigkeiten strukturiert und durch ein IoC-Framework ersetzt worden.
Das Kernteam des Neusystems erkennt zu diesem Zeitpunkt, dass das neue Design des Sumpfadapter II leider inhärent nicht mit osteuropäischen Zeichen im anzubindenden Datensumpf zurecht kommen kann und beschließt eine komplette Neuentwicklung. Der letzte Mitarbeiter mit Durchblick zusammen mit dem Brainfuck 2D-Entwickler hat einen validierenden Parser für SOAP-Messages in Brainfuck 2D fertig gestellt.
So, und was lernen wir daraus? Es ist ziemlich unsinnig, mit einer Truppe von Leuten, die nie in ihrem Leben mit Erfolg versprechenden Methoden Software entwickeln durften, ein System komplett neu aufbauen zu wollen: sie werden nicht wissen, wie es geht. Dagegen ist es durchaus möglich, mit Menschen, die unter vielen und drückenden Problemen leiden, Methoden zu erarbeiten, wie ein Problem nach dem anderen gelindert und schließlich gelöst werden kann. Und so lernen sie Punkt für Punkt kennen, wie man Software erfolgreich entwickelt.
PS: Nein, natürlich habe ich nichts gegen Brainfuck 2D. Das ist eine vollwertige Programmiersprache. Bestimmt.