Heutige Softwareentwicklung (1) – Babysteps?

In den letzten Jahren kamen mir bei der Softwareentwicklung (Fokus PHP und JavaScript) wenig neue Dinge unter. Neu im Sinne von revolutionär oder wenigstens so, dass ich existierende Probleme endlich lösen, ohne sie nur zu entschärfen. Es geht mir hier um grundsätzliche Probleme, keine Projektspezifischen. Welche das sind, schlüssele ich später auf.

Highlights der letzten 10 Jahre

Zunächst eine kleine Auflistung der Highlights aus der PHP- und JavaScript-Welt, die mir so untergekommen sind, welche zu einer inkrementellen Verbesserung der Programmierung (+ Skripting) und Softwareentwicklung geführt haben.

PHP7 und HHVM

Existierende Implementierungen schrauben an der Performance und den Features, entwickeln aber die Sprache/Kern nur inkrementell weiter. Ausführungen über die beiden konkurrierenden Systeme kann man z.B. hier finden.

Die Entwicklung ist nachzuvollziehen und sinnvoll, da man Kompatibilität zu alter Software erhalten will. Gleichwohl möchte man Erleichterungen für Programmierer einbauen, welche die tägliche Arbeit erleichtern.

Wie sich eine Sprache jedoch „anfühlen“ könnte, wenn man sie auf einmal mit Funktionalen oder Logischen Paradigmen ausstattet, wäre mal zu testen.

Zusammenfassend hat sich also die Ausdrucksmächtigkeit der Sprache erhöht und durch verschiedene Hilfsmittel wird die tägliche Arbeit für den Programmierer erleichtert.

NodeJS + ECMA5+ + TypeScript + NPM

Im JavaScript-Bereich hat sich die letzten 10 Jahre sehr viel getan. Die vorher sehr unkontrollierte und amateurhafte Erstellung von JS-Code hat sich hoch professionalisiert. Weiterhin wurde durch Einführungen von TypeScript oder ECMAScript 5+ auch die Entwicklung für und mit JavaScript selbst weiterentwickelt. An sich wurden inhaltlich nur Konzepte aus anderen Sprachen portiert und integriert.

Zusammenfassend wurde dabei die Ausdrucksmächtigkeit der Sprachen für den „JavaScript-Bereich“ erhöht, die Qualität der Entwicklungsumgebungen hat sich erhöht (Atom, Electron,…) und mit TypeScript wurde ein Superset von JavaScript eingeführt, welches für Großprojekte (+1 Mio. LOC) ausgelegt ist und die Wartung erleichtert.

Composer

Composer ist ein super Tool, um Abhängigkeiten zwischen eigenen und fremden PHP-Bibliotheken zu verwalten. Es hat damals dazu geführt, dass der Wildwuchs im PHP-Bereich schrittweise abgenommen hat und man anfing sich seine Anwendung modulartig zusammenzustellen.

Ich habe das Gefühl, dass composer hat dazu geführt hat, dass Programmierer verstärkt in Modulen denken, wenn sie eigene Software entwickeln. Das kann die Fragmentierung der Community verringern und bestimmte Projekte pushen, was wiederum zu höherer Code-Qualität führen kann.

Bleibt festzuhalten: Software wird heute mehr als Modul bzw. Modulsammlung gedacht, womit eine höhere Wahrscheinlichkeit einhergeht, dass sie für eigene Projekte wiederverwendbar ist.

Aspektorientierte Programmierung

Bei der Aspektorientierten Programmierung liegt der Fokus darauf, generische Funktionalitäten klassenübergreifend zu verwenden. Laut Wikipedia versucht man logische Aspekte von der Geschäftslogik zu trennen. Soweit die Theorie.

Nach einer Weile habe ich einigermaßen verständliche Beispiele für den Einsatz von AOP in PHP finden können. In diesem Git-Repository wurde ich fündig. Darin wird AOP benutzt, um sich in den Programmablauf einzuklinken. Z.B. wird eine Methode eingeschleust, welche vor einer anderen aufgerufen wird. Dies geschieht, indem man in den Funktionskopf der einzuschleusenden Methode ABC ein @before xyz() einträgt. Die Funktion xyz wird danach immer vor der Funktion ABC aufgerufen.

Meinem Verständnis nach wird AOP zur Manipulation des Programmablaufs eingesetzt (ein-hooken z.B. vor einem Funktionsaufruf). Ich vermute, dass damit die Erweiterbarkeit und Wiederverwendbarkeit von Fremdmodulen erhöht werden kann.

