Infrastructure as Code: Warum Handarbeit auf Servern gefährlich ist

Jeder Systemadministrator kennt den Moment: Man meldet sich auf einem Server an, führt ein paar Befehle aus, ändert eine Konfigurationsdatei, installiert ein Paket. Alles funktioniert. Ein paar Wochen später steht man vor demselben Problem — nur auf einem anderen Server. Und man kann sich nicht mehr erinnern, welche Befehle man ausgeführt hat. Der Server ist ein Schneemann: Er sieht aus wie die anderen, aber unter der Oberfläche ist alles anders.

Infrastructure as Code (IaC) ist die Antwort auf dieses Problem. Und wer als Softwareentwickler und Administrator — wie ich — beide Welten kennt, weiß, dass IaC nicht nur ein Trend ist, sondern eine Notwendigkeit.

Das Problem mit manuellen Servern

Manuelle Administration skaliert nicht. Das ist keine Meinungsfrage, sondern Mathematik. Wenn jeder Server ein Unikat ist, dann ist jeder Server ein Risiko. Wenn ein Server ausfällt und niemand weiß, wie er konfiguriert war, dann ist der Wiederherstellungsprozess ein Glücksspiel.

Die typischen Symptome sind vertraut:

  • Konfigurationsdrift. Server, die identisch sein sollten, unterscheiden sich in subtilen — und manchmal kritischen — Details.
  • Wissensmonopole. Nur eine Person weiß, wie ein bestimmter Server konfiguriert ist. Wenn diese Person krank wird oder geht, ist das Wissen weg.
  • Nicht-reproduzierbare Zustände. Ein Server funktioniert, aber niemand kann erklären, warum. Oder schlimmer: Niemand kann ihn neu aufsetzen.
  • Angst vor Änderungen. Weil niemand weiß, was passieren wird, wenn man etwas ändert, ändert man nichts. Bis man es muss. Und dann ist es zu spät.

Was Infrastructure as Code bedeutet

IaC ist nicht einfach „Skripte schreiben“. Es ist ein Paradigmenwechsel in der Art und Weise, wie wir über Infrastruktur denken.

Deklarativ statt imperativ. Statt zu beschreiben wie ein Server konfiguriert werden soll („installiere nginx, kopiere die Konfiguration, starte den Dienst“), beschreibt man was der Zustand sein soll („nginx muss installiert sein, die Konfiguration muss diesen Inhalt haben, der Dienst muss laufen“). Der IaC-Workflow kümmert sich um das Wie.

Versioniert. Jede Änderung an der Infrastruktur ist in Git committet. Man kann sehen, wer was wann geändert hat. Man kann zu einem früheren Zustand zurückkehren. Man kann Änderungen über Code-Reviews prüfen lassen.

Reproduzierbar. Wenn ein Server ausfällt, kann er aus der IaC-Definition komplett neu aufgesetzt werden. Wenn ein neuer Server benötigt wird, kann er in Minuten statt Stunden erstellt werden.

Testbar. Bevor eine Änderung auf Produktion geht, kann sie in einer Testumgebung validiert werden. Syntaxfehler, fehlende Abhängigkeiten, inkompatible Konfigurationen — alles wird erkannt, bevor es Schaden anrichtet.

Die Werkzeuge

Die Wahl des Werkzeugs hängt vom Kontext ab. Hier eine Übersicht der etablierten Optionen:

Ansible

Das probably bekannteste IaC-Werkzeug. Ansible ist agentenlos (arbeitet über SSH), verwendet YAML für die Konfiguration und hat eine flache Lernkurve. Für FreeBSD-Administratoren ist es attraktiv, weil es native Module für ZFS, Jails, pkg und andere FreeBSD-Spezifika hat.

- name: ZFS-Dataset für Backup erstellen
  community.general.zfs:
    name: tank/backup
    state: present
    extra_zfs_properties:
      compression: zstd
      atime: off
      mountpoint: /backup

Der Nachteil: Ansible ist langsam bei großen Infrastrukturen und die YAML-Syntax wird bei komplexen Logiken unübersichtlich.

Terraform / OpenTofu

Für Cloud-Infrastrukturen ist Terraform (oder der Open-Source-Fork OpenTofu) der De-facto-Standard. Es verwaltet die Infrastruktur als Ressourcen — Instanzen, Netzwerke, Sicherheitsgruppen, Speicher — und hält den Zustand in einer State-Datei.

resource "aws_instance" "freebsd" {
  ami           = "ami-0xfreebsd15"
  instance_type = "t3.medium"

  tags = {
    Name = "freebsd-prod"
  }
}

Pulumi

Pulumi geht einen anderen Weg: Statt YAML oder HCL verwendet man echte Programmiersprachen — Python, TypeScript, Go. Das erlaubt echte Logik, Schleifen, Bedingungen und Typsicherheit.

import pulumi
import pulumi_command as command

server = command.remote.Command("freebsd-setup",
    connection=command.remote.ConnectionArgs(
        host="10.0.0.1",
        user="root",
    ),
    create="pkg install -y nginx",
)

Für FreeBSD: Besonderheiten

FreeBSD bringt eigene Werkzeuge mit, die in der IaC-Welt eine Sonderrolle einnehmen:

  • Jails sind leichtgewichtige Container, die sich hervorragend mit Ansible oder Shell-Skripten verwalten lassen.
  • ZFS bietet programmierbare Snapshots und Replikation, die sich in IaC-Workflows integrieren lassen.
  • pkgbase (seit FreeBSD 15.0) macht das Basissystem selbst paketierbar und damit IaC-kompatibel.

Der Pfad zur Einführung

IaC lässt sich nicht über Nacht einführen. Aber man kann schrittweise vorgehen:

  1. Bestandsaufnahme. Welche Server gibt es? Welche Konfigurationen? Was ist dokumentiert, was nicht?
  2. Ersten Server codifizieren. Den einfachsten Server nehmen und seine Konfiguration in Ansible (oder ein anderes Werkzeug) übertragen. Das dient als Lernprojekt und als Template.
  3. Neue Server nur noch via IaC. Jeder neue Server wird ausschließlich über Code konfiguriert. Keine manuellen Änderungen mehr.
  4. Bestehende Server migrieren. Nach und nach die manuelle Konfiguration durch IaC-Definitionen ersetzen. Das ist der langwierigste Teil.
  5. CI/CD für Infrastruktur. Änderungen an der IaC-Definition werden automatisch in Testumgebungen deployed, bevor sie auf Produktion gehen.

Die Kultur ändert sich mit

IaC ist nicht nur ein technischer Wechsel, sondern ein kultureller. Plötzlich sind Konfigurationsänderungen sichtbar. Jeder kann sie sehen, kommentieren, zurückrollen. Das ist ungewohnt — besonders für Administratoren.

Aber es ist der richtige Weg. In einer Welt, in der Sicherheitseinheiten täglich nachweisen müssen, welche Konfiguration auf welchem Server zu welchem Zeitpunkt aktiv war, ist manuelle Administration ein Risiko, das man sich nicht mehr leisten kann.

Und für den einzelnen Administrator ist IaC eine Erleichterung: Man muss sich nicht mehr erinnern, was man auf welchem Server gemacht hat. Es steht alles im Git-Repository. Wenn man nicht weiß, wie ein Server konfiguriert ist — man schaut nach. Immer. Zuverlässig. Ohne Ausnahme.

Automatisierung als Geschäftsmodell: Warum IT-Dienstleister ohne Automatisierung untergehen

