Verbesserung von Code-Qualität durch KI-Nutzung laut Google-Report DORA (Oktober 2024): +7,5%. Laut derselben Studie haben 39% der Befragten wenig bis kein Vertrauen in KI-generierten Code und die Stabilität kann sich um bis zu 7,2% reduzieren.
Laut einer GitHub-Studie (November 2024) coden Entwickler dank Copilot 55% schneller. Die Wahrscheinlichkeit für Korrektheit (überprüft mit Unit-Tests) ist 56% höher. In Blindstudien lag die Lesbarkeit des KI-generierten Codes knapp 4%, die Verlässlichkeit 3% und die Wartbarkeit 2% höher. 4, 3, 2 Prozent: Das scheitert an der 5-Prozent-Hürde, auch wenn die Studie behauptet, die Zahlen seien signifikant und nicht irgendwelche Schwankungen.
Laut Stackoverflow Developer Survey 2024 verwenden 62% der Entwickler KI-Tools. 72% sind zufrieden oder sehr zufrieden mit diesen Tools. 81% nennen erhöhte Produktivität als größten Vorteil. 66% vertrauen dem KI-generierten Code nicht und 63% bemängeln den fehlenden Kontext zur Codebasis. Entspricht meinem persönlichen Eindruck.
Keine oder geringe (bis 10%) Ausschöpfung von KI-Potenzial in deutschen Unternehmen (Selbsteinschätzungen laut Studie Stifterverband Deutsche Wissenschaft und McKinsey, Januar 2025): 70% – Antwort „Ja, den Mitarbeitern fehlen grundlegende KI-Kompetenzen“ (gleiche Studie): 79%
Antwort „KI sorgt für zusätzliche Arbeitslast“ (Studie von Upwork, Sommer 2024): 77%
Gelöste Aufgaben des KI-Tests „Humanity’s Last Exam“ durch aktuelle Sprachmodelle (Stand Januar 2025): maximal 10%
Umfrage der Game Developers Conference (Januar 2025): 30% sehen einen negativen Einfluss generativer KI, Vorjahreswert: 13%. 36% der Entwickler in der Spielebranche nutzen KI-Tools (+5% seit 2024), allerdings auch in Marketing/Finance, hier geht es also nicht nur ums nackte Programmieren.
Einen Nutzen durch Einsatz von KI-Anwendungen sehen laut einer Befragung der Boston Consulting Group in Deutschland ein Viertel der befragten Unternehmen. Laut der gleichen Befragung erwarten 95% der Führungskräfte keinen Rückgang der Mitarbeiterzahlen aufgrund KI-Einsatz. Denn die frei werdenden Ressourcen kann man ja vorteilhaft für andere Aufgaben einsetzen, zum Beispiel seit Jahren herumliegende Bugtickets bearbeiten…
Geschätzte jährliche Energiekosten durch ChatGPT: 140 Millionen Dollar. (Eine Anfrage an ChatGPT verbraucht 10x soviel Strom wie eine Google-Suche. Ob sie wohl auch 10x so informativ ist…?)
Geschätzte Energiekosten für das Training von ChatGPT 4: 8,2 Millionen Dollar
Kursverlust Börsenwert Nvidia aufgrund von ein paar angeberischen Behauptungen über die Effizienz von DeepSeek (das chinesisch und zensiert und womöglich mit ChatGPT trainiert ist) in sozialen Medien am 28.1.25: 589 Milliarden Dollar.
Künftig biete ich mit dem Rheinwerk-Verlag zweitägige Online-Seminare an. Diese gehen über die Inhalte in meinem Buch hinaus, richten sich nach aktuellen Technologien und gehen auch auf spezifische Bedürfnisse ein.
Ich schreibe einen kleinen Programmierwettbewerb aus. Zu gewinnen gibt es einen rosafarbigen Pokal in Dildoform, den ich aus naheliegenden Gründen nicht abbilde.
(Symbolbild)
Wir befinden uns in einer nahen Zukunft. Die KI-basierte Software namens „YourPorn 1.0“, die ihr entwickelt habt, ermöglicht es, ein paar (nackige) Selfies von sich selbst hochzuladen, ein paar Schlagworte einzugeben, und heraus kommen die ersten 30 Sekunden eines einmaligen, individuellen, in 720p codierten Pornos mit dem User in der Hauptrolle.
Generiert von einer Video-KI, natürlich, die mit einschlägigen Trainingsdaten gefüttert wurde (und davon gibt es wahrlich genug).
Das volle, 5 Minuten lange Video in Full HD kostet dann 9,99 (es gibt natürlich auch günstige Abomodelle).
Die Teilnahmebedingungen könnt ihr mit einem ChatGPT-Prompt eures Vertrauens erfragen. Nicht vergessen: Das Wasserzeichen „KI-generiert“, damit der Quatsch nicht für Deep Fakes missbraucht werden kann.
Tja, da war ich zu schnell: Kaum ist mein Godot-Buch auf dem Markt, in dem ich anfangs Scratch-Beispiele zeige, um sie dann in Godot-Code zu übersetzen, erscheint Scratch für Godot…
Okay, es heißt nicht so, sondern Block Coding, aber es funktioniert genau so.
Noch ist es nicht auf deutsch übersetzt, aber das ist sicher nur eine Frage der Zeit. Ich werde das Addon in meinem nächsten Projekt ausprobieren, um rauszufinden, ob man damit schneller oder langsamer entwickelt. Ich denke, für einfache Aufgaben eignet es sich gut – und vor allem für Einsteiger. Bestimmt werde ich das Addon bei der Spieleprogrammieren-AG einsetzen, die ich im zweiten Halbjahr an einer Schule veranstalte. Wo die Grenzen des Addons erreicht werden, wird eben doch Code eingesetzt. Ich denke, auch bei Berechnungen ist Code übersichtlicher. Spannend wird, wie Code und Blocks interagieren. Ich werde berichten!
Nach mehrmonatiger Arbeit ist mein neues Buch erschienen. Es richtet sich an Programmiereinsteiger, obwohl/weil es u.a. auch das Single-Responsibility-Prinzip erklärt – ja, das ist auch bei Spielen sinnvoll.
Das Buch ist überall zu haben, wo es Bücher gibt, und im Verlagsshop.
Eine Untersuchung kam zu dem Ergebnis, dass die Verwendung von KI-basierten Coding-Hilfen die Codequalität verschlechtert.
Ach tatsächlich? *Augenverdreh-Smiley*
Programmierer sind bekanntermaßen faul. Wenn man ihnen die Möglichkeit gibt, noch fauler zu sein, werden sie sie nutzen. Und dummerweise sind die Qualitätsmängel einer KI-Codeempfehlung nicht immer offensichtlich. Letztlich muss einem aber klar sein: Es ist copy+paste-Coding. Und das hat immer eingebaute Nachteile, weil eine ggf. sinnvolle Abstrahierung nicht stattfindet und manchmal notwendige Änderungen übersehen werden. Copy+paste-Fehler gehören zu den häufigsten. Die KI kann auch Fehler machen, Denken muss man schon noch selbst, das kann sie nämlich nicht!
Ich bin mal gespannt, wann erste, anspruchsvollere Dev Leads die Coding Rule rausgeben, keinen KIs als Programmiersklave zu verwenden. Und, wie das ggf. überprüft werden soll.
Übrigens sind auch Lizenzfragen hier relevant. Manch ein KI-generierter Codeschnipsel könnte von Scannern als Duplikat einer restriktiv lizensierten Stelle aus irgendeinem gitbub-Repo identifiziert werden. Der zuständige Entwickler* kann dann schlecht mit dem Finger auf die KI zeigen, denn die Verantwortung für den erzeugten Code trägt er. Dieser Verantwortung müssen Entwickler* gerecht werden und generierten Code kritisch hinterfragen, und zwar mindestens genauso kritisch, als wäre er von einem Kollegen geschrieben worden.
Wer KI-Instrumente einsetzt, sollte nicht von Faulheit getrieben sein – sondern von Vorsicht.
Whatsapp begründet den Schritt mit fehlenden Sicherheitsupdates für die alten Versionen, fehlender Unterstützung für App-Features (hier würden mich mal die Details interessieren) und weil kaum noch jemand solche alten Geräte verwendet.
Im Mülleimer ist noch Platz!
Natürlich verwenden nur noch 0,00000irgendwas Prozent aller Android-Nutzer so alte Geräte, aber in absoluten Zahlen dürften das trotzdem nicht wenige sein. Ein zwar altes, aber grundsätzlich noch funktionierendes Gerät muss also auf den Elektromüll geschmissen werden, weil die Whatsapp-Entwickler keine Lust mehr haben, die App-Unterstützung für Android 4 weiter zu gewährleisten, sprich: sich mit alten Bibliotheken oder Sicherheitslücken herumzuschlagen. Irgendwo verständlich, klar.
Denn die Ursache des Übels liegt natürlich nicht bei den Entwicklern von Whatsapp, sondern bei denen von Android.
Wie selbstverständlich muss jedes Jahr eine tolle neue better-than-ever Android-Version auf den Markt kommen! Und um zu kaschieren, dass diese für die meisten Nutzer eigentlich keine nennenswerten Verbesserungen bringt, ändert man immer wieder das Design und behauptet, dass die Version noch sicherer ist als die vorherige. Was ja auch stimmt.
Bloß: Es spräche ja nichts dagegen, die Sicherheitsprobleme der vorherigen Version einfach durch Updates zu beseitigen. Bei LTS-Versionen von Linux-Betriebssystemen funktioniert das ja auch schon viele Jahre lang (und Android ist ein Linux). Würde man effizienter, modularer programmieren (und keine Bloatware installieren), wäre auch auf älteren Geräten mit wenig Speicher noch genug Platz für alles. Sicherheitspatches erfordern wohl kaum Megabyteweise neuen Binärcode!
Hach, sie können ja nicht anders
Da bekanntermaßen Hardware-Hersteller überhaupt kein Interesse daran haben, ihren Kunden zu ermöglichen, ältere Geräte länger zu nutzen, verschwenden die natürlich keine Entwicklerressourcen an solche Upgrades. Lieber springen sie auf den Google-Zug auf und bringen jedes Jahr eine neue Geräte-Generation, die eine noch tollere Kamera hat, ein noch größeres Display, ein noch hübscheres Notch oder das man in den Pool mitnehmen oder falten kann, denn das ist es ja, was wir Menschen unbedingt brauchen. Inzwischen gibt es auf diesem Planeten grob geschätzt 14 Milliarden Smartphones, jeder erwachsene Mensch besitzt also längst weit mehr als zwei (plus Tablets). Mehr als die Hälfte ist also überflüssig.
Letztlich reden wir hier von einer Ressourcenverschwendung, die das Gegenteil von nachhaltig ist und einen Material- und Energieverbrauch mit sich bringt, der in einer Welt, die vor dem Klimakollaps steht, verboten gehört. Aber die Anbieter haben ja keine Alternative: Wenn sie keine neuen Betriebssysteme oder Geräte verkaufen können, entfallen schlicht die Einnahmen und sie müssen den Laden dicht machen. Helfen könnte bei Betriebssystemen ein Abo-Modell. Gibt’s ja in anderen Branchen auch. Neue, noch leistungsfähigere Hardware ist unnötiger Schein-Fortschritt auf Kosten des Planeten. Das ist krank.
Nur ein paar Beispiele
Bei Apple ist es übrigens nur ein bisschen besser. Für mein 11 Jahre altes, aber noch tadellos funktionierendes MacBook Air, gibt es kein aktuelles MacOS X mehr, und das anstehende Update für den Chrome-Browser installiert sich nicht unter dem alten OS. Folglich bin ich fürderhin gezwungen, einen veralteten Browser zu verwenden, mir einen anderen zu suchen oder das Gerät zu ersetzen.
Noch mehr Beispiele? Ein kleines noch aus eigener Erfahrung: Beim letzten größeren Linux-Kernel-Upgrade musste ich meinen tadellos funktionierenden, nur wenige Jahre alten WLAN-Stick ersetzen, weil der Treiber für den enthaltenen Chip aus dem Kernel entfernt worden war. Wer trifft eigentlich solche rücksichtslosen Entscheidungen, die letztlich beim Endkunden Kosten und Elektromüll verursachen?! Wer trägt die Verantwortung, wem kann ich die Rechnung schicken, wem das Altgerät zwecks umweltgerechter Entsorgung?
Der Gipfel der Ressourcenverschwendung und des Hardware-Wegwerf-Wahns ist übrigens gar nicht Android, sondern Windows. Version 11 kann bekanntlich (normalerweise) nur auf Rechnern mit einem spezifischen Hardwaremodul installiert werden. Sobald also der Support für Windows 10 endet (14. Oktober 2025), müssen alle PCs ohne dieses Modul sicherheitshalber weggeschmissen werden, weil es keine Lücken-Updates mehr gibt (und Windows 10 ist voller Lücken, ach übrigens: Mit Linux kann man solche PCs noch lange weiter betreiben!). Wie viele Geräte da auf den Schrott wandern werden (oder willkommene Opfer für Verschlüsselungstrojaner werden), wage ich nicht zu schätzen.
EDIT: Inzwischen sind zwei weitere prominente Fälle aus dem Android-Bereich bekannt geworden: Die ZDF Mediathek und Youtube laufen nicht mehr unter Android 5. Immerhin verweisen beide Apps auf „Im Browser öffnen“. Was ein bisschen lächerlich ist, denn der läuft ja auch auf dem Gerät, warum dann nicht die Apps, die ja einfach in einem Chrome Webview laufen könnten?!
Diese Funktion ist @Deprecated, weil ich den Namen nicht mehr cool fand
Nicht unerwähnt bleiben soll der Aufwand, den uns als Entwickler jeder endende Software-Upgrade-Pfad aufzwingt. Jede Anwendung verwendet ja irgendwelche Bibliotheken, die ihrerseits gewisse Systemanforderungen haben. Schlicht ausgedrückt: Sobald eine neue Version von ir-gend-was.jar eine Änderung an unserem Code oder gar an den Systemvoraussetzungen unserer Anwendung ändert, müssen wir zwingend aktiv werden – aber niemand bezahlt diesen Aufwand! Diese Kosten – Zeit, Personal, Energie – müssen in unser Produkt von vornherein eingepreist werden, obwohl sie gar nicht seriös kalkuliert werden können, weil sie nicht einmalig anfallen wie der Kaufpreis, sondern laufend.
Und solche Anpassungen müssen wir dauernd machen: Nicht nur bei Android-Apps, wenn Google z.B. verlangt, dass wir die Billing-Library Version 5 für In-App-Käufe verwenden müssen, ansonsten dürfen wir unsere App nicht mehr updaten. Natürlich hat sich die API geändert, also müssen wir Dokus lesen und Codeanpassungen vornehmen, meist ohne dass unsere App dadurch auch nur einen Euro mehr Einnahmen erzeugt. Unverschämtheit!
Oder man denke an PHP-Skripte, die nicht mehr funktionieren, weil der Zugriff auf unbekannte Array-Keys seit PHP 8 standardmäßig eine Warnung statt eine Notice auswirft. Noch schlimmer waren nur die grundlegenden Änderungen am MySQL-Treiber, der alle vorherigen Funktionsnamen änderte. Welche Aufwände das weltweit verursacht hat, und wie viele PHP-Skripte seitdem einfach nicht mehr funktionieren, weil sich niemand darum kümmert, kann niemand schätzen. Nichts gegen Produktpflege, Refactoring, Bugfixing oder von mir aus Verschönerung einer API. Aber wenn man weiß, dass andere Entwickler davon abhängig sind, und eine abwärtsinkompatible Änderung Aufwände verursacht, die man selbst ja nicht hat und deshalb ein Problem anderer Leute sind, dann ist man schlicht ein rücksichtsloser Energieverschwender. Ach übrigens: Wenn man von vornherein seine Software sauber konzipiert, braucht man hinterher weniger zu ändern! Buchempfehlung siehe rechts. Und ansonsten hat man gefälligst die Bedürfnisse des Rests der Welt über die eigenen zu stellen.
Ich verlange daher zeitlich unbegrenzten Update-Support für alle Betriebssysteme wie Linux, Android, Windows, MacOS sowie für alle Open-Source-Software-Bibliotheken und -Plattformen. Neue Features können jederzeit hinzugefügt werden (bitte modular, so dass sie nur dann automatisch nachgeladen werden, wenn gewünscht bzw. wenn der Hardware-Support vorhanden ist), aber niemals dürfen vorhandene Funktionen entfernt oder geändert werden. Tatsächlich hat diese Herangehensweise einen immensen Vorteil: Es muss nur noch eine Software-Version gepflegt und mit Sicherheitsupdates versorgt werden, nämlich die aktuelle. Weniger Stress = mehr Zeit für besseres Coden!
Der Code funktionierte einwandfrei. Er lief (gefühlt) jahrelang problemlos.
Bis eines schönen Tages an einem Freitag dem 13. jemand einen Fehler meldete: Eine seiner XML-Dateien könne nicht geladen werden. Das Programm habe wohl einen Fehler.
Es gab ja keine Codeänderung, also musste es an der fraglichen XML-Datei liegen.
In der Datei fand sich nun folgendes:
<numFiles>2,315</numFiles>
Das ist aus Sicht des XML-Parsers natürlich kein Long, sondern ein String oder (bestenfalls, falls englische Locale voreingestellt ist) ein Double.
Wer zum Kuckuck schreibt einen numerischen Wert in eine XML-Datei mit Tausendertrennzeichen?!
Dazu gibt’s nur einen möglichen Kommentar:
#fail
In diesem Sinne, mögen euch unnötige Kommas erspart bleiben!
Es war einmal ein Programmierer wie jeder andere. Sprich: Kurz vor Feierabend bekam er die Aufgabe, mal eben schnellTM einem neuen Kunden Zugriff auf eine bestimmte Ressource zu gewähren.
Was glauben Sie, warum er sein für den Abend geplantes Date mit Pizza&Kuscheln absagen musste?
Klarer Fall: Weil er oder einer seiner Kollegen bzw. Vorgänger das Bibel-Zitat aus der Überschrift nicht kannte.
Daten als Code
Wie immer sagt ein Beispiel mehr als 1000 Worte, und ich will ja nicht Ihre Zeit verschwenden. Schauen Sie sich daher das folgende Codebildchen an.
switch(userrole) {
case "DRG_BES":
case "DRG_ARG":
case "DRG_EFT":
mgrNr = 332;
break;
case "DRG_ALB":
mgrNr = 451;
break;
case "DRG_EDV":
mgrNr = 322;
break;
case "DRG_BFF":
mgrNr = 537;
break;
case "DRG_DDA":
mgrNr = 336;
break;
...
Wohlgemerkt handelt es sich hier um einen (leicht verfremdeten) originalen Ausschnitt aus einer umfangreichen Software-Lösung in Java. Die case-Konstruktion im analysierten Gesamtcode war noch länger, und ganz ähnliche Monster gab es an weiteren Stellen.
In einem anderen Projekt gab es auch mal eine ganz ähnliche Konstruktion zur Behandlung von speziellen Userrechten, daher der eingangs erwähnte Anwendungsfall unseres armen Programmierers. Denn der muss, um seine Aufgabe zu lösen, nun den Code erweitern, und zwar möglicherweise an mehreren Stellen. Dann muss er die Software testen, bauen und deployen oder, sollte es sich nicht um eine Serveranwendung handeln, ein Setup-Paket an einen Kunden schicken.
Wie gesagt: das war’s mit dem Date. Denn getreu Murphy’s Law geht dabei irgendwas schief … na ja, ich denke, Sie kennen das, haben es selbst erlebt oder erleiden müssen und fühlen mit unserem armen Programmierer, der sich an der Schwelle zur grausigsten aller Schrottsoftwareapokalypsen wähnt.
Daten sind Daten
Der Knackpunkt ist natürlich: Wenn Sie Daten als Code schreiben, müssen Sie die Anwendung kompilieren, bauen und ausrollen, um etwas zu ändern. Befinden sich Daten da, wo Daten hingehören (in Datenbanken oder Ressourcen- bzw. Konfigurationsdateien), und ist der Code generisch, genügt es, die Daten an der richtigen Stelle zu ändern, was in 99,99% der Fälle deutlich weniger Aufwand ist.
Der zugehörige Ersatz-Code für obiges Konstrukt könnte beispielsweise so aussehen:
Dabei ist es dem Code an dieser Stelle egal, ob userSettings ein simples POJO ist, in das die richtigen Daten irgendwann vorher geladen wurden, oder ob die Klasse den gewünschten Wert in diesem Moment aus einer Datei oder Datenbank liest. Im ersteren Fall muss die Anwendung möglicherweise neu gestartet werden, oder anderweitig signalisiert bekommen, dass sich Settings geändert haben und neu geladen werden müssen. Hat der Software-Architekt damit gerechnet, dass sich solche Daten außerhalb ändern können, hat er möglicherweise auch einen automatischen Refresh eingebaut. Zu beachten ist nämlich, dass sehr häufige Zugriffe auf meist statische Settings-Daten durchaus die Performance beeinträchtigen können, wenn jedesmal ein Datenbankzugriff oder z.B. eine XML-Deserialisierung notwendig ist. Ein Caching von Settings für ein paar Minuten ist also oft eine gute Idee.
Sie sehen natürlich auf den ersten Blick, dass der neue Code nicht nur ein Vielfaches kürzer ist als das alte Switch-Konstrukt. Er ist außerdem sofort zu verstehen und damit sehr gut wartbar. Auch die Fehleranfälligkeit ist geringer, weil eine versehentliche Veränderung in einem String-Literal oder einer der „magic numbers“ im Eingangsbeispiel hier nicht passieren kann (der Compiler würde es wohlgemerkt nicht merken).
Daten sind Daten, aber wie?
Wenn Sie vor der Entscheidung stehen, wie und wo Sie Konfigurationsdaten ablegen, gibt es weder eine Patentlösung noch allgemeingültige Empfehlungen.
So möchten Sie User/Rollen-Konfigurationen einer auf Kunden-PCs laufenden Anwendung sicher verschlüsseln oder zumindest digital signieren, damit sich niemand auf einfache Weise zusätzliche Rechte verschaffen kann. Das ist natürlich mit Standard-Bibliotheken ohne weiteres möglich und ändert nichts am Grundprinzip der sauberen Trennung von Daten und Code.
Auf Dateiebene kommen ini- oder properties-Dateien in Frage, für komplexere Daten (wie Maps/Dictionaries wie im obigen Beispiel) XML- oder Json-Format. Beachten Sie, dass es für so ziemlich jede Programmiersprache, die etwas auf sich hält, Bibliotheken gibt, die Ihnen solche Dateien in Objekte deserialisieren (z.B. GSON oder Jackson für Java). So können Sie eine bestimmte Datenstruktur erzwingen, brauchen keine tippfehleranfälligen Stringliterale für den Zugriff, und strukturell falsche Daten führen zu Ausnahmefehlern, die (ordentliche Fehlerbehandlung vorausgesetzt) sofort sichtbar werden.
Auf Nummer sicher gehen Sie mit einer Datenbank, in der das relationale Schema die Datenstruktur fest vorgibt. Das muss natürlich kein ausgewachsener SQL-Server sein – auch dateibasierte Datenbanken wie Apache Derby oder SQLite erfüllen ihren Zweck. Das obige Beispiel würde eine Tabelle mit zwei Spalten (userrole und mgrNr) erfordern, wobei die userrole gleichzeitig der unique primary key wäre und die get-Funktion letztlich eine SQL-Query ausführt:
SELECT mgrNr FROM settings WHERE userrole=:?
Diese oder jene Daten
Daten in Ressource-Dateien abzulegen (auch dateibasierte Datenbanken sind letztlich welche), eröffnet Ihnen mit modernen Build-Systemen wie Maven weitere Möglichkeiten. So können Sie mit Maven-Profiles das Buildsystem anweisen, unterschiedliche Ressourcen-Verzeichnisse zu verwenden. Auf diese Weise können Sie Testversionen getrennt von Produktivversionen verwalten oder auch unterschiedliche Ausprägungen eines Produkts bauen. In Mavens pom.xml schreiben Sie einfach:
Sie übergeben dem mvn-Kommando mit dem Parameter -P den Namen des gewünschten Profils. Dann verwendet Maven zusätzlich zum Standard-Verzeichnis für Ressourcen (resources) ein Verzeichnis namens resources-[profile]. Legen Sie also einfach die jeweiligen Dateiversionen in unterschiedliche resources-xxx-Verzeichnisse und bauen Sie die Anwendung mit dem passenden -P-Parameter.
Wenn Sie das Springframework verwenden, können Sie mit passenden Annotations dafür sorgen, dass Konfigurationsparameter direkt zu Java-Beans verarbeitet werden, die per Autowire im Inversion-of-Control-Container zur Verfügung stehen. Aber das ist ein Thema für einen anderen Artikel.
Mögen Ihre Daten immer Daten sein, auf dass keines Ihrer Dates ausfallen muss!
Wir leben im digitalen Zeitalter (na gut, die meisten von uns), und langsam aber sicher wird vielen Entscheidern klar, dass die AWS-Cloud (oder ihre Verwandten) nicht nur total praktisch ist, sondern auch eine ganze Menge Energie verbraucht. Schätzungen sprechen von bis zu 20% des Energieverbrauchs der ganzen Welt. Wohlgemerkt sind Anwendungen in der Cloud immer noch sparsamer als eigene Rechenzentren mit Servern aus Blech, die 24 Stunden an der Steckdose nuckeln, aber beispielsweise nur tagsüber benötigt werden. Cloud-Instanzen sind üblicherweise „shared“ und verbrauchen nur dann Energie, wenn benötigt. Trotzdem bedeuten mehr Cloud-Instanzen natürlich auch mehr Energieverbrauch (und CO2-Ausstoß, sofern das Rechenzentrum keinen grünen Stromanschluss besitzt).
Tatsächlich können wir die Frage nach dem Energieverbrauch auch Codern und Software-Architekten stellen: Benötigt euer Software-System wirklich 10 Instanzen und 3 Datenbanken? Muss für eine eher simple Anwendung 1 GB RAM reserviert werden und die Kiste mit dem fettesten Prozessor oder darf es ein bisschen weniger sein? Sollte der Energieverbrauch einer Plattform vielleicht sogar zu den Entscheidungskriterien gehören?
Der Vergleich
Für die 2. Auflage meines Buchs „Besser coden“ habe ich ein Kapitel über effizienten Code geschrieben – und ein paar Messungen durchgeführt. Dazu habe ich eine relativ einfache Webanwendung in mehreren Sprachen geschrieben und Aspekte wie Performance, Ressourcenverbrauch und Anspruch an Entwickler verglichen. Letzteres ist nicht zu unterschätzen: Spart eine Technologie Speicher, aber Sie finden keinen Entwickler, der sie beherrscht, bleibt ihr tolles Softwaresystem graue Theorie.
Es traten an:
Java 13 und Spring Boot, das beliebte Framework für Webservices
PHP 7.4, eine bewährte, einfache Skriptsprache mit Cache APCu
Rust 1.52 und Actix Web, eine ziemlich neue Sprache samt passendem Webservice-Framework
sowie quasi als Online-Bonus (nicht im Buch) Go.
Der Webservice besitzt nur einen einzigen Endpoint, der dafür gedacht ist, ein Wort gegen eine hinterlegte Liste zu prüfen. Eine solche Funktion ist beispielsweise in einem Scrabble-Spiel nötig: Ist das gelegte Wort erlaubt oder nicht? Das Ergebnis wird dabei als JSON-Antwort formuliert.
Die Liste ist absichtlich nicht in einem ausgewachsenen Datenbanksystem hinterlegt, denn ich möchte nicht die Effizienz unterschiedlicher RDBMS bewerten, sondern die von Software-Plattformen. Daher lädt die zu schreibende Anwendung die Wortliste beim Start aus Textdateien und hält sie dann im RAM. Im Test enthielt diese Liste knapp 180.000 Einträge. Im Fall von PHP erfordert eine solche Vorgehensweise zwingend den Einsatz eines Caches (hier verwendet: APCu), um die Dateien nicht bei jedem Aufruf des Skripts erneut laden zu müssen.
Die Rechenzeit habe ich mit dem Apache Benchmark ab gemessen, einmal einen Einzelrequest und einmal 10.000 auf einmal in sechs parallelen Threads, um die Leistung im Parallel Processing zu bestimmen.
Den Code finden Sie in Grundzügen in meinem Buch (bis auf die Go-Version). Hier fasse ich Ihnen nur die Ergebnisse zusammen:
Java/Spring Boot
PHP/APCu
Rust/Actix
Go
RAM-Verbrauch
50 MB
200 MB
0,9 MB
24 MB
Anwendungsgröße
19 MB (JAR)
372 Bytes (Skript)
8,4 MB (binär)
7,1 MB (binär)
Zeit 1 Aufruf
1,8 ms
0,9 ms
0,4 ms
0,5 ms
Zeit 10.000 Aufrufe
1,1 s
0,6 s
0,5 s
0,5 s
Startup-Dauer
2,5 s
nicht messbar
53 ms
75 ms
Buildtime
7,4 s
entfällt
69 s
1 s
Coding-Anspruch
leicht
sehr leicht
schwierig
mittel
Sie sehen, dass das rein binäre Rust-Programm zur Laufzeit am schnellsten und genügsamsten ist – aber finden Sie mal einen Rust-Entwickler auf dem Jobmarkt oder lernen Sie die Sprache „mal eben“! Ich hab letzteres versucht und brauchte mehrere Packungen Schokokekse, um die spezielle Speicherverwaltung zu kapieren. Die lange Buildtime ist dabei dem anspruchsvollen Compiler- und Linker-Vorgang geschuldet.
Abgesehen vom RAM-Verbrauch ist PHP unter dem Strich wohl die effizienteste Lösung. Aber viele Entwickler scheuen sich davor, größere Projekte in PHP anzulegen – die fehlende starke Typisierung und die immer über uns Entwicklern schwebende Versuchung, spaghettimäßig PHP- und HTML-Code zu mixen, sowie ein paar Fallen wie vergessenes $this->, sind klare Minuspunkte. Dafür ist die Turnaround-Zeit Null: Skript nur speichern, schon ist es bereit zum Aufruf per HTTP.
Java ist nicht ohne Grund sehr beliebt. Aber die Java-Runtime, so optimiert sie mittlerweile auch ist, geht alles andere als sparsam mit Ressourcen um und ist merklich langsamer als die Binärcode-Konkurrenz (auch PHP verwendet dank Zend-Engine letztlich Binärcode). Ein Maven-Buildprozess lädt gefühlt mehrmals täglich das halbe Internet runter. Dafür ist der Code (speziell mit Spring Boot) aufgeräumt und vergleichsweise leicht zu debuggen. Große Projekte mit komplexer Geschäftslogik sind in Java wohl vergleichsweise am lesbarsten abzubilden.
Fazit
Sie sehen: Es gibt keine Lösung, die gleichzeitig einfach und technisch effizient ist. Sie müssen immer abwägen: Lohnt es sich, in eine hocheffiziente, moderne Technik wie Rust oder Go zu investieren? Oder setzen Sie auf eine bewährte und einfache Technik wie Java und nehmen in Kauf, dass Sie mehr Server benötigen (und Energie verbrauchen), wenn mehr Rechenpower erforderlich ist? Gerade bei neuen Projekten ist es sicher eine gute Idee, über diese Fragen zu diskutieren. Denn später können Sie die Plattform nicht mehr einfach ändern.
So bleiben vermutlich noch auf Jahre oder Jahrzehnte Java-Webservices und PHP-Skripte state of the art – obwohl mit Rust oder Go, C++20, D … technisch hochmoderne und extrem effiziente Konkurrenzprodukte be- reitstehen.