Railway Oriented Programming

Ein sehr interessantes Konzept, welches für Java auf heise.de vorgestellt wurde. Der Hintergrund für die Verwendung ist, dass man mithilfe funktionaler Programmierung eine klare Fehlerbehandlung hat und vermeidet, dass das Programm in (zu viele) verschachtelte if-Anweisungen ausartet.

Diese Illustration ist von Scott Wlaschin und seinem Docker auf speakerdeck.

Die Idee ist folgende. Jede Funktion im Programm liefert nur noch eine Art Result zurück, welches Success oder Failure ist. Man geht von einem „Happy Path“ aus, welcher der Verkettung der Success-Werte der Funktionen entspricht (siehe Illustration, grüner Weg: Validate => Update => Send). Sobald ein Fehler (Failure) geworfen wird, verlässt man diesen Pfad und begibt sich in die Fehlerbehandlung (siehe Illustration, roter Weg).

Auf den ersten Blick ist dieses Konzept sehr interessant. Leider konnte ich auf die schnelle nicht durchsteigen, wie man es für PHP umsetzen könnte. Das Konzept ist für Situationen gedacht, in denen der Programm-Code aus vielen ineinander geschachtelten if-Anweisungen besteht. Durch die Aneinanderreihung reduziert man nicht nur die LOC, sondern erhält auch einen klaren „Fehler-Weg“. Für mich ist dieses Konzept bisher nur eine Verdeckung existierender Strukturen, ohne dabei real existierende Komplexität abzubauen. Ähnliche und weitere Kritik kommt auch von den Lesern des Beitrags, z.B. hier und hier.

Aktuelle Problemstellungen

Die eingangs angesprochenen Probleme möchte ich hier kurz präzisieren. Meiner Meinung nach gibt es heutzutage keine befriedigende Lösung für folgende Problemstellungen.

  1. Erweiterbarkeit von Software auf Klassen-Ebene – Es gibt Entwickler, welche Methoden und Properties in Klassen als private markieren. Das mag vielleicht von der Theorie der Softwareentwicklung so empfohlen werden, jedoch ist es in der Praxis Mist, wenn ich auf diese Elemente in einer abgeleiteten Klasse nicht zugreifen kann. Genauso mies ist es, wenn man Klassen als final kennzeichnet. Hier gibt es zwar eine Diskussion mit dem Tenor, dass es OK ist, aus Sicht der Erweiterbarkeit ist final jedoch hinderlich.
  2. Software als Ganzes ist nicht für Integration in andere Software gedacht – Web-Software könnte ich z.B. per iFrame in meine „Seite“ laden und damit verschiedene Systeme kombinieren. Wenn sie dann jeweils noch per Module erweiterbar sind, umso besser (z.B. um die User-Authentifizierung zu koppeln). Jedoch gibt es viele Systeme (z.B. OwnClodu), welche nicht erlauben, dass man sie per iFrame benutzt. Man sperrt sich dagegen, teil von etwas größerem zu sein. Das mag aufgrund von Sicherheitsüberlegungen so eingerichtet wurden sein, ist jedoch in der Praxis hinderlich, besonders dann, wenn man die Sperre nicht ohne weiteres deaktivieren kann. Software sollte immer als Modul gedacht werden, auch wenn sie eigenständig ist.
  3. Ständige Defizite zwischen Konzept und Code – Manche Leute entwickeln Software erst auf dem Papier und gießen sie danach in Code, manche machen es umgekehrt und manche parallel. Jedem gemein ist jedoch, dass es immer Defizite zwischen dem Konzept und dem Code gibt. Im Konzept eingeschlossen ist für mich auch die Dokumentation. Je nach Vorgehensmodell kann diese Differenz bestehen zwischen
    • Pflichtenheft und Quellcode (Wasserfallartiges Vorgehen)
    • Use Cases und Quellcode (Scrum)

    Andere Formen der Dokumentation, was die Software „können“ soll, mit eingeschlossen (z.B. Funktionsköpfe, Konfigurationsdateien,..). Der Hintergrund dieses Defizits ist, dass sich Software entwickelt:

    • Es treten neue Erkenntnisse während der Entwicklung auf (z.B. Sicherheitsentwicklungen, neue Software, neue Anforderungen von außen wie Gesetzesänderungen, …)
    • vormals geplante Abläufe sind unzureichend/fehlerhaft/… und müssen geändert werden
    • Kundenwünsche und -vorstellungen ändern sich; nicht selten wird mitten im Projekt entschieden, dass statt A nun B gemacht wird.

    Weiterhin kann es auch auf Code-Ebene, nämlich zwischen Logik- und Darstellungsebene Differenzen geben. Z.B. wenn man erst Mockups erstellt und diese dann mit Code unterfüttert.

  4. Schlechte Wartbarkeit aufgrund (organisch) gewachsener Software – Software wächst in der Regel organisch und wird regelmäßig refactored bzw. neu geschrieben. Das führt meistens zu besser wartbarem Code, ist jedoch zeitaufwendig und kann zu neuen Fehlern führen. Da sich die Außenwelt der Software, z.B. das Betriebssystem oder PHP-Version, ständig ändert, muss die Software darauf angepasst werden. Das wird verschärft durch Drittsoftware, welche ebenfalls weiterentwickelt wird. Man ist als Entwickler nicht in einer Seifenblase, sondern muss auf verschiedene Stellen achten, damit die eigene Software am Ende noch das tut, was man von ihr erwartet.
  5. Der Entwickler als Branchenkenner – Warum geht man eigentlich davon aus, dass Softwareentwickler jede Domäne (z.B. E-Commerce, Banken, CRM, …) kennen, für die sie Software bauen? Vorlesungen und Fachliteratur zu diesem Thema konzentrieren sich vordergründig darauf, wie die Software zu bauen ist. Man geht davon aus, dass sich der Softwareentwickler die Domain-spezifischen Dinge erarbeitet und sie dann auch soweit versteht, dass er sie umsetzen kann. (Stichwort Pflichten- und Lastenheft) Leider wird die Zeit für die Erarbeitung dieses Fachwissens entweder nicht gern bezahlt oder gleich als gegeben vorausgesetzt.

