Große Softwaresysteme zu bauen ist komplex – stimmt, aber warum ist das so? Warum dauert es ewig, Software zu bauen und warum merkst du schon nach kurzer Zeit, dass die “neue”, “fertige” Software bereits anfängt zu “rotten”? Warum hält Software nicht 20 Jahre, ohne dass du daran arbeitest und warum werden Migrationen immer teurer, um so länger du wartest?
Fragen über Fragen, die sich so ziemlich jeder, der Software baut oder bauen lässt, fragt und nur selten gute Antworten bekommt. Auch in diesem Artikel werden wir natürlich nicht alles klären können – soviel ist mal sicher! Wie baust du Software für die Zukunft, die irgendwann in Produktion geht, in Produktion bleibt und sogar weiterentwickelt werden kann? Software, mit der die Nutzer zufrieden sind.
Software zu bauen ist Handwerk – ja, auch wenn nahezu jeder studiert hat, lernt man im Studium nur die Grundlagen und das Handwerk später im Job. Handwerk in der Softwareentwicklung hat den Anspruch, etwas mit Qualität auszuliefern. Handwerk und Qualität sind die zwei wichtige Begriffe, die oft zu kurz kommen. Oft wird Software irgendwie gebaut bis sie funktioniert und dann aus Angst nicht wieder angefasst. Die Auslieferung findet am Ende statt und nicht selten ist das Ergebnis eine Katastrophe – ab da wird es dann teuer.
Manuelle Tests bei der Entwicklung reichen nicht aus, um Qualität sicherzustellen und manuelle Tests in Produktion schon gar nicht. Kompromisse bei der Qualität führen immer zu Problemen. Daher gilt “Test First'' und Tests müssen automatisiert werden. Die richtigen Tests zu schreiben, ist dabei wichtig. Gibt es nur Integrationstests oder Systemtests ist definitiv was im Argen, denn diese Tests sind langsam und testen nur das Zusammenspiel. Klar, das ist wichtig, aber es reicht allein nicht aus und ist ein Indiz dafür, dass du zu spät angefangen hast zu testen. Leichtgewichtige Unit-Tests zeigen, dass die Software das macht, was erwartet wird und stellen sicher, dass das auch so bleibt. Ist der Test noch so trivial, lohnt es sich, ihn zu schreiben.
Jede Änderung am System wird auslieferungsfähig gemacht – klar, fähig bedeutet nicht, dass alles immer ausgeliefert wird, aber man sollte es können. Das “Bauen” von Software darf nicht manuell erfolgen, sondern ist zu automatisieren. Der Grad der Automatisierung sollte so weit gehen, dass Änderungen nur dann akzeptiert werden, wenn die Qualität gehalten werden kann und die erwartete Funktionalität erhalten bleibt. Klar vier Augen Prinzip wie Code Review sollte auch stattfinden.
Mit diesem Ansatz wird sichergestellt, dass zu jeder Zeit eine wichtige Änderung ausgeliefert werden könnte.
Alles aus einer Hand – genau wie bei Handwerkern. Gibt es verschiedene Abteilungen oder Teams, die für bestimmte Bereiche zuständig sind, folgen Kommunikations- und Zuständigkeitsprobleme. Interdisziplinäre Teams, die Fachlichkeit, Backend, Frontend, Devops, Daten und Betrieb gemeinsam verantworten, sind schneller, effektiver und liefern höhere Qualität. Es darf keine Kopfmonopole geben, damit das Risiko nicht mehr handlungsfähig zu sein sinkt. Jeder sollte in der Lage sein, den Job des anderen machen zu können. Klar, das ist aufwändig, aber es lohnt sich massiv für die Zukunft. Zentrale Dokumentation macht es neuen Teammitgliedern leichter produktiv zu werden und langfristig sparst du Zeit und Geld.
Reise mit leichtem Gepäck – ja, das lässt sich auch auf Software übertragen. Nicht selten machst du dir zu wenig Gedanken darüber, was für Abhängigkeiten durch vermeintlich kleine Bibliotheken oder Frameworks entstehen. Diese Abhängigkeiten machen dir später das Leben schwer, denn Änderungen können auf allen Ebenen passieren. Am besten prüfst du sehr genau, welche Abhängigkeiten akzeptierbar sind. Einige Firmen neigen dazu, ihre Abhängigkeiten einzufrieren und sie in einer bestimmten Version zu erhalten. Gar nicht doof, denn so wird sichergestellt, dass die Software auch morgen noch reproduzierbar gebaut werden kann. Das Problem mit den vielen Abhängigkeiten sollte ganzheitlich gelöst werden, ein lokaler Spiegel hilft zusätzlich auf jeden Fall. Kontinuierlich solltest du Abhängigkeiten aktualisieren, denn nur so werden frühzeitig Probleme erkannt und es kann reagiert werden, wenn Änderungen noch leicht durchgeführt werden können. Getreu dem Motto, das eine tun, aber das andere nicht lassen.
Gebaut, um ausgetauscht zu werden – klingt komisch, aber nach der Migration ist vor der Migration. Frameworks und Bibliotheken kommen und gehen – das ist nichts Neues. Software darauf zu optimieren, dass Funktionalität wiederverwendet werden kann, ist weniger wichtig, als sie darauf zu optimieren, ausgetauscht zu werden. In der Softwareentwicklung reagierst du mit Architekturstilen wie Hexagonale Architektur, Onion Architektur oder Clean Architektur. Im Grunde lässt sich all das zusammenfassen, indem du Funktionalität komplett isoliert betrachtest und nur über wohldefinierte Schnittstellen ansprichst. Diese Schnittstellen sollten möglichst einfach gehalten werden und nur das enthalten, was unbedingt nötig ist. Somit ist es relativ einfach, Funktionalität auszutauschen, ohne das restliche System anfassen zu müssen. Wir wollen hier nicht zu technisch werden, aber wenn es dich tiefergehend interessiert, kannst du gerne den Links folgen. Der wichtigste Punkt ist hier, wohldefinierte, einfache und technisch unabhängige Protokolle zur Kommunikation zu definieren. Ganz besonders wichtig hierbei ist es, Dinge wirklich einfach zu halten und nicht für alle eventuellen Anwendungsfälle auszustatten.
Migration auch in der Organisation – ja, Migrationen sind nicht immer technisch. Viel zu oft vergisst du auch Prozesse und die eigene Organisation an die neuen Gegebenheiten anzupassen. Nicht selten wirst du über die Jahre immer weniger schnell und baust eine Mauer, wo keine sein sollte. Arbeitest du hier kontinuierlich daran darauf zu achten, dass solche Probleme gar nicht erst entstehen, macht es dich Mittel bis langfristig deutlich flexibler und schneller. Nach dem Gesetz von Conway definiert die Organisation am Ende die Struktur des Systems. Probleme können also nicht rein technisch angegangen werden.
Der Nutzer ist die wichtigste Person – Software wird nicht gebaut, um sich mit Technik zu beschäftigen. Viel zu oft wird vergessen, dass der Endnutzer die wichtigste Person ist. Wie das Zeug unter der Haube funktioniert und wie toll die Schnittstellen sind, interessiert den Nutzer wenig. Ist der Nutzer von Anfang an einbezogen und basiert die Bedienoberfläche und die damit einhergehende Funktionalität auf den Bedürfnissen des Nutzers, dann ist dieser auch zufrieden. Durchlebst du diesen Prozess kontinuierlich und justiert die Implementierung und Tests nach, wird eine Migration der Funktionalität oder Frontend-Technologie kein Problem darstellen. Wichtig ist nur, dass die Erwartungshaltung des Nutzers erfüllt wird. Ganz wichtig ist auch, dass ein System Fehlerunanfällig ist und mit ausfallenden Fremdsystemen adäquat umgeht. Temporäre Funktionalität nachvollziehbar nicht zur Verfügung zu stellen ist natürlich besser, als ein Absturz, oder falsche Ergebnisse. Hat der Nutzer Vertrauen in das System, bilden sich auch keine Insellösungen oder Ähnliches, die es später auch zu migrieren gilt.
Fazit
Auf wenigen Seiten zu beschreiben, wie du zukunftssichere Software baust, ist Quatsch, aber die wichtigsten Punkte sind genannt und sollten zum Nachdenken und Weiterlesen ermutigen. Vielen reicht die “Vogelflugperspektive” auf so ein Thema, um Veränderungen anzustoßen und genau dazu existiert dieser Artikel. Es ist also nicht ein “Rad” an dem du drehen muss, sondern es ist die konsequente Beachtung einiger weniger Prinzipien.
Kommentare