Wer IT-Dienstleistungen verkauft, hat ein Problem: Seine Zeit ist begrenzt. Jede Stunde, die er an einem Kundensystem verbringt, ist eine Stunde, die er nicht für einen anderen Kunden aufwenden kann. Das Geschäftsmodell des klassischen IT-Dienstleisters — Stunden abrechnen, Probleme manuell lösen, individuell betreuen — hat eine harte Decke.

Automatisierung ist der Weg durch diese Decke. Und wer als Softwareentwickler und Administrator die Werkzeuge beherrscht, hat einen entscheidenden Vorteil: Er kann Automatisierung nicht nur anwenden, sondern bauen.

Das Problem mit dem Stundensatz

Ein klassischer IT-Dienstleister verrechnet vielleicht 80 bis 120 Euro pro Stunde. Wenn er 40 Stunden in der Woche arbeitet (realistisch sind es weniger, weil Akquise, Verwaltung und Reisezeit abgehen), ergibt das eine Umsatzdecke von etwa 160.000 bis 240.000 Euro im Jahr. Davon gehen Steuern, Versicherungen, Fortbildung, Arbeitsplatzkosten ab.

Skalierung ist in diesem Modell nur möglich, indem man Mitarbeiter einstellt. Aber jeder Mitarbeiter bringt neuen Verwaltungsaufwand, Schulungsbedarf, Qualitätskontrolle. Der Gewinn pro Kopf sinkt, wenn man nicht gleichzeitig die Effizienz steigert.

Was Automatisierung ändert

Automatisierung verändert die Mathematik. Statt eine Aufgabe einmal pro Kunde manuell zu erledigen, baut man sie einmal — und deployt sie x-mal.

Beispiel: Ein Kunde braucht einen Webserver mit Nginx, PHP-FPM, SSL-Zertifikaten und einer Monitoring-Lösung. Manuell dauert das — wenn man ehrlich ist — einen halben Tag. Mit Automatisierung dauert es beim ersten Kunden auch einen halben Tag (zuzüglich der Zeit für das Skript/Playbook). Bei jedem weiteren Kunden dauert es fünf Minuten.

Der Effekt: Die Kosten pro Deployment sinken drastisch, während die Qualität steigt. Automatisierte Deployments sind reproduzierbar, testbar, dokumentiert. Manuelle Deployments sind — wie oben beschrieben — Schneemänner.

Konkrete Automatisierungsfelder

Systemadministration

Alles, was auf einem Server konfiguriert wird, kann automatisiert werden:

  • Server-Setup — Basiskonfiguration, Benutzer, SSH-Keys, Firewall-Regeln
  • Dienst-Konfiguration — Webserver, Datenbanken, Mailserver, Monitoring
  • Updates und Patching — Automatisierte Aktualisierungen mit Rollback-Möglichkeit
  • Backup und Recovery — ZFS-Snapshots, Replikation, automatisierte Restore-Tests

Kundenschnittstelle

  • Onboarding — Neukunden bekommen automatisch ihre Infrastruktur
  • Reporting — Wöchentliche Statusberichte automatisch generiert und versendet
  • Abrechnung — Nutzungsbasierte Abrechnung direkt aus Monitoring-Daten
  • Ticket-Routing — Automatische Zuordnung von Support-Anfragen

Compliance und Dokumentation

  • Audit-Logs — Jede Änderung an der Infrastruktur ist in Git dokumentiert
  • Compliance-Checks — Automatisierte Überprüfung von Sicherheitsstandards
  • Disaster-Recovery-Tests — Regelmäßige, automatisierte Tests der Wiederherstellungsprozesse

Der Preis der Automatisierung

Automatisierung ist nicht kostenlos. Sie erfordert:

Zeit. Die Erstellung eines robusten Ansible-Playbooks dauert länger als die manuelle Konfiguration eines einzelnen Servers. Die Investition amortisiert sich erst ab dem zweiten oder dritten Deployment.

Kompetenz. Wer automatisiert, muss programmieren können. Nicht auf dem Niveau eines Software-Entwicklers, aber ausreichend, um Skripte zu schreiben, Datenstrukturen zu verstehen und Fehler zu diagnostizieren.

Wartung. Automatisierte Prozesse müssen gepflegt werden. Wenn sich Upstream-Software ändert, müssen die Playbooks angepasst werden. Wenn neue Anforderungen dazukommen, müssen sie integriert werden.

Aber — und das ist der entscheidende Punkt — diese Kosten fallen einmal an. Die manuellen Kosten fallen jedes Mal an.

Vom Dienstleister zum Produkt

Die konsequente Automatisierung öffnet einen Weg, der über das Dienstleistungsgeschäft hinausgeht: den Übergang zum Produkt.

Wenn die Infrastruktur-Einrichtung automatisiert ist, kann man sie als Self-Service anbieten. Der Kunde wählt seine Konfiguration über ein Web-Interface, das System richtet alles automatisch ein, und der Dienstleister muss nur noch überwachen und bei Problemen eingreifen.

Das ist der Weg, den erfolgreiche IT-Unternehmen gegangen sind: Hetzner mit der Robot-Weboberfläche, GitLab mit seinem CI/CD-Produkt, HashiCorp mit Terraform Cloud. Alle haben begonnen, Dienstleistungen manuell zu erbringen, und sie dann in Produkte verwandelt.

Für kleine IT-Dienstleister bedeutet das nicht, dass man ein SaaS-Startup werden muss. Aber es bedeutet, dass man seine wiederkehrenden Aufgaben identifizieren, automatisieren und als Paket anbieten kann — zu einem Preis, der manuell nicht erreichbar ist.

Der erste Schritt

Wer mit Automatisierung anfangen will, sollte nicht versuchen, alles auf einmal umzustellen. Der beste erste Schritt ist:

  1. Den häufigsten wiederkehrenden Prozess identifizieren
  2. Ihn einmal vollständig automatisieren
  3. Die Zeitersparnis messen
  4. Den nächsten Prozess angehen

Bei mir war das die ZFS-Replikation mit Syncoid. Ein Prozess, der vorher jeden Abend manuell überprüft werden musste und regelmäßig Probleme verursacht hat. Nach der Automatisierung läuft er im Hintergrund — zuverlässig, reproduzierbar, fehlerfrei.

Die Zeit, die ich dadurch gewonnen habe, habe ich in die nächste Automatisierung investiert. Und die nächste. Irgendwann war der Punkt erreicht, an dem ich mehr Zeit für echte Problemlösung hatte als für wiederkehrende Aufgaben.

Das ist das Versprechen der Automatisierung. Und es hält, was es verspricht — wenn man den anfänglichen Aufwand investiert.

Was ist neu in Qt 6.10? Ein Überblick für Entwickler

Qt 6.10 ist ein klassisches „Framework‑Release“, das an vielen Stellen nachzieht, wo sich in den letzten Jahren Anforderungen und Best Practices geändert haben: moderneres UI‑Layouten mit Flexbox, stärkere Unterstützung für Vektoranimationen (SVG/Lottie), vereinfachte Datenanbindung zwischen C++ und QML, neue Hilfstypen für Modelle und Bindungen – und dazu ein spürbares Update bei Accessibility und Plattformintegration.

Im Folgenden ein Überblick über die wichtigsten Änderungen von Qt 6.10, auf Basis der offiziellen Release‑Informationen.

Accessibility: High Contrast und Screenreader werden ernster genommen

