Welche Tätigkeiten oder Bestandteile sind bei der Entwicklung von großer bzw umfangreicher Software wichtig, werden aber oft vernachlässigt?
Ich hatte bisher häufiger mit großen Projekten zu tun und in der Regel fehlt die Zeit, die Architektur entsprechend anzupassen.
Software wächst eigentlich organisch. Das heißt, man baut etwas auf, wenn es viel benutzt wird, baut man eine verbesserte Version und reißt das alte ab bzw. macht das Vorhandene stärker.
Der freudige Ausruf "Wir haben da doch schon was" ist teilweise legendär. Je nach Entscheiderebene wird er in der gleichen Sprechweise freudig oder eben ironisch gemeint.
Meistens wird das vorhandene eben nicht verstärkt und den Anforderungen angepasst, sondern unverändert beibehalten und die Anforderungen dem Vorhandenen angepasst. Damit wächst Software aber nicht mehr organisch, sondern eckig und kantig, die Kräfte in der Software werden nicht organisch geleitet, sondern entlang von Sollbruchstellen an die - wenn sie dann brechen - ein paar Pflaser ("Patches") geklebt werden.
Das bedeutet nicht, dass solche Software nicht auch gut funktionieren kann. Aber damit wird sie komplexer als notwendig. Was man beim organischen Wachstum gespart hat, weil man einfach nur die Anforderungen gebogen hat, führt dazu, dass je größer die Software wird, dass man sich umso mehr um vorhandene Strukturen herumbiegen muss. Es ist irgendwann nicht mehr nachvollziehbar, wann welche Sonderbedingung mit welcher anderen Sonderbedingung kollidiert. Plötzlich nutzen zwei Unterprogramme die gleichen globalen Variablen. Jow, globale Variablen sollte man nicht benutzen, aber in Fortran heißt das Common Block und ist ein Feature. Und sehr große Software ist eben auch häufig schon ein paar Jahre alt.
Und da wären wir gleich beim nächsten Problem: Trends. Eine große Software, die sich den neusten Trends verschreibt, verlässt in Teilen ihre vorhandene Architektur. Vielleicht ist der neue Trend eine prinzipiell gute Idee. Aber wenn er ein Fremdkörper in der Architektur ist, dann ist es auch eine neue Sollbruchstelle.
Wenn es plötzlich neue und alte APIs gibt, wenn man das Gefühl dafür verliert, was die Architektur eigentlich von einem will und in welcher Form sie Ergebnisse liefert, dann macht man es mal richtig und mal falsch. Anekdote aus der Realität: Während eines Meetings die Frage "Dafür haben wir doch bestimmt einen Standard?" Antwort: "Dafür haben wir mehrere Standards."
Vielleicht ist es also besser, den Trend auszulassen und stattdessen die schlechtere Architektur beizubehalten. Oder - wenn man sich sicher ist, dass das nicht nur ein Trend ist, sondern sich in Zukunft auszahlt, auch die komplette Architektur anzupassen.
Gute Entwickler haben deswegen Guidelines. Bisher war ich noch in keinem Unternehmen, wo das funktioniert hat.
Große Software hat noch ein Problem, was vermutlich in 10–20 Jahren ein Trend werden wird: Abwärtskompatiblität und der Bruch damit. Ich muss alte Dateiformate lesen können, die alte Versionen der Software geschrieben haben. Arbeitsschritte, die vor 10 Jahren ein Trend waren und heute anders gemacht werden, muss man weiterhin anbieten, weil wichtige Kunden es lieber so machen wie vor 10 Jahren. Und das heißt, ich darf keine ungenutzten oder veralteten Features entfernen, weil sie im Dateiformat ja trotzdem auftauchen könnten und Kunden keine Lust haben sich umzugewöhnen.
Also baut man die neuen Features um die Strukturen herum, die man aus Abwärtskompatiblität nicht abreißen will.
Am Ende erhält man nicht eine organisch gewachsene schmale Säule, die auch mal was aushält, sondern ein breit verzweigtes undurchschaubares Geflecht, was als ganzes die Belastungen der Tests aushält, aber deutlich empfindlicher auf fragwürdige Eingaben oder Belastungen reagiert und wenig Reserven hat auszugleichen.
Und so hatte ich das Vergnügen, dass in einer auf mehreren Servern verteilten Software bei mir ein Fehler ankam, weil ein anderer Server durch eine fehlerhaftes Datenpaket verwirrt wurde, welches von einem anderen Server versandt wurde, welches dort durch einen kaputtes Objekt erzeugt wurde, welches entstanden ist, weil dort ein kaputtes Objekt eine Exception ausgelöst hat. Es war also nicht ein Bug, sondern vier, die wenn sie passierten am Ende eine Exception bei mir auslösten. Das ganze ziemlich willkürlich, weil das erste kaputte Objekt tagelang in einer Liste hängen konnte, bis es die Erzeugung des zweiten Objektes sabotiertierte, welches dann dazu führte, dass die übertragenen Daten kaputt waren usw.
Statt die Komponenten sauber zu kapseln, hat man das Fehlermanagement versaut - Fehler sind ja keine Features und die Entwickler sind dazu angehalten, schnell neue Features dran zu patchen. Wir haben da ja schon was. Oh, der alte Aufrufer kann mit unseren Exceptions nichts anfangen… für das neue Feature nicht wichtig.
Dokumentation halte ich übrigens häufig für Kontra-Produktiv. Denn meist ist sie schlampig, wird bei Veränderungen nicht angepasst. Im Idealfall schafft man sich Standards, wie Dinge zu heißen haben, wie sie aufgebaut sind. Und auch hier versauen Trends Dokumentation:
Wird ein Objekt nur über seine API betrachtet, ist das Objekt etwas, was gewisse Dinge kann. Die Repräsentation der Daten im Speicher wird gekapselt, also muss sie den Entwickler nicht interessieren. Üblicherweise hat eine Klasse aber einen Sinn und der Sinn ist die Verarbeitung von Daten und die Daten, die man verarbeitet, sind im Speicherlayout dokumentiert. Einer Klasse sieht man ihre Bedeutung eher dadurch an, welche Daten sie verbindet als durch die Methoden, die sie anbietet.
Ein Datensatz mit zwei doubles x und y ist deutlich eher als ein 2D-Punkt zu erkennen, als die Erkenntnis, dass man das Objekt voneinander subtrahieren kann oder mit einem Vektor-Objekt addieren kann.
Was wichtig ist sind Tests. Und damit sind wir ganz schnell bei semantiklastigen, stark statisch typisierten Programmiersprachen. Wer auf die Idee gekommen ist, C++ mit Java zu ersetzen will keine großen Projekte bauen, weil Java hier im Vergleich nicht viel zu bieten hat. Oder Python. Beides Sprachen, die Hypes erfahren haben, weil sie für kleine Projekte interessant sind. Weil man damit einen schnellen Start hat. Aber halt keinen langen Atem, bzw. es wird einfach teuer und zeitintensiv, weil eben die semantische Unterstützung der Sprache fehlt, die Architektur organisch nachwachsen zu lassen. Also lernt man es nicht. Also macht man es nicht. Man akzeptiert, dass Software ab einer gewissen Größe einfach Fehler haben muss. Die Größe der Software wird zur Entschuldigung für die Qualität und damit vernachlässigt man, große Software qualitativ zu machen, also organisch wachsen zu lassen.