Und jetzt?

Die eingangs vorgestellten Highlights lösen die aufgezählten Probleme maximal teilweise. Für eine praxistaugliche Verzahnung von Konzept und Code ist mir bisher nix bekannt. Das soll nicht heißen, dass es nicht möglich ist. Die Fokussierung auf Module und modulare Software (z.B. mittels Composer oder NPM) entschärft mMn Problem 1 und 2 massiv. Leider gibt es weiterhin das Anspruchsdenken, dass gewisse Systeme als eine Art Host lauf müssen. Hinter Problem 1 und 2 steht die Entscheidung des Entwicklers und lässt sich nicht so einfach von außen korrigieren.

Für Problem 3 habe ich bisher nichts effektives gefunden, welches sich auch in der Praxis bewert. UML kann hier helfen, jedoch kenne ich niemanden, der das praktisch umsetzt. Zu anstrengend und teilweise auch nicht gewollt, da man bis auf Papier nix produziert. Andere sehen das ähnlich.

Das Problem 4 mit organisch gewachsener Software rührt mMn daher, dass die Softwarespezifikation bzw. vorher schon die Anforderungen, erst spät im Entwicklungsprozess (einigermaßen) fest standen. Weiterhin können unerwartete Änderungen eintreten, auf die man reagieren muss, was das Problem verschärft. Die Verwendung von Modulen kann hier zu einer Verschlimmerung beitragen, wenn sich auf einmal Modulversionen nicht mehr vertragen. Oder es wird nun statt PHP 5.4 nur noch PHP 7 unterstützt. Besonders gravierend wird es, wenn das Modul ebenfalls organisch wächst und an den erwähnten Problemen leidet. 🙂

Die Beschreibung von Problem 5 basiert auf eigenen Erfahrungen und wurde bisher noch nicht in Medien oder Blogs thematisiert (z.B. Golem und heise.de). MMn scheint die Ansicht vorzuherrschen, dass der Entwickler sich schon einarbeitet und man geht stillschweigend davon aus, dass er Felder von Biologie über Steuererklärungen bis hin zu Raketentriebwerken versteht. Der Knackpunkt liegt hier darin, dass anspruchsvolle Software oft Felder geschrieben wird, für die man eine längere Ausbildung braucht, um in ihnen zu arbeiten. Der Entwickler muss quasi quer einsteigen, sollte er sich nicht schon auskennen. Weiterhin obliegt die Aufgabe teilweise beim Kunden, den Entwickler aufzuklären.