Qt 6.10 legt sichtbar mehr Wert auf Barrierefreiheit:

  • High‑Contrast‑Mode auf allen Plattformen:
  • Die eingebauten Styles orientieren sich stärker an den jeweiligen System‑Einstellungen für hohen Kontrast.
  • Ziel: Qt‑Apps sollen sich optisch nahtlos in die restliche Oberfläche einfügen und dabei besser lesbar sein.
  • Für dich als Entwickler bedeutet das: Ohne Codeänderung profitierst du von verbessertem Kontrast, wenn Nutzer das systemweit aktivieren.
  • Bessere Integration mit Assistive Technologies:
  • Qt Widgets und Qt Quick Controls wurden überarbeitet, damit Screenreader und andere Hilfsmittel sauberer angesprochen werden.
  • Besonders für WebAssembly gab es Verbesserungen in der Accessibility‑Implementierung.
  • Viele Änderungen fließen auch in LTS‑Branches zurück (Patch‑Releases), sofern du dort aktuell bleibst.

Unterm Strich: Qt 6.10 hilft dir bei der Einhaltung von Accessibility‑Vorgaben, ohne dass du an jeder Ecke Speziallogik schreiben musst.

Qt Quick: FlexboxLayout und moderne UI‑Bausteine

Qt Quick bleibt die Speerspitze für neue UI‑Konzepte. Mit Qt 6.10 kommen unter anderem:

FlexboxLayout (Tech Preview)

  • Neues Layout‑Element FlexboxLayout für Qt Quick, das sich an CSS Flexbox anlehnt.
  • Vorteile:
  • Besseres Verhalten bei unterschiedlich großen Screens und Aspect Ratios.
  • Weniger Custom‑Layoutcode für „responsive“ UIs.
  • Vertraut für alle, die bereits mit Web/CSS arbeiten.
  • Integration:
  • Bindet sich in das bestehende Layout‑System von Qt Quick ein (attached properties etc.).
  • Ist als Technologievorschau gekennzeichnet – API kann sich bis zum nächsten LTS noch ändern.

Animierte Vektorgrafiken (SVG & Lottie)

Qt 6.10 baut konsequent auf den SVG/Vektor‑Verbesserungen der vorherigen Versionen auf:

  • VectorImage (aus 6.8 bekannt) wird erweitert:
  • Unterstützung für animierte Vektorgrafiken in
    • SVG‑Format und
    • Lottie‑Format.
  • Qt Lottie‑Modul:
  • Deutlich verbesserte Unterstützung moderner Lottie‑Dateien.
  • Lottie‑Assets können jetzt als skalierbare, hardwarebeschleunigte Vektorgrafiken direkt im Qt‑Quick‑Scenegraph gerendert werden.

Für UI‑Designer bedeutet das: Aufwendige, aber leichtgewichtige Animationswelten lassen sich direkt aus Design‑Tools (Figma/After Effects → Lottie) in Qt übernehmen.

Neuer Quick Control: SearchField

  • Spezialisierter Eingabebaustein für Suchfelder.
  • Bringt:
  • nativen Look & Feel auf allen Plattformen (wie andere Qt Quick Controls),
  • integrierte Vorschlagsliste mit Modellanbindung.
  • Integration:
  • suggestionModel kann über die üblichen Mechanismen (QAbstractItemModel, Kontextobjekte, QML‑Modelle) befüllt werden.
  • Besonders interessant in Kombination mit den neuen Modell‑/Bindungswerkzeugen (siehe unten).

Einfachere Verbindung zwischen C++‑Daten und QML‑UIs

Ein klassischer Schmerzpunkt in Qt‑Projekten war die Verbindung zwischen C++‑Backend und QML‑UI. Qt 6.10 nimmt sich genau dieser Nähte an.

QRangeModel: C++‑Container direkt als Modell

  • QRangeModel ist eine schlanke QAbstractItemModel‑Implementierung, die C++‑Ranges (z.B. std::vector, std::array, beliebige iterierbare Container) direkt als Modell zur Verfügung stellt.
  • Eigenschaften:
  • Kann einfache Typen (int, QString, …) wie auch komplexe Typen (GADGETs, std::tuple) abbilden.
  • Modellrollen werden automatisch erzeugt.
  • Funktioniert gleichermaßen in Qt Widgets (Views) und in QML/Qt Quick.
  • Beispiel‑Anwendung:
  • std::vector<int> als Liste in einer QML‑ListView,
  • std::vector<MyGadget> als typ‑sicheres Modell in Delegates mit required property.

Damit entfällt für viele Anwendungsfälle das Schreiben eigener QAbstractItemModel‑Subklassen.

delegateModelAccess: Zurückschreiben in das Modell vereinfachen

Bisher war das Schreiben aus einem Delegate zurück in das Modell oft umständlich:

  • Entweder über model‑Objekte im Delegate
  • oder über Kontextproperties und eigene Signal‑Handler.

Mit Qt 6.10 kann eine View über delegateModelAccess: DelegateModel.ReadWrite explizit erlauben, dass required‑Properties im Delegate direkt zurück ins Modell schreiben. Das reduziert Boilerplate deutlich, insbesondere in größeren QML‑UIs.

Synchronizer: Zwei‑ und Mehrwege‑Bindungen

  • Neues Element Synchronizer (Tech Preview, Modul Qt.labs.synchronizer).
  • Zweck:
  • Mehrere Properties so synchron halten, dass sie (so weit möglich) denselben Wert haben, ohne klassische Bindungen zu „brechen“.
  • Funktioniert für Kombinationen von C++‑ und QML‑Eigenschaften.
  • Praxisnutzen:
  • Typische „Slider ↔ Modell‑Wert“‑Szenarien lassen sich deklarativ und ohne zusätzliche Signal‑Handler abbilden.

TreeModel für QML

  • Neues QML‑TreeModel, mit dem sich Baumdaten direkt in QML deklarieren lassen.
  • Zielgruppe:
  • Prototyping,
  • kleinere Datenmengen,
  • Szenarien, in denen ein kompletter C++‑Baummodell‑Layer Overkill wäre.

Zusammen genommen macht Qt 6.10 die Kante zwischen C++‑Backend und QML‑Frontend deutlich angenehmer.

Qt Graphs: FilledSurface und mehr

Qt Graphs wird auch in 6.10 weiter ausgebaut:

  • Neue Diagrammtypen und Anpassungen:
  • u.a. ein neuer „FilledSurface“‑Graph zur besseren Darstellung gefüllter Flächen.
  • Bessere Integration in Qt Quick und die neuen Layout‑/Vektorfeatures.

Wenn du bereits mit Qt Graphs arbeitest, lohnt sich ein Blick in die konkreten Release Notes für dieses Modul.

Sonstige Verbesserungen und Plattform‑Updates

Wie bei jedem Qt‑Release gibt es eine Reihe weiterer Verbesserungen:

  • Plattformintegration:
  • Qt 6.10 richtet sich nach den jeweils aktuellen Versionen der großen Desktop‑, Mobile‑ und Embedded‑Plattformen (siehe Release Note und Wiki‑Seite).
  • Bugfixes und Feinschliff:
  • Dutzende Fehlerkorrekturen in den Untermodulen (Widgets, Quick, Network, etc.).
  • Viele Details lassen sich in den detaillierten Release Notes (6.10.0–6.10.2) nachlesen.

Fazit

Qt 6.10 ist kein „großer Sprung“ im Sinne völlig neuer Konzepte, sondern ein Release, das viele typischen Alltagsbaustellen adressiert:

  • Accessibility wird ernst genommen (High Contrast, Screenreader‑Integration).
  • Qt Quick bekommt mit FlexboxLayout, animierten Vektorgrafiken und SearchField Werkzeuge, die aktuelle UI‑Designs besser abbilden.
  • Die Verbindung zwischen C++‑Daten und QML‑UIs wird durch QRangeModel, delegateModelAccess, Synchronizer und TreeModel deutlich entschärft.

