Softwareentwicklung Teil 2: Weitere „spannende“ Erfahrungen in Firmen

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.

Der neue Server

Über die letzten 15 Jahre habe ich HP MicroServer als Home-Server eingesetzt. Ich hatte immer zwei mit FreeBSD (einen Main- und einen Backup-Server) sowie meist noch einen mit Windows, um dort Windows-Software auszuführen, der aber auch nicht permanent lief, sondern nur, wenn er gebraucht wurde.

In meinem letzten Hauptserver liefen vier vier Terrabyte große SATA-Festplatten im RAIDz1, also letztlich eine Art RAID-5 in Software. Das Ganze lief stabil und gut, aber meine heutigen Ansprüche waren viel zu langsam. Neben etlichen Jails mit etlichen Diensten (Web, Datenbanken, DHCP, NTP, DNS, Samba, NFS, uvm.) liefen auch zwei Minecraft-Server darauf und, gelinde gesagt, machte es einfach keinen Spaß mehr.

Etwas Neues musste her. Diesmal wollte ich keinen HP MicroServer mehr, da ich verschiedene Festplattenkombinationen wollte, um mehr Speed zu bekommen. Er sollte vernünftig virtualisieren können. Er sollte auch eine Möglichkeit haben, SAS-Festplatten mit Near-Line-SAS-Festplatten und SATA-Festplatten und -SSDs mischen zu können.

Dell PowerEdge T330

Aufgrund dessen, dass der Server für einen Privathaushalt ist, aber dennoch wichtig, speichert er immerhin wichtige Dokumente, ist für Entertainment da und auch zum Entwickeln von Software, entschied ich mich für eine gebrauchte Maschine, allerdings von einem Händler. Bei ServerShop24 entschied ich mich für einen Dell PowerEdge T330.

Meine Vorteile:

  • Die Maschine ist einfach zu öffnen und zu erweitern
  • Man kann, out of the box, 8 3,5″ HDDs reinstöpseln
  • SAS und SATA sind problemfrei mischbar (bei meiner Konfiguration).
  • FreeBSD läuft problemfrei drauf.

Meine Nachteile:

  • Dell (bäh).
  • FreeBSD bietet keine integrierte Lüftersteuerung, die muss man selbst bauen, was aber nicht so schwer ist – ansonsten drehen die Lüfter auf Vollgas und das macht keinen Spaß.
  • Sehr groß (eigentlich ist es mir egal).

Das ganze Ding hat 64GB RAM. Genug, für die ganzen Dienste, Jails, Virtualisierung von Windows 11. Ich habe zwei 300GB SAS HDDs für das Betriebssystem und Software drin (ZFS Mirror, gmirror für SWAP), 4 3TB NL-SAS HDDs (raidz-1) für das Datengrab und zwei SATA-SSDs für Virtualisierung und Minecraft (ZFS Mirror). Das System ist verschlüsselt.

Zusätzlich habe ich für ein paar Euros einen SD-Card-RAID-Controller gekauft, der zwei SD-Cards aufnimmt und die miteinander synchron hält. Darauf sichere ich immer wieder Systemdateien als Backup. Das ist aber eher eine Spielerei.

Weiterhin habe ich eine Netzwerkkarte mit vier Ports verbaut, mit der ich den Server mehrfach via LACP ans Netzwerk anbinden kann. So hat bspw. die Windows-VM ein eigenes Netzwerkinterface. Auch habe ich noch ein zweites (Redundanz) Netzteil eingebaut.

Hier liegen dann noch Ersatzplatten herum. Alles zusammen schätze ich, dass der Server „nur“ um die 1.200 Euro gekostet hat.

Das Gerät läuft jetzt hier seit mindestens einem halben Jahr durchgängig mit FreeBSD und völlig problemfrei. Nächtliche Backups gehen auf einen weiteren Home-Server sowie auf meinen Online-Server.

Bisher kann ich das System empfehlen, warten wir aber mal ab, wie es in zwei Jahren aussieht.

Neues macOS auf altem, nicht mehr unterstützten Macs: OpenCore Legacy Patcher

Vor einigen Jahren hatte ich mein altes MacBook Pro 16″ aus Ende 2013 meinem Sohn vererbt, da ich mir ein neues MacBookPro 16″ 2019 mit einem i9 gekauft hatte und dieses nicht mehr brauchte. Problem war, dass darauf kein aktuelles macOS mehr installierbar war.

Da ich mir jetzt ein neues MacBook Pro 14″ M5 gekauft habe und mein altes meinem Sohn vererbt hatte, bekam ich das Gerät aus 2013 zurück. Die Frage war jetzt: Was mache ich damit? Ich dachte mir, mit diesem alten macOS kann und will man nichts mehr anfangen und ich könnte mal den OpenCore Legacy Patcher ausprobieren, mit dem sich ein neueres macOS auf einem alten, nicht mehr von Apple unterstützten Mac installieren lässt. Natürlich ohne Support und weitere Online-Patches, aber die bekomme ich beim alten macOS ja auch nicht mehr.

Kurz um: Ich habe den Schritt gewagt und macOS Seqouia 15.7.2 installiert. Ich bin äußerst verwundert, wie gut das Gerät mit dem macOS läuft. Es reagiert schnell und ist voll benutzbar (bisher). Auch die ganze Software, die ich bisher benutzte, funktioniert einwandfrei. Ich bin sehr überrascht. Für bsplw. meine Tochter ist das Gerät damit noch voll nutzbar.

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.

