1. März 2022 von Marius Voskamp
Eine Reise zu guter Software
Wie viele andere Developer habe auch ich meine ersten Schritte in der Softwareentwicklung gemacht, ohne genau zu wissen, auf was ich mich einlassen würde. Die ersten kleinen Versuchsprojekte gingen grandios am Ziel vorbei, waren völlig unbenutzbar und instabil. Doch all diese Probleme schienen mit etwas besserem Code lösbar und damit war mein Ziel gefasst: Programme und Features bauen, die Leute brauchen und gern benutzen. Erst auf dem Weg begriff ich langsam, dass es mit schlichtem Schreiben von Code wohl nicht getan sein würde. Diese Reise dauert bis zum heutigen Tag an. Meine Sicht auf Softwareentwicklung hat sich zwischenzeitlich mehrfach tiefgreifend verändert. Die entscheidenden Erkenntnisse und Sinneswandel würde ich gerne mit euch teilen und euch damit eine Orientierung in der Welt der Softwareentwicklung an die Hand geben.
Vom Wasserfall zum Meer
Meine ersten zwei Schritte in der Softwareentwicklung waren ein „Hello World“ und ein Taschenrechner auf Grundlage einer längst veralteten Technologie namens Windows Forms – eine Technologie, mit der man Programme für Windows-Desktop-PCs schreiben konnte. „Hello World“ ist übrigens ein kleines Programm, was den Text „Hello World!“ anzeigt. Es ist üblicherweise das Erste, was man baut, wenn man eine Programmiersprache erlernt, um zu verstehen, wie sie funktioniert.
Mein „Hello World“ war nicht schön und der Taschenrechner unhandlich – allerdings waren es ja meine ersten Programme und ich ging davon aus, dass es nur Übung bräuchte, um gute Programme zu schreiben.
Etwa drei Jahre später – meine Berufsausbildung war abgeschlossen – arbeitete ich inzwischen an einem Programm zur Maschinensteuerung und befasste mich mit der Programmierung von Features und damit, wie man dieses Programm von unseren Computern auf die Computer der Kunden bekommt. Das Vorgehen bei all meinen Aufgaben war bis hierhin immer gleich: Zuerst wurde genau definiert, was bei der Entwicklung rauskommen soll, ein Plan erstellt, wie man zu diesem Ziel kommt, und dann wurde losprogrammiert. Dieses Vorgehen nennt man Wasserfallmodell.
Das Wasserfallmodell mag auf den ersten Blick sinnvoll und charmant einfach erscheinen: Das Ziel definiert man präzise, die Reise dahin plant man detailliert vor und dann folgt man nur noch dem vorgesteckten Weg. Das Problem bei diesem Vorgehen ist, dass es weder gut mit der Natur unserer Welt harmoniert noch mit der Natur der Softwareentwicklung. Die Welt wandelt sich stetig und immer schneller, was sich wandelnde Anforderungen, Prioritäten und Ziele zur Folge hat. Softwareentwicklung ihrerseits ist ein kreativer Prozess, bei dem man auf dem Weg zum Ziel Lösungen für Probleme finden muss, die erst auf dem Weg erkennbar werden.
Kurzum: Ich entwickelte lang an großen Features, wurde selten planmäßig fertig und am Ende war das Resultat selten das, was wirklich gebraucht wurde. Das war sowohl für mich frustrierend als auch für die Benutzer und damit auch weit von meinem Ziel entfernt.
In dieser Situation lernte ich das erste wichtige Element kennen, um gute Software zu entwickeln: Agilität.
Agilität ist eine Initiative, die von einer kleinen Gruppe erfahrener Softwareentwickler vor mehr als 20 Jahren mit dem Agilen Manifest begründet wurde und die durch ihre schnörkellosen, zielorientierten und flexiblen Prinzipien die Welt der Softwareentwicklung erobert hat. Das Agile Manifest besteht aus vier Grundsätzen, die Kooperation, Flexibilität und funktionierende Software in den Mittelpunkt von Softwareentwicklung stellen (s. https://agilemanifesto.org/). Das heutzutage wohl bekannteste agile Vorgehen ist Scrum.
Es wurde von zwei der Verfasser des Agilen Manifests entwickelt und wird bis heute von ihnen gepflegt. Scrum ist ein leichtgewichtiges Rahmenwerk, das Verantwortlichkeiten, Events und Artefakte für die Entwicklung in kleinen Teams definiert. Damit soll das Team in die Lage versetzt werden, schrittweise eine werthaltige und zu jeder Zeit einsatzfähige Software zu entwickeln. Anforderungen und Umsetzung werden hierbei regelmäßig miteinander abgeglichen in der Erwartung, dass es Missverständnisse, Hindernisse, Prioritätsänderungen und Zieländerungen geben wird. Während Softwareentwicklung also vom Wasserfallmodell als ein planbarer Sprung von der Spitze eines riesigen Wasserfalls begriffen wird, versteht Scrum sie als Reise über ein weites Meer, wo regelmäßige Kurskorrekturen erforderlich sind. Damit passt Scrum deutlich besser zu einer sich stetig wandelnden Welt und ist zum De-facto-Standard für moderne Softwareentwicklung geworden.
Von der Egoperspektive zu erfundenen Personen und Kurzgeschichten
Ich schrieb zwischenzeitlich passgenauere Features und verstand technische Probleme immer besser. Das resultierende Programm war aber immer noch unhandlich und die Funktionen schwer zu finden und zu verstehen. Der Code machte also immer noch nicht genau das, was ich wollte. Ich verstand auch viele der Techniken und Konzepte nicht, die meine Kolleginnen und Kollegen verwendeten. Also begann ich ein Informatikstudium in der Erwartung, dass ich dort Antworten finden würde. Zur etwa gleichen Zeit wurde in meiner Firma eine neue Abteilung gegründet, die sich speziell mit der Entwicklung der UI (User Interface) befassen sollte, der ich dann logischerweise auch beitrat. Es galt schließlich zu lernen, wie ich zu meinem Ziel komme. Bis hierhin baute ich UIs nach dem Ansatz: Was könnte man mit dieser Software machen wollen?
Hier lernte ich zwei weitere wichtige Elemente kennen, die helfen gute Software zu entwickeln:
Usability Engineering und Requirements Engineering.
Um gute Programme für User zu schreiben, muss man verstehen, wer diese User sind und wobei ihnen das Programm helfen soll. Das erscheint erstmal trivial, gestaltet sich aber in der Realität häufig schwierig. Denn meist gibt es nicht nur einen User und häufig kennt man ihn nicht persönlich, geschweige denn, dass man ihn wiederholt, intensiv mit Fragen über Ziele und Wünsche löchern kann. Die Disziplin, die sich mit dieser Problematik befasst, heißt Usability Engineering. Sie kennt Techniken und Methoden, die einem helfen zu verstehen, wer die Benutzer sind und was sie wollen.
Eine sehr hilfreiche dieser Techniken aus dem Bereich Usability Engineering sind Personas. Eine Persona ist eine Beschreibung eines typischen, aber fiktiven Benutzers mit Namen, Alter, Bild, Persönlichkeitsmerkmalen, Kenntnissen, Interessen und Wünschen. Eine solche Persona hilft dabei, sich in die Lage des Benutzers zu versetzen und so den eigenen Lösungsansatz zu hinterfragen oder ihn mit Kollegen zu diskutieren. Im Zweifel muss man bereit sein dem Kredo „Kill your darlings“ zu folgen, denn nur weil einem selbst eine Lösung gefällt, bedeutet das noch lange nicht, dass es eine gute Lösung für den Benutzer des Programms ist.
Eine sehr hilfreiche Technik aus dem Bereich Requirements Engineering sind User Stories. Sie sind kurze Beschreibungen von Anforderungen nach der Formel „Als [Kunde] möchte ich [Aktion], um [Mehrwert] zu erhalten“, die in der Regel durch Akzeptanzkriterien konkretisiert werden. Wichtig beim Befüllen der Platzhalter ist, dass klar wird, wer sich etwas wünscht, was diese Person wünscht und worin der Nutzen besteht, wenn dieser Wunsch erfüllt wird. Die Akzeptanzkriterien sollten die Anforderung konkretisieren und die Umsetzung der Anforderung messbar machen.
User Stories ermöglichen es, Anforderungen schnell in kleinen, handhabbaren Einheiten mit allen wesentlichen Informationen zu erfassen. Sie harmonieren dadurch auch hervorragend mit agiler Entwicklung, da ihr geringer Umfang und Aufwand die Flexibilität befördern.
Usability Engineering und Requirements Engineering bieten eine Vielzahl von großartigen Werkzeugen, um gute Softwareentwicklung zu betreiben. Entscheidend ist, dass man ein passendes Werkzeug für die jeweilige Herausforderung auswählt. Bei der Auswahl sollten Aufwand und Nutzen stets in einem gesunden Verhältnis stehen.
Vom Lötkolben zur Fertigungsstraße
Ein paar Jahre vergingen. Zwischenzeitlich hatte ich gelernt mich in Nutzer hineinzuversetzen, Anforderungen aus Wünschen herauszuschleifen und auf deren Grundlage gut benutzbare Software zu designen. Das Ziel schien zum Greifen nah, war aber praktisch nie zu erreichen. Zwar verstand ich nun theoretisch, wie ich „gute Software, die man gerne benutzt“ erschaffen kann, aber praktisch nie wurde die Entscheidung getroffen, den dafür nötigen Aufwand zu investieren. Es gab zu viele andere Baustellen, zu viele dringende Aufgaben und immer wieder kosteten menschliche Fehler Zeit. Das Studium der Informatik war inzwischen abgeschlossen und ein wirtschaftlich geprägtes Studium folgte. Es galt zu verstehen, warum Entscheidungen gegen gute Software getroffen werden und ob sich daran etwas ändern lässt. Die Neugier nach Neuem trieb mich zur Suche nach einer anderen Firma an, die vielleicht anders entscheiden würde, und ich landete in einem völlig anderen Feld mit einer völlig anderen Mentalität. Die Firma war deutlich kleiner und wie immer war viel zu tun.
Hier lernte ich weitere wichtige Elemente, um gute Software zu entwickeln: Automatisierung, Qualitätskontrolle und DevOps.
An neuen Aufgaben mangelt es in der Softwareentwicklung nie, jedoch immer an der Zeit. Aufgaben, die wiederholt anfallen, können in Summe dazu führen, dass wichtige Zeit für die eigentliche Weiterentwicklung der Software verloren geht. Man sollte also prüfen, ob sich eine zeitaufwändige, regelmäßige Aufgabe mit vertretbarem Aufwand automatisieren lässt. Automatisierung kann je nach Komplexität der Aufgabe von einem simplen Shell Script bis hin zu einer vollausgebauten CI/CD reichen. CI steht übrigens für Continuous Integration und bezeichnet die Automatisierung von Prozessen zum Bereitstellen der Software. CD hingegen für Continuous Delivery und bezeichnet die Automatisierung von Prozessen zum Ausliefern der Software.
Auch Fehler stehlen bei der Softwareentwicklung wertvolle Zeit. Das Beste wäre keine Fehler zu machen. Das ist allerdings unmöglich. Daher sollte man das Zweitbeste machen und sie vermeiden. Mit den Fehlern, die trotzdem entstehen, macht man das Drittbeste und verringert ihre Auswirkungen.
Fehler kann man unter anderem durch Automatisierung und Standardisierung vermeiden. Das reicht vom Automatisieren des Zusammenbauens der Software bis hin zum Einsatz von Standard-Komponenten und -Frameworks, etablierten Architekturen, Mustern und Prinzipien.
Die Auswirkung von Fehlern verhindert man am besten, indem man sie früh findet. Gängige Mittel hierfür sind Unit Tests, Integration Tests, System Tests, Testautomation, statische Codeanalysen und Code Reviews. Eine Daumenregel für die Kosten von Fehlern ist, dass ein Fehler mit jeder Stufe, die er in Richtung Kunde gelangt, zehnmal teurer wird. Den Fehler während der Entwicklung zu finden, kostet ein paar Sekunden. Wenn der Fehler allerdings die Stufen Unit Test, Integration Test, Code Review, System Test bis hin zum Kunden springen kann, dann kann aus diesen paar Sekunden Aufwand von mehreren Tagen entstanden sein, weil der Fehler gemeldet, dokumentiert, reproduziert und gefixt werden muss. Hieran sind schon mehrere Personen beteiligt, ganz zu schweigen von dem Imageverlust beim Kunden.
Kommunikation und Vernetzung von Entwicklung und Betrieb hilft zusätzlich Fehler strukturell zu vermeiden und früh zu finden. Hier hat sich in den letzten Jahren die Rolle DevOps herausgebildet, die das Bindeglied zwischen diesen beiden Welten darstellt und die Kooperation beider Welten fördert.
Automation, Standardisierung und DevOps bieten Zeitvorteile, Fehlervermeidung und Fehlerfrüherkennung. Viele Schritte wiederholt per Hand zu machen oder Bauteile selbst zu entwerfen, kann wertvolle Zeit kosten, die in die Entwicklung der Software investiert werden kann.
Wenn ein Lötkolben auch flexibel einsetzbar ist, hat er immer noch den Nachteil, dass er von einem Menschen bedient wird, der Fehler machen kann. Eine Fertigungsstraße hingegen ist kaum flexibel, hat aber den Vorteil, dass sie kaum Fehler macht, wenn sie erstmal steht. Die Wahl sollte hier immer zugunsten von Zeit und weniger Fehlern ausfallen.
Fazit
Es dürfte euch wenig überraschen, dass es keinen Golden Hammer gibt, um gute Programme zu schreiben. Der Golden Hammer beschreibt ein Phänomen in der Softwareentwicklung, bei dem man versucht, alle Probleme mit dem gleichen, vertrauten Werkzeug zu lösen, obwohl das Problem möglicherweise ein anderes Werkzeug erfordern würde. Es wird häufig zusammengefasst mit: „Wenn du nur einen Hammer hast, sieht alles aus wie ein Nagel.“
Ein Vorgehensmodell, eine Programmiersprache, eine Technologie sind selten die Antwort auf alle Probleme, die euch begegnen werden. Um gute Programme zu schreiben, müsst ihr Probleme analysieren können und für verschiedenartige Probleme passende Werkzeuge im Werkzeugkoffer haben (oder zumindest wissen, wo ihr passende Werkzeuge finden könnt). Glücklicherweise ist die Gemeinschaft der Softwareentwicklung geprägt von großer Hilfsbereitschaft und einem regen Austausch, sodass man selten mit einem Problem allein dasteht. Ob ihr das passende Werkzeug nun auf Stackoverflow (ein Internetforum, auf dem Softwareentwickler Fragen stellen können und andere diese beantworten), GitHub (eine Internetplattform, auf der man Quellcode bereitstellen kann) oder in einem Blog findet, ob ihr es im Studium, in einem Buch oder Podcast findet oder ob ihr es bei einer Konferenz, einem Meet-up oder einfach bei einem Arbeitskollegen findet, ist nicht entscheidend. Wichtig ist überhaupt zu suchen und zu fragen. Zu verstehen, dass ein Werkzeug nicht zu einem Problem passt, und nach einem passenderen zu suchen ist ein Zeichen von Professionalität und unterscheidet Basteln von Entwickeln.