Wer bereits auf Qt 6.x unterwegs ist, sollte Qt 6.10 vor allem dann in Betracht ziehen, wenn:

  • Accessibility und regulatorische Vorgaben eine Rolle spielen,
  • moderne, animierte UIs (Vektor/Lottie) benötigt werden,
  • oder die Brücke zwischen C++‑Backend und QML‑Frontend bislang viel Handarbeit erfordert.

Quellen

  • Qt Blog: Qt 6.10 Released! – https://www.qt.io/blog/qt-6.10-released
  • Qt Wiki: Qt 6.10 Release – https://wiki.qt.io/Qt_6.10_Release
  • Qt Doku: New Features in Qt 6.10 – https://doc.qt.io/qt-6.10/whatsnew610.html

KooKooK: App für neurodivergente Menschen als Testballon

Vor einiger Zeit wollte ich mich mit QML näher auseinandersetzen und suchte nach einer Projektidee. Als Domain hatte ich noch „kookook.org“ bei mir registriert und dachte, dass ich einfach damit was mache. Bei der Projektidee ging es, allen voran, darum, dass ich mich mit dieser Technik auseinandersetzen und mich vor allem auf mobile Plattformen (iOS, Android) konzentrieren wollte.

Ich habe mich entschieden, an einer App für neurodivergente Menschen zu arbeiten, um ein Ziel vor Augen zu haben. Herausgekommen ist jetzt eine kleine App, mit der man seine Stimmungen und Reize tracken kann. Das Programm kann noch nicht viel, aber vielleicht interessiert es jemanden.

Hier sind ein paar Screenshots:

Es gibt auch eine Android-Version, die ist im Google Play Store allerdings noch nicht freigegeben, da ich dafür erst 12 Tester benötige und mir dazu gerade die Zeit fehlt. Wer Lust hat: Gerne per E-Mail an mich, ich schalte die Version dann frei.

Die App ist kostenlos und benötigt keine Internetverbindung.

Softwareentwicklung Teil 2: Weitere Erfahrungen

Mir fallen noch einige weitere Dinge ein, die ich hier zu teilen versuche. Meist geht es um technische Schulden und, für mich, komische Ansätze in der Verwaltung von Firmen.

Ich war vierzehn Jahre lang in einem Unternehmen beschäftigt und das hat, zumindest in Bezug auf die IT, auf die IT-Abteilung gehört. Haben wir immer die richtigen Entscheidungen getroffen? Nein. Aber zumeist und aus den fehlgeschlagenen Entscheidungen haben wir immer gelernt. Ich möchte das hier einmal als Positivbeispiel anbringen. Ich schätze, es muss 2012 gewesen sein, als wir neue Server anschafften. Zwei baugleiche Geräte für Redundanz. Wir kamen auf die Idee, dort Festplatten mit 4TB Speicherkapazität einzubauen und entschieden uns für Western Digital SAS. Leider wusste keiner von uns, und da hätten wir uns vorher definitiv noch einmal genauer informieren müssen, dass es SAS-Festplatten und Nearline-SAS-Festplatten gab. Wir waren zwar verwundert über die Preise, denn eine solche Platte mit 4TB in etwa so viel wie welche mit 960GB, aber wir forschten nicht weiter nach, sondern freuten uns.

Wir klatschten die Server also mit einer Menge dieser Platten voll, richteten darauf zraid-2 ein (RAID 6) und banden die Geräte ins Netzwerk ein. Wie zuvor auch, exportieren wir die Home-Laufwerke gut 50 Clients per NFS. In Tests funktionierte alles tadellos und wir schlossen glücklich und schnell die Umstellung ab. Dann kamen die Leute und wollten arbeiten. Es kam zu großen Wartezeiten und ein gewisser Unmut machte sich breit, denn das System performte nicht. In einzelnen Tests lief es hervorragend, sequentielles Schreiben und Lesen funktionierte tadellos. Aber in der Masse und bei der Menge an Dateien war es grottenlangsam und unbenutzbar. Wir suchten zu zweit sicher einen Monat nach dem Problem, bis ich auf die Idee kam, dass die Platten in Form der Hardware schuldig waren. Wir stellten dann ein solches Szenario nach und stellten dem gegenüber ein System mit echten (super teuren) SAS-Festplatten und siehe da: Genau da war das Problem. Die Lösung war schnell da: Neue Festplatten mussten her. Wir besprachen das mit der Geschäftsleitung, räumten ein, dass es letztlich unsere Schuld war, wir das aber vorher nicht wussten und es kein böser Wille war. Das Ende vom Lied war, dass wir neue Festplatten, die teuer waren und eine niedrige Kapazität hatten, kauften und einbauten. Das kostete die Firma natürlich eine Unmenge an Geld (ich habe noch gut 12.000 Euro im Kopf). Uns wurde aber nicht der Kopf abgerissen. Erstens wäre der weitere Betrieb in alter Form ohnehin teurer geworden, da niemand arbeiten konnte, und zweitens wusste man, dass Fehler passieren. Wir haben das zusammengefasst, noch einmal berichtet, was wir gelernt hatten, und solche Probleme traten dann nie wieder auf.

In einer anderen Firma sprach ich einmal an, dass wir einen gewissen Style-Guide einführen sollten, und dieser bezog sich nur auf die Benennung von Variablen, Konstanten und Funktionen. Die Benennung solcher Dinge kann man weiter unten in meinem vorherigen Artikel nachlesen, da habe ich ein Beispiel. Ich sprach mich für „sprechende“ Namen aus. Dies wurde abgelehnt, denn dann hätte man ewig lange Namen, mehr aussagen würden diese auch nicht, und man würde sich tottippen (VI(M) war der Editor der Wahl und mit [CTRL]+[p] bzw. [CTRL]+[n] ergänzt er begonnene Namen vollautomatisch). Aufgrund der Ablehnung und auch aufgrund anderer, vorheriger und späterer Aussagen, war mir klar, dass da keine zielbringende Diskussion möglich war. Also ließ ich es.

In der selben Firma kam man auf die Idee, die (teuer zu bezahlenden) Mitarbeiter ein bis mehrmals pro Woche (später dann die Entwickler, die an einem gewissen Projekt arbeiteten, alle zwei Wochen) halbtägig in den technischen Service zu stecken. Der Tenor, so ich mich noch richtig erinnere, war, dass diese dann mehr vom Drumherum mitbekommen und Prozesse besser verstehen. Man war dann in der Tickethölle und musste bestimmte, ausgewählte, redundante Aufgaben abarbeiten. Das Problem: Für viele Serviceaufgaben gab es entweder keine Tools oder diese waren so dermaßen komplex, fehleranfällig und schwergängig, dass sie ein „normaler“ Servicemitarbeiter nicht hätte benutzen können. Bereits einfache Aufgaben konnten ganze Prozesse zerschießen. Anstatt die Softwareentwickler laut ihrer Kompetenzen dazu zu bringen, Tools zu entwickeln, die der Service nutzen hätte können, nutzten wir die halbgaren Tools in der Hoffnung, nichts Schlimmeres zu machen. Doch trotz großer Vorsicht passierte dies ab und an, auch mir zwei Mal. Einmal, ich weiß es noch, haben ein Kollege und ich mehr als einen halben Tag daran gesessen, das Problem zu fixen. Dabei wäre es wirklich sinnvoller gewesen, diese Tools robust zu machen.