xrdp läuft zur Zeit recht stabil auf FreeBSD

Nur eine kleine Empfehlung. Immer wieder habe ich mir xrdp auf FreeBSD angesehen. Bisher empfand ich es als unbrauchbar, da es alles andere als stabil funktionierte und teils auch nicht startete.

Seit einer Weile funktioniert xrdp hervorragend.

FreeBSD-Desktop Xfce auf xrdp unter FreeBSD 14.2

xrdp stellt eine RDP-Session her. Damit kann man einen entfernten Computer via a nutzen, ähnlich zu RDP unter Windows. Auf dem oberen Bild kann man das sehen. Hier läuft auf einem Host ein FreeBSD 14.2, worunter ein Jail mit FreeBSD 14.2 läuft, auf dem xrdp installiert ist. Darin wird dann ein Xfce ausgeführt. Seit gut drei oder vier Monaten läuft es problemfrei.

Auf macOS nutze ich Jump Desktop, um auf den xrdp-Desktop zuzugreifen. Ich hatte auch Windows App ausprobiert, hier ist aber das Problem, dass ich es nie geschafft habe, ein deutsches Tastaturlayout zu übertragen. Mit Jump Desktop, welches leider kostenpflichtig ist, ist dies problemlos möglich.

Jump Desktop Hauptfenster

Ich kann das bisher sehr empfehlen, wenn man kein FreeBSD auf seinem Computer installieren will, sondern eine zentrale Installation nutzen möchte, auf der sich auch verschiedene Nutzer gleichzeit einloggen möchten.

Gebrauchte Macs an Apple-Account gebunden

Vielleicht hilft das dem ein oder anderen. Ich habe 2023 gebraucht ein MacBook Pro 16″ (BJ 2019) als Zweitgerät auf Kleinanzeigen gekauft. Zwei Jahre lang hatte ich das in Benutzung, jetzt übergab ich es, aufgrund eines neuen Hardwarekaufs, an meinen Sohn. Also Programme, die er nicht braucht, gelöscht, Account angelegt und meinen Account gelöscht. So weit, so gut. Er hatte das Gerät jetzt gut eine Woche in Benutzung und forderte mich gestern, am 13.11.2025 auf, ihm nochmal das WLAN-Passwort zu geben, denn sein Mac sei irgendwie abgestürzt.

Ich sah mir das Gerät direkt einmal an und er hatte recht: Es versuchte, in den Internet-Recovery-Modus zu booten. Mir fiel dann ein, dass beim Erstellen vom Account meines Sohns irgendeine Apple-ID angezeigt wurde, die vom Vorbesitzer war. Anscheinend ist dieser informiert worden, dass da an seinem alten Mac gearbeitet wird. Er hat dann – so wie es aussieht – den Mac aus der Ferne vollständig gelöscht.

Zusätzlich bin ich nicht mehr in der Lage, den Mac überhaupt noch zu nutzen, da dieser via Apple-ID des Vorbesitzers entsperrt werden muss. Ich habe ihm einen Brief geschrieben (ich habe nur noch die Adresse und hoffe, er wohnt da noch) und darum gebeten, den Mac aus seinem Account zu entfernen. Wenn er das nicht macht, habe ich nur rechtliche Möglichkeiten, das zu verlangen, die aber wiederrum mit Aufwand und ggf. Kosten einhergehen. Ansonsten ist das Gerät nicht mehr als ein teurer Briefbeschwerer.

Mein Sohn ist natürlich enttäuscht und traurig. Ich hoffe, dass der Vorbesitzer noch einlenkt und alles wieder gut wird.

Also: Denkt beim Verkauf von Apple-Geräten daran, sie aus euren Accounts freizugeben und wenn ihr solche Geräte kauft, checkt das vorher. Ich denke nicht, dass hier böse Absicht hinter gesteckt hat, weiß es aber nicht. Natürlich sind alle Daten, Programme, usw. unwiderruflich gelöscht. Wohl dem, der ein Backup hat.

PostgreSQL-Installation oder -Update auf FreeBSD läuft auf Fehler: >>could not load locale „sr_YU.ISO8859-5″<<

Nachdem ich PostgreSQL 16 auf Version 17 aktualisieren wollte, trat beim initdb folgender Fehler auf:

root@marge:~ # service postgresql oneinitdb
initdb postgresql
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with this locale configuration:
  locale provider:   libc
  LC_COLLATE:  C
  LC_CTYPE:    C.UTF-8
  LC_MESSAGES: C.UTF-8
  LC_MONETARY: C.UTF-8
  LC_NUMERIC:  C.UTF-8
  LC_TIME:     C.UTF-8
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /server/database/postgres ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default time zone ... UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... 2025-08-25 11:53:19.740 UTC [71247] FATAL:  could not load locale "sr_YU.ISO8859-5.bak"
2025-08-25 11:53:19.740 UTC [71247] STATEMENT:  SELECT pg_import_system_collations('pg_catalog');

child process exited with exit code 1
initdb: removing contents of data directory "/server/database/postgres"

Die einzige Möglichkeit, wie ich das Problem umgehen konnte, war, dass ich die Datei „/usr/share/locale/sr_YU.ISO8859-5“ in ein anderes Verzeichnis verschoben habe und dann initdb erneut ausführte. Danach habe ich die Datei zurückverschoben.

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.