Mir fällt noch ein Beispiel in dieser Firma ein. Wir hatten einen Workshop und mussten danach irgendwelche Funktionen noch fertig machen. Beim Testen wurde bei einem der komplexen Parameter aus Versehen die falsche Datenbank angegeben. Keinem von uns, wir waren drei oder vier Leute, fiel dies auf. Leider wurden dann wichtige Datensätze von Kunden in der Produktivumgebung überschrieben bzw. gelöscht. Die Produktivumgebung für mehrere tausend Kunden war nicht vollständig von der Entwicklungsumgebung abgeschirmt. Mein Chef hat es zwar hinbekommen und war auch nicht böse, aber es hätte auch anders ausgehen können. Beim Vorschlag, dass sowas irgendwie anders gelöst wird, kam nur ein lapidares „Nutzen Sie doch einfach die richtige Datenbank.“. Schlussendlich wurde doch zumindest ein Parameter im Programm umbenannt.

Es gibt an dieser Stelle zwei wichtige Dinge:

  1. Die Fehlerkultur in einem Unternehmen bestimmt auch die Qualität der Software, die Weiterentwicklung dieser, die der Entwickler und zu guter Letzt die des Unternehmens.
  2. Das Unternehmen sollte unbedingt die Vita der Leute, die es beschäftigt, ansehen. Wer kann wo am besten eingesetzt werden? Wert hat wo die besten Erfahrungen? Usw. usf. Das wurde in einigen Unternehmen, in denen ich war, nicht getan.

Die schrecklichste Zeit, die ich je hatte, waren meine zwei Monate bei einem Softwareunternehmen in Koblenz. Das Unternehmen hatte unglaublich viele Probleme. Neben der bescheidenen Bezahlung für die Programmierer war allen voran die Qualität des Codes völlig unzulänglich. Niemand sah sich meine Vita an, doch einer meiner Programmierkollegen meinte irgendwann: „Er soll sich doch mal die Memory-Leaks in unserer Software ansehen, er kennt sich da aus.“

Ich wühlte mich also durch etliche Zeilen kaum lesebaren Codes, da es auch hier keinen einheitlichen Style gab. Viel schlimmer war aber, dass ich nur Zugriff auf einen auf ein Core-System aufsetzenden Code hatte, aber nicht auf das Core-System selbst. Im Core-System gab es aber etliche Probleme. Zugriff bekam ich trotzdem nicht, so dass ich meiner Aufgabe überhaupt nicht sinnvoll nachgehen konnte. Dass nur ich das alles kaum verstand, sah man am Versionsverwaltungssystem (Subversion): Von den meisten Kollegen gab es maximal einen Commit pro Woche und der Commit hatte meistens nur ein bis drei Codezeilen inne. Niemand stieg durch das System.

Auch die Aufteilung innerhalb dieser Firma war äußerst komisch. Es kam SAFE zum Einsatz, das aber nur nebenher. Aufgeteilt wurde die Entwicklung zwischen zwei Menschen: dem Requirements-Engineer (kurz RE) auf der einen, dem Programmierer auf der anderen Seite. Gehen wir von einer Rolle als Senior-Softwareentwickler (was auch immer das sein soll) aus. Der RE bekam den Auftrag für ein neues Modul oder einen Teil eines Moduls. Dieser beschrieb dann, was gemacht werden sollte (aus meiner Sicht noch ok) und dann unterteilte er es in etliche Untertasks. D.h. der RE musste dem Softwareentwickler seine eigenen Kompetenzen wegnehmen und für ihn planen, wie der Softwareentwickler dies umzusetzen hatte. Und der Softwareentwickler musste auf diese Tickets seine Zeiten buchen (das war übrigens die Hauptaufgabe: Verwalten seiner Zeiten, jede Sekunde musste protokolliert werden) und sich dafür rechtfertigen, wenn er länger brauchte oder Dinge nicht funktionierten. Das habe ich in den zwei Monaten, in denen ich da war, öfter erlebt. Man musste sich vor seinem Vorgesetzten und mitunter vor dessen Vorgesetzten verantworten. Es war die pure Hölle, was ich mittlerweile aber auch verstehen kann, weiß ich doch mittlerweile, in welcher Szene sich der Gründer des Unternehmens widerwärtig aufhält.

Kommen wir noch einmal auf die erste Firma zurück. Dort war eines der Probleme, wie ich in einem anderen Artikel bereits schrieb, dass unglaublich viel Code generiert wurde. Dabei ging es um eine Art OR-Mapper für DBMS und XML. Objektorientierte Entwicklung und generische Programmierung wurden hier immer wieder abgelehnt, obwohl diese unglaublich viele Probleme gelöst hätten. Man entschied sich für die Erneuerung des Codes für eine Multiparadigmensprache. Diese kann zwar Objektorientierung, aber nicht wirklich Vererbung. Warum die Wahl auf diese Programmiersprache fiel, war mir nie klar. Letztlich wurde das damit bekräftigt, dass die neue Generation von Programmierern kein C mehr kann. Auf C basierte die meiste alte Software (wenig auf C++, Ruby, Python, Shell-Scripts, usw.). Die Wahl hielt ich für suboptimal. Ein sinnvoller Weg wäre vielleicht gewesen, auf C++ umzusteigen. Da ein Projekt bereits in C++ umgesetzt war, waren auch schon bestimmte Kompetenzen vorhanden. Auch hätte man einen Teil des alten Codes leichter weiterverwenden können, als es mit der ausgewählten Programmiersprache möglich war (wenn diese auch C unterstützte). Die Ablösung wäre vielleicht einfacher gewesen, aber natürlich auch, dass Programmcode 1:1 übernommen worden wäre, was vielleicht nicht das eigentliche Ziel war. Es wurde allerdings Code übernommen und nahezu 1:1 in die neue Programmiersprache gesetzt. Variablen hießen gleich, Funktionen hießen gleich, der Programmablauf war der gleiche.

Zum Einsatz kamen zwei DBMS. Ein teures von einem teuren Hersteller und, für das neue Projekt, ein super gutes, welches ich auch oft empfehle. Beide DBMS wurden aber nicht wirklich als DBMS genutzt, sondern nur als Puffer für irgendwelche Daten. Auch auf vernünftige referentielle Integrität wurde, so wie ich die Datenstrukturen wahrnahm, verzichtet. Je nachdem, ob man in der Entwicklung war und Daten löschen wollte, musste man die händisch aus etlichen Tabellen entfernen. Von Normalisierung und Stored Procedures usw. müssen wir gar nicht sprechen.

Softwareentwicklung: Verwaltung zuerst – aber wie steht es um Softwarequalität?

In den letzten Jahren habe ich in verschiedenen Unternehmen gearbeitet, die zwar unterschiedlich waren, aber mit ähnlichen Problemen konfrontiert waren. Mit diesem Text möchte ich meine Perspektive auf diese Herausforderungen teilen.

Als Softwareentwickler und Administrator habe ich an zahlreichen Projekten im Softwarebereich, in der Administration sowie im Aufbau von Netzwerken und Client-Server-Strukturen gearbeitet. Einige dieser Projekte habe ich selbst geleitet, andere in Zusammenarbeit mit verschiedenen Personen betreut.

In meinen letzten Positionen war ich jedoch hauptsächlich auf die Programmierung beschränkt. Ich hatte keine Entscheidungsbefugnis und konnte mein Wissen und meine Expertise nicht sinnvoll einbringen. Stattdessen führte ich eine monotone und ermüdende Fließbandarbeit aus. Diese Erfahrung ermöglichte es mir jedoch, die Probleme aktueller Softwareunternehmen zu beobachten, die in einem ewigen Kreislauf der Selbstoptimierung gefangen sind, aber im IT-technischen Kernbereich keine bedeutenden Fortschritte erzielen.

Zwei Hauptthemenbereiche traten in diesen Unternehmen hervor (nicht in jedem, aber mindestens in einem):

  1. Die Notwendigkeit, agil zu werden und zu bleiben.
  2. Die Implementierung von CI/CD um jeden Preis.

Für die meisten klingen diese beiden Punkte sinnvoll, und in gewisser Weise sind sie es auch, insbesondere der zweite Punkt. Die Umsetzung dieser Ansätze erfolgte jedoch nicht als strukturierter Prozess, sondern eher als eine übergeordnete Aufgabe, die den Fokus von den wirklich wichtigen Aufgaben ablenkte.

Beginnen wir mit dem Thema Agilität. Was neue Softwarefirmen (Startups) von Anfang an praktizieren, wird in etablierten Unternehmen oft nur mühsam eingeführt. Ich erinnere mich noch gut daran, wie wir uns in einem Unternehmen, das von technischen Schulden geplagt war, ausschließlich auf die Einhaltung agiler Prinzipien konzentrierten, anstatt die eigentlichen Probleme anzugehen. Das bedeutete, dass wir mit Hochdruck Tools einführten und Schulungen durchführten, die die Mitarbeiter hauptsächlich beschäftigten. Da es Architekten, Product Owner und Scrum Master und so weiter gab, wollte kaum noch jemand Techniker sein, sondern einen solchen Titel tragen und Tickets und Epics durch die Gegend schieben. Das Softwareprodukt selbst blieb dabei auf der Strecke.

Wir hatten mehrere Schulungen mit einem Unternehmen, das sich meiner Meinung nach auf die Vermittlung esoterischer Agilität spezialisiert hatte. Alle zwei Wochen fand die Retrospektive statt, in der jeder seine Gefühle offenbaren musste und wir uns an wilden (Kinder-)Spielen wie „Wer ist schneller mit dem Mauszeiger?“ beteiligten. Wichtige Themen wie Bugs, Features, Refactoring, Neuausrichtung, Ideen und die Erweiterung unseres technischen Wissens wurden dabei völlig vernachlässigt. Stattdessen mussten wir uns gegenseitig loben und betonen, wie großartig unser Team sei. Das Produkt blieb oft ein einziges Chaos, und mir war die ganze Nummer mehr als peinlich.

Fahren wir mit CI/CD fort. Obwohl ich es für wichtig halte, ist es nicht so entscheidend wie das Produkt selbst. In einer Firma musste ich ein Frontend entwickeln, das zwei Kommandozeilenprozesse startete und überwachte. Es war eine relativ einfache Software, die schnell geschrieben werden konnte. Tatsächlich musste ich sie zweimal schreiben. Zuerst verwendete ich Qt und C++, was zu einem funktionsfähigen, modernen und effizienten Produkt führte. Es wurde jedoch entschieden, dass es einfacher sein und lieber mit wxWidgets geschrieben werden sollte, da die Firma bereits Erfahrung mit dieser Technologie hatte. Daher begann ich mit dem Schreiben einer neuen Version. Obwohl der Funktionsumfang geringer war, dauerte die Entwicklung aufgrund der geringeren Abstraktion von wxWidgets länger. Schließlich war das Produkt fertig, funktionsfähig und bereit für die Auslieferung. Es handelte sich um eine relativ kleine Codebasis von etwa 4.000 Zeilen C++. Nachdem ich das Produkt fertiggestellt hatte, hatte ich aufgrund von schlechtem Management (man war ja mit Agilität beschäftigt) nichts mehr zu tun. Man schlug vor, dass ich mich mit dem Testing befassen sollte, da das Produkt in CI/CD integriert werden musste. Dies umfasste drei Hauptaspekte:

  1. Unit- und Integrations-Tests mit Google-Test
  2. Statische Codeanalyse mit SonarQube
  3. Automatische GUI-Tests

Ich habe mich dann über ein halbes Jahr ausschließlich mit diesem Projekt beschäftigt. Während die Unit- und Integrations-Tests noch relativ einfach zu handhaben waren, erwies es sich als deutlich schwieriger, Anwendungsfälle für eine Software mit über 4.000 Zeilen Quellcode zu entwickeln, von denen der Großteil GUI-bezogen war. Das agile Testmanagement verlangte eine Code-Coverage von mindestens 70 %. Anstatt mich also auf die eigentliche Kernsoftware zu konzentrieren, die dieses von mir entwickelte Frontend lediglich startete und überwachte, widmete ich mich der ABM und der Erstellung von Tests, die letztendlich keinen wirklichen Zweck erfüllten. Der Grund dafür ist einfach: Die Software war funktionsfähig, sollte nicht weiterentwickelt werden und war nicht anfällig für Fehler. Sie war ausgebaut und lediglich im Bestand. Darüber hinaus sollte sie in naher Zukunft durch eine andere Software ersetzt werden.

Trotz dieser Umstände durfte ich mich weiterhin mit der Umsetzung der teils eigenwilligen Standard-Regeln von SonarQube befassen. In den gesamten 4.000 Zeilen Code entdeckte SonarQube lediglich einen einzigen Bug (einen gelöschten Pointer, der in einem anderen Codesegment erneut aufgerufen wurde). Die restlichen Hinweise bezogen sich auf unbedeutende Aspekte wie die Anzahl der Member-Variablen und die Begrenzung der Methodenanzahl pro Klasse auf 30.

Da die Software auch zwei klickbare Buttons enthielt, musste ich vollautomatische GUI-Tests implementieren. Dies stellte sich bei einer wxWidgets-Anwendung mit C++ als Herausforderung dar, konnte aber erfolgreich gemeistert werden. Dadurch wurde das gesamte GUI vollautomatisch testbar.

Obwohl dies die Software, die im Mittelpunkt stand, nicht direkt voranbrachte, fügte es doch ein weiteres Puzzleteil zur Pipeline hinzu. Ich konnte wertvolle Erkenntnisse gewinnen, die ich bei zukünftigen Softwareprojekten, wo es sinnvoll ist, anwenden kann.

In einer anderen Firma war ich an zwei oder drei Softwareteilen beteiligt. Auch hier wurde, im Zuge des Wachstums des Produkts, zunehmend Wert auf die Pipeline gelegt. Folglich mussten zahlreiche Tests nachimplementiert werden. Dies war nur bedingt sinnvoll, da drei wesentliche Probleme vorlagen:

  1. Die Qualität des Codes und insbesondere der gesamten Architektur war mangelhaft, wenn nicht gar nicht vorhanden.
  2. Es gab Fachverfahren, die eine umfassende Einarbeitung erforderten oder eine gezielte Einweisung notwendig gemacht hätten.
  3. Die Software sollte vollständig durch eine neue Version in einer anderen Programmiersprache ersetzt werden. Diese neue Architektur war jedoch ebenfalls fehlerhaft und entsprach nicht den Qualitätsstandards, die ich vertrete.

Einige der Tests hätten durchaus auch für die zweite Generation der Software verwendet werden können, was sinnvoll war, um das Verhalten der alten und neuen Software zu vergleichen. Doch meiner Meinung nach war das Projekt dazu verdammt, gegen die Wand zu fahren.

Unit-Tests, Integrationstests, Code-Reviews, CI/CD, statische Codeanalyse, GUI-Tests, Dokumentation, nachverfolgbare Ticketbearbeitung, Kompetenztransfer und vieles mehr sind entscheidend für eine nachhaltige Softwareentwicklung. Diese Elemente sollten jedoch von Anfang an integriert sein. Sie später, insbesondere bei großen Projekten, einzuführen, ist ineffektiv, vor allem bei komplexen Projekten mit Hunderttausenden von Codezeilen. Darüber hinaus können andere technische Schulden so hoch sein, dass sie dringendere Aufmerksamkeit erfordern.

Ich habe es immer wieder erlebt, dass Unternehmen weiterhin auf veraltete Frameworks setzen. Ich bin auf Systeme gestoßen, die 20 Jahre alt oder noch älter waren und längst nicht mehr unterstützt wurden. Oder auf Plattformen, die über 20 Jahre alt waren und auf denen immer noch die Software für aktuelle Systeme kompiliert wird, weil die Migration auf aktuelle Plattformen vernachlässigt wurde.

Doch das ist nicht alles. Die Qualität von Software-Code ist ein entscheidender Faktor in der Softwareentwicklung. Ich habe Ein-Personen-Projekte erlebt, die aufgrund ihrer zunehmenden Komplexität oder der Sorge um die langfristige Verfügbarkeit des ursprünglichen Entwicklers zusätzliche Teammitglieder erforderten. Diese neuen Teammitglieder haben jedoch oft Schwierigkeiten, den Code, die Fachverfahren und das gesamte System zu verstehen.

Aber warum ist das so? Ein Ein-Personen-Projekt hat von Natur aus seine Grenzen. Der ursprüngliche Entwickler versteht den Code vielleicht noch, aber das gilt nicht unbedingt für andere. In den letzten Jahren bin ich immer wieder auf schlecht geschriebenen Code gestoßen. Schon die Verwendung von Funktionen, Methoden oder Variablennamen wie „reg_rq_fachver2_23“ ist wenig hilfreich. Darüber hinaus habe ich unglaublich redundanten Code gesehen, wie zum Beispiel:

int za_wwggw_w2(int p, int p_k, int p_key, int k, int key, char *pk) {
int _key;
struct pkey *pkey_p;
return za_wwggw_w3(p, p_k, p_key, k, key, pk, &_key, pkey_p);
}

Selbst nach mehrmaligem Lesen verstehe ich diesen Code nicht. Wenn ich mich durch etwa 50 Dateien mit mehreren zehntausend Zeilen Code kämpfen müsste, der genauso oder noch schlimmer aussieht, würde ich irgendwann aufgeben.

Es gab Software, bei der GUI-Komponenten mit Namen wie Edit1, Edit2, Edit3 bis Edit123 versehen waren. Diese Praxis führte zu immensen technischen Schulden, die ich nicht erklären kann. Schließlich ist allgemein bekannt, dass sauberer und lesbarer Quellcode die Notwendigkeit von Dokumentation minimiert (die oft ohnehin nicht vorhanden ist), das Bugfixing und die Wartbarkeit erhöht sowie die Einarbeitung anderer Menschen vereinfacht.

Betrachten wir das Projekt mit der Methode za_wwggw_w2. Es war offensichtlich, dass das Projekt unkontrolliert gewachsen war und eine Reimplementierung erforderlich war. Meiner Meinung nach war dies der einzige praktikable Ansatz, um das Projekt weiter auszubauen. Allerdings sah ich sofort mehrere Probleme:

  1. Die Reimplementierung wurde nicht als eine vollständige Neubegründung des Projekts verstanden, was angesichts seiner Komplexität eine gewaltige Aufgabe gewesen wäre. Stattdessen wurde sie als eine Portierung des alten Codes auf eine neue Programmiersprache wahrgenommen, mit minimalen Anpassungen, um die Besonderheiten der neuen Sprache zu berücksichtigen. Als ich den Code der anderen Programmierer untersuchte, stellte ich fest, dass er fast identisch mit dem alten Code war, mit nur wenigen geringfügigen Änderungen. Die zugrundeliegenden Probleme blieben bestehen, und ohne jemanden direkt verantwortlich machen zu wollen, würde ich behaupten, dass die meisten, die an dieser Umsetzung beteiligt waren, sie nicht vollständig verstanden.
  2. Darüber hinaus wurde im Prozess keine Architektur implementiert. Da die neue Programmiersprache eine einfache Integration von Code aus dem alten System ermöglichte, wurde diese Funktion häufig genutzt.
  3. Es wurde eine Masse an „gleichem“ Code automatisch generiert, anstatt auf Abstraktion, Ableitung und Konventionen zu setzen. Viele tausende Zeilen gleicher Code mit leicht unterschiedlicher Logik wurde generiert.

Leider wurden die gleichen Fehler gemacht wie bei dem Ein-Personen-Projekt, obwohl das Team aus erfahrenen Programmierern bestand, die jahrelange und jahrzehntelange Erfahrung mitbrachten. (Ich kam relativ spät dazu und wurde hauptsächlich als „Programmieräffchen“ eingestellt.)

Diese Erfahrung habe ich in zahlreichen Projekten gemacht. In einem Projekt, das stark auf eine Datenbank angewiesen war, deaktivierte mein Vorgänger die referentielle Integrität mit der Begründung, es würde die Leistung verbessern. Dies führte jedoch dazu, dass die Datenbank in einen fehlerhaften Zustand geraten konnte (was auch mehr als nur einmal passierte). Solche Probleme hätten leicht behoben werden können, indem beispielsweise ein zuverlässiges Datenbankmanagementsystem (DBMS) verwendet oder qualitativ hochwertiger Code geschrieben worden wäre. Trotzdem wurden diese Maßnahmen nicht ergriffen.

Meine Botschaft ist klar: Anstatt sich ausschließlich auf das Umfeld zu konzentrieren, würde es vielen Produkten zugutekommen, wenn ein stärkerer Fokus auf die Produkte selbst gelegt würde. Auch vorher Gedanken zu machen und Erfahrungen von Menschen einzubringen, die das seit Jahren/Jahrzehnten bereits machen, ist absolut sinnvoll.

Testaufgaben nach einem Bewerbungsgespräch

Ich habe jetzt länger überlegt, ob ich darüber überhaupt bloggen möchte. Ich mache es doch einmal, denn es gab für mich etwas sehr interessantes und es ist schon eine ganze Weile her.

Ich war auf Jobsuche, schrieb hier und da eine Bewerbung. Dann hatte ich ein Vorstellungsgespräch und der Herr am anderen Ende der Telefonleitung meinte, es könnte passen. Nachdem er über meine Konditionen nachdachte und das mit der Geschäftsleitung durchsprach, bekam ich einen Anruf, dass diese passen würden, die Firma aber eine Testaufgabe für mich hätte. Darum soll es hier gehen, nämlich um diese eine Testaufgabe und um Testaufgaben im Allgemeinen.

Nachdem wir den Techstack abgeklärt hatten und es darum um PHP und Symfony ging und ich auch sagte, dass ich von beidem keine Ahnung habe (PHP habe ich das letzte Mal vor 20 Jahren gemacht), wurde mir versichert, dass vor dem persönlichen Kennenlernen nur noch eine einfache Testaufgabe zu meistern wäre, nämlich die Umsetzung eines Kontonummer-Bankleitzahlen-IBAN-Konverters. Ich sagte: kein Problem, das musste ich auch schon in einem echten Projekt vor einigen Jahren machen, er soll mir die Aufgabe zukommen lassen. Kurze Zeit später war dann das Dokument mit der Testaufgabe da. Fünf vollgeschriebene DinA4-Seiten. Dabei ging es nicht um die Entwicklung eines Konverters, sondern um ein gesamtes Projekt mit etlichen Zwischenstationen.

Ich sollte mich erst einmal in PHP 8.1 einarbeiten. Danach in Symfony 6.1. Um beides richtig zu machen, würde ich schon keine Stunden mehr veranschlagen, sondern eher einige Tage bishin zu wenigen Wochen. Eine Programmiersprache lerne ich zwar schnell, vor allem PHP, bei der ich nicht einmal von ganz vorne einsteigen müsste, dann aber noch ein komplexes Framework, das ist Aufwand.

Zusätzlich dazu sollte ein Login-Verfahren entwickelt werden und eine dazugehörige Website mit Datenbankanbindung (MySQL oder MariaDB). In der Datenbank sollten die bereits zuvor konvertierten Daten tabellarisch dargestellt werden. Jeder Aufruf sollte einen Zähler inkrementieren.

Weiterhin sollten die Daten dann als JSON exportiert werden können.

Dazu gehörte dann die Installation und Konfiguration von Debian 11, die Installation der benötigten Software (spezielle PHP-Version, nicht in APT drin, MySQL oder MariaDB, Symfony und Apache oder NGINX).

Das war der technische Part. Dazu kam dann ein Benutzerhandbuch für den Konverter über Funktionalität und Bedienung. Dann noch eine Dokumentation für das gesamte Deployment durch einen Laien (Installation und Konfiguration Betriebssystem, Installation und Konfiguration Webserver, Installation und Konfiguration Datenbankserver, Installation und Konfiguration PHP, Installation und Konfiguration von Symfony – durch einen Laien!).

Weiterhin sollte das Projekt vorher komplett durchgeplant werden, vermute mit Pflichenheft, war nicht genauer beschrieben und es sollten auch Tests (Unit-Tests, händische Tests) durchgeführt und dokumentiert werden.

Versteht mich bitte nicht falsch: Das alles kann ich, aber rein realistisch, wenn man es vernünftig macht, ist das ein Projekt, das man nicht in einigen Stunden hinbringt, selbst, wenn man PHP und Symfony auf hohem Level beherrscht. Schon alleine am Pflichtenheft schreibt man eine Weile, das Benutzerhandbuch rotzt man auch nicht einfach runter…

Ich lehnte den Job dann mit Begründung ab. Eine Antwort darauf erhielt ich nie.

Unterschiede zwischen Qt und wxWidgets

Qt und wxWidgets sind zwei beliebte Frameworks für die Entwicklung von Benutzeroberflächen, die sich in technischen und Lizenzunterschieden unterscheiden. Technisch gesehen basiert Qt auf einer objektorientierten Architektur mit einem eigenen Meta-Objekt-System (MOC), das Funktionen wie Signale und Slots für die Ereignisbehandlung bietet. Im Gegensatz dazu verwendet wxWidgets eine Wrapper-Architektur, die die nativen APIs der Zielplattform nutzt, was zu Anwendungen führt, die sich stärker an das native Look-and-Feel anpassen. In Bezug auf Programmiersprachen unterstützt Qt hauptsächlich C++, bietet aber auch Bindings für Sprachen wie Python (PyQt/PySide). wxWidgets ist primär für C++ konzipiert, hat aber ebenfalls Bindings für Python (wxPython) und Perl. Beim GUI-Design kommt Qt mit dem leistungsstarken Qt Designer zur visuellen Gestaltung von Benutzeroberflächen, während wxWidgets oft manuelles Codieren der GUI oder den Einsatz von Drittanbieter-Tools erfordert. In Bezug auf die Plattformunterstützung deckt Qt Windows, macOS, Linux sowie Embedded-Systeme und mobile Plattformen (Android, iOS) ab. wxWidgets unterstützt ebenfalls Windows, macOS und Linux, hat aber eingeschränkte Unterstützung für mobile Geräte.

In Bezug auf die Lizenzierung ist Qt unter GPL, LGPL und kommerziellen Lizenzen lizenziert. Die GPL erfordert die Offenlegung des Quellcodes von abgeleiteten Werken, während die LGPL eine flexiblere Nutzung ermöglicht, insbesondere für proprietäre Software, wenn dynamische Verlinkung verwendet wird. Unternehmen können auch eine kommerzielle Lizenz erwerben, um diese Einschränkungen zu umgehen. wxWidgets wird unter der wxWidgets-Lizenz veröffentlicht, die sehr liberal ist und ähnlich wie die LGPL funktioniert. Sie erlaubt die Entwicklung von sowohl Open-Source- als auch proprietärer Software, ohne die Notwendigkeit, den Quellcode offenzulegen.

Zusammenfassung der Unterschiede

  • Architektur:
    • Qt: Objektorientiert mit MOC, Signale und Slots.
    • wxWidgets: Wrapper-Architektur, nutzt native APIs.
  • Programmiersprachen:
    • Qt: Hauptsächlich C++, Bindings für Python und andere.
    • wxWidgets: Primär C++, Bindings für Python und Perl.
  • GUI-Design:
    • Qt: Qt Designer für visuelle Gestaltung.
    • wxWidgets: Oft manuelles Codieren oder Drittanbieter-Tools.
  • Plattformunterstützung:
    • Qt: Windows, macOS, Linux, Embedded, mobile (Android, iOS).
    • wxWidgets: Windows, macOS, Linux, eingeschränkte mobile Unterstützung.
  • Lizenz:
    • Qt: GPL, LGPL, kommerziell.
    • wxWidgets: Liberale wxWidgets-Lizenz, ähnlich LGPL.

wxWidgets-Tutorial 014: Überblick Event-Handling

In diesem Video zeige ich grob, wie das Event-Handling funktioniert. Tiefer gehen wir darauf aber dann bei den einzelnen Widgets ein.

Letztlich gibt es drei Varianten, wovon die dritte „deprecated“ ist und nicht mehr in neuen Programmen benutzt werden soll:

  1. Event-Table
  2. Bind
  3. Connect
Überblick über Event-Handling

Ein Screenshot unseres Beispielprogramms.

Screenshot unseres Beispielprogramms

Der Quelltext:

#include <wx/wx.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();
	
	protected:
		void OnButtonClick(wxCommandEvent &event);

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("Events")) {
	wxButton *button = new wxButton(this, wxID_ANY, _("Click me"));

	button->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClick, this);
}

void MyFrame::OnButtonClick(wxCommandEvent &event) {
	std::cout << "Button clicked" << std::endl;
}

Hier geht es zum Video.

wxWidgets-Tutorial 013: wxGridBagSizer

In diesem Video zeige ich, wie ein wxGridBagSizer funktioniert.

Der wxGridBagSizer basiert auf dem wxFlexGridSizer, bietet aber die Möglichkeit, Widgets und Sizer darin explizit zu positionieren und der Sizer kann Zellen zusammenführen.

Sizer (Layoutmanager): wxGridBagSizer

Hier ein Screenshot von unserem Beispielprogramm:

Screenshot des Beispielprogramms

Der Quelltext:

#include <wx/wx.h>
#include <wx/gbsizer.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("wxGridBagSizer")) {
	auto *mainPanel = new wxPanel(this);
	
	auto *gridBagSizer = new wxGridBagSizer(5, 5);

	wxGBSpan twoSpan(1, 2);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Firstname:")), wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(0, 1), twoSpan, wxEXPAND);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Lastname:")), wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(1, 1), twoSpan, wxEXPAND);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Age:")), wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(2, 1));

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Zipcode/City:")), wxGBPosition(3, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(3, 1));
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(3, 2), wxDefaultSpan, wxEXPAND);

	gridBagSizer->AddGrowableCol(2);

	mainPanel->SetSizer(gridBagSizer);
	gridBagSizer->SetSizeHints(this);
}

Hier geht es zum Video.