Wenn Software-Updates mehr kaputt machen als sie reparieren: Ein persönlicher Erfahrungsbericht mit GIMP 3.0.4

Mit GIMP 3.0.4 wurde aus einem stabilen Werkzeug ein Bug-Monster: blockierte Shortcuts, verwaiste Dialoge, kaputte Export- und Font-Funktionen. Der Release wirkt wie KI-generierter Code ohne echte Tests – ein Beispiel dafür, dass KI, CI/CD und Automatisierung keine menschliche Qualitätskontrolle ersetzen können.

Vor wenigen Minuten habe ich über 1 Stunde 30 Minuten damit verloren, der GIMP-Community auf GitLab mehrere Bugs zu melden. Normalerweise mache ich das gerne, wenn ich auf Fehler stoße – schließlich lebt Open-Source-Software davon, dass Nutzer:innen Rückmeldung geben. Aber diesmal war es anders: Die Anzahl, Art und Systematik der Bugs in GIMP 3.0.4 geben mir ein sehr ungutes Gefühl.

Vor dem Update lief alles perfekt

Mit der Version vor 3.0.4 war ich rundum zufrieden. Alles funktionierte reibungslos, die Bedienung war konsistent, und es gab keine Stolperfallen im Workflow. Eigentlich hätte es kein Update gebraucht. Doch aufgrund des Splashscreens mit der neuen Versionsbenachrichtigung habe ich das Update trotzdem eingespielt – und plötzlich ging nichts mehr wie zuvor.

Ein Update voller Stolperfallen

Die Liste der Fehler ist lang, hier nur ein Auszug:

  • GIMP hat plötzlich einen globalen Keyboard Hook, der selbst außerhalb von GIMP alle Shortcuts wie Ctrl+C, Ctrl+V oder Ctrl+S blockiert (VS Code, VS 2022, Android Studio, Linqpad, Notepad++ usw.)
  • Mehrere Dialogfenster verlieren ihren Kontext und werden zu „verwaisten“ Fenstern, die sich unkontrolliert vervielfachen.
  • Im Export-Dialog wächst Text nicht nach rechts, sondern nach links aus dem Fenster hinaus – und die restliche UI reagiert nicht mehr.
  • Die Font-Auswahl und Größenänderung verhält sich grotesk: Statt den Wert im Feld zu ändern, landet meine Eingabe im eigentlichen Bildtext. (Mein Verdacht: die Beschreibung für Code-generierender KI war nicht eindeutig und missverständlich und dadurch hat der KI die Textfelder von Font-Eigenschaften mit dem Text im Bild (Layer) verwechselt. Das passiert oft und ist typisch für Coder mit sehr wenig Erfahrung in Coding und Software-Engineering)
  • Nach dem Rotieren oder Einfärben von Ebenen entstehen nach dem Zuschneiden plötzlich verschobene Layer-Kopien.

Das sind keine Kleinigkeiten, sondern fundamentale Fehler, die das Arbeiten massiv behindern.

Mein Verdacht: KI-Code ohne echte Tests

Die Häufung und Art dieser Bugs weckt bei mir einen starken Verdacht:

  • Es wirkt, als wären viele Code-Änderungen automatisiert entstanden, möglicherweise mit Hilfe von KI-generiertem Code. Z.B. durch unzureichende, nicht-eindeutige und missverständliche Beschreibung für KI, wurde ein Code generiert der zwar mit kein(e) einzige(r) Compiler-Fehler/Warnung übersetzt werden (und laufen) kann, aber sich bei der Nutzung völlig falsch verhält.
  • Darauf aufbauend hat man sich blind auf CI/CD-Pipelines verlassen, ohne dass echte Menschen die Software in Alltagsszenarien getestet haben.
  • Das Ergebnis: ein Release, das wie ein Krebs-Geschwür über die Community ausgerollt wurde.

Natürlich ist das nur mein Eindruck als Nutzer (mit 17+ Jahren Erfahrung in Software-Engineering und Entwicklung). Aber die Systematik der Fehler lässt kaum an Einzelfälle glauben.

KI ersetzt weder Menschen noch Tests durch Menschen

Ich möchte hier einen Punkt betonen: KI kann helfen, Code schneller zu schreiben oder Vorschläge zu liefern. Aber ohne menschliche Qualitätskontrolle, manuelles Testen und kritisches Hinterfragen wird jede Software zur Zumutung. Continuous Delivery (CD) und Continuous Integration (CI) sind mächtige Werkzeuge – aber sie ersetzen nicht das Verständnis für Usability und händisches Durchklicken, das echte Fehler sichtbar macht.

Fazit

Software-Updates sollten Verbesserungen bringen, nicht Regressionen. Im Fall von GIMP 3.0.4 war das Gegenteil der Fall. Ich musste auf die vorherige Version zurückrollen, weil grundlegende Dinge nicht mehr funktionierten.

Mein Appell an alle Entwickler:innen (egal ob Open Source oder kommerziell):

  • Nutzt KI, aber lasst sie nicht allein entscheiden.
  • Vertraut auf CI/CD, aber ergänzt sie mit echten Tests durch Menschen.
  • Denkt an eure Nutzer:innen – sie verlieren sonst Stunden ihrer Lebenszeit für Dinge, die gar nicht hätten passieren dürfen.

Buzzword-Bingo: Der überteuerte „Kaffeevollautomat mit künstlicher Intelligenz“ und die „natürliche Dummheit“

„Seit jeher verkaufen Geschäftsleute das, von dem Techniker schon immer geträumt haben.“
Anonym

Letztes Jahr, noch weit vor der Weihnachtszeit, bevor der Weihnachtsstress überhaupt erwähnt wurde, schickte mir mein Bruder einen Link zu einem „Kaffeevollautomaten mit künstlicher Intelligenz„.

Ein Kaffeeautomat mit KI? Wozu?“ fragte ich mich, und ich begann gleichzeitig neugierig und aufmerksam, die Texte zu dieser überteuerten Kaffeemaschine zu lesen. Die Webseite eines Online-Shops pries den Hersteller für seine angeblichen Innovationen in der KI-Technologie und den neuen Schritten in der Digitalisierung… und da dachte ich nur „OMG! Wieder einmal hat die Marketingabteilung oder ein einfallsloser Manager zu Marketing-Gimmicks gegriffen und Schlangenöl als Neuheit verkauft…

Ich war verärgert! Wie kann man nur seine Kunden für dumm verkaufen und sie derart belügen?

Statt mich nur zu ärgern, lenkte ich meinen Ärger um und entwickelte schnell eine einfache Webseite, die eine solche „KI-Kaffeemaschine“ simuliert. Dazu fügte ich eine Konsole mit kurzen Erläuterungen hinzu, was gemacht wird und warum – damit auch diejenigen, die nicht aus der IT-Welt stammen, es verstehen können.

Ich bin gespannt, wann die Taschenlampe mit Big Data, Blockchain, NFT und KI, die von einem Kernfusionsreaktor angetrieben wird, anstelle von Batterien/Akkus, angepriesen wird.

Wer wissen möchte, um welchen Hersteller es sich handelt, kann im Web nach „Kaffeemaschine mit künstlicher Intelligenz“ suchen. Im Simulator befindet sich auch ein Hinweis.


Wie drei Zeilen eines gewöhnlichen Algorithmus die Illusion von künstlicher Intelligenz bei überteuerten Geräten zerstören:

1. Zuerst werden zum Beispiel 6 verschiedene Kaffeesorten festgelegt. Für jede dieser Kaffeesorten wird ein Zähler für die jeweiligen 6 Tasten festgelegt. Es ist auch möglich, mehr Tasten und/oder Kaffeesorten festzulegen:

const coffeeList =
    [
        { id: 0, name: "Americano", count: 0 },
        { id: 1, name: "Mocca", count: 0 },
        { id: 2, name: "Cappuccino", count: 0 },
        { id: 3, name: "Latte", count: 0 },
        { id: 4, name: "Türkisch", count: 0 },
        { id: 5, name: "Frappé", count: 0 }
    ];

2. Danach wird für jede Kaffeesorte ein Knopf erstellt, der auch als Button oder Schaltfläche bezeichnet wird. Dem Mausklick-Mechanismus dieses Knopfes wird eine Methode zugewiesen, die den Zähler, auch als Counter bezeichnet, um eins erhöht und diesen Wert dann wieder in die Zähler-Variablen schreibt. Anschließend wird die Methode aufgerufen, die die angezeigten Knöpfe aktualisiert, um die Ansicht zu erneuern:

function increment(id) {
    // Finde das Kaffee-Objekt durch die ID (welche von der Taste geliefert wurde):
    const coffee = coffeeList.find((x) => x.id == id);

    // Zähler für den Kaffee/Button inkrementieren:
    coffee.count++;

    // Inkrementieren des Zählers und das Auffrischen der Anzeige:
    updateView(coffee);
}

3. In der Methode „updateView(coffee)“ wird eine weitere Methode aufgerufen, um zu überprüfen, ob eine Aktualisierung der Anzeige (der Knöpfe/Schaltflächen) erforderlich ist oder nicht. Diese Aktualisierung erfolgt nur dann, wenn sie notwendig ist.

function hasToUpdateView() {
    for (i = 0; i < 6; i++) {
        if (i < 5)
            if (coffeeList[i].count < coffeeList[i + 1].count) {
                return true;
            }
    }
    // Nein! Keine Änderung ==> Auffrischen nicht notwendig.
    return false;
}

4. Anschließend wird in der Methode „updateView(coffee)“ fortgefahren. Wenn die Antwort der Methode „hasToUpdateView()“ positiv ist, das bedeutet, wenn sie „true“ zurückgibt, dann werden die Knöpfe so lange nach links verschoben, bis der Wert des linken Knopfes höher oder gleich ist wie der Wert des ausgewählten Knopfes.

function updateView(coffee) {
    const btn = document.getElementById("btn" + coffee.id).innerText = coffee.name + "\n" + coffee.count + "x";
    if (hasToUpdateView()) {
        sortByCount();
        createButtons();
    }
}

Der vollständige Code hier nochmal zusammenhängend:

/*******************************************************
 *           J̳ava U̳nique R̳uthless A̳utomat 0.8.15       *
 *      Kaffevollautomat mit Künstlicher Intelligenz   *
 *       Copyright (C)2024 by Pedram GANJEH-HADIDI     *
 *******************************************************/
var shell = "***************************************\n* J̳ava U̳nique R̳uthless A̳utomat 0.8.15 *\n***************************************\n";
const ruler = "---------------------------------------";
var clickCounter = 0;
const coffeeList =
    [
        { id: 0, name: "Americano", count: 0 },
        { id: 1, name: "Mocca", count: 0 },
        { id: 2, name: "Cappuccino", count: 0 },
        { id: 3, name: "Latte", count: 0 },
        { id: 4, name: "Türkisch", count: 0 },
        { id: 5, name: "Frappé", count: 0 }
    ];

window.addEventListener('load', function () {
    const textArea = document.getElementById("txaShell");
    textArea.innerHTML = shell;
    createButtons();
    const w = window.screen.availWidth;
    const h = window.screen.availHeight;
    const e = document.getElementById("txaShell");
    e.setAttribute("width", w);
    e.setAttribute("height", h / 2);
});

window.addEventListener('resize', function () {
    createButtons();
});

function increment(id) {
    const coffee = coffeeList.find((x) => x.id == id);
    writeLog(`${++clickCounter}. T͟a͟s͟t͟e͟n͟d͟r͟u͟c͟k͟: Sie habe die Taste für\n${coffee.name} Kaffee geklickt. Nun wird die Anzahl dieser Taste hochgezählt!\n&#10233; ${coffee.count} + 1 = ${++coffee.count}`);
    updateView(coffee);
}

function updateView(coffee) {
    const btn = document.getElementById("btn" + coffee.id).innerText = coffee.name + "\n" + coffee.count + "x";
    if (hasToUpdateView()) {
        sortByCount();
        createButtons();
    }
}

function writeLog(str) {
    shell += str + "\n";
    const textArea = document.getElementById("txaShell");
    textArea.innerHTML = shell;
    textArea.scrollTop = textArea.scrollHeight;
}

function writeLine() {
    writeLog(ruler);
}

function hasToUpdateView() {
    for (i = 0; i < 6; i++) {
        if (i < 5)
            if (coffeeList[i].count < coffeeList[i + 1].count) {
                writeLog(`V͟e͟r͟g͟l͟e͟i͟c͟h͟e: ${coffeeList[i].name}: ${coffeeList[i].count} ≧ ${coffeeList[i + 1].name}: ${coffeeList[i + 1].count} ❌\nDa Links nicht größer oder gleich Rechts ist\n&#10233; Tasten neu ordnen!`);
                writeLog(`T͟a͟s͟t͟e͟n͟p͟o͟s͟i͟t͟i͟o͟n͟e͟n͟ a͟k͟t͟u͟a͟l͟i͟s͟i͟e͟r͟e͟n͟: Die Taste\nfür ${coffeeList[i + 1].name} so oft wie notwendig nach Links verschieben...`);
                writeLine();
                return true;
            }
    }
    writeLog("V͟e͟r͟g͟l͟e͟i͟c͟h͟e: Die Anzahl der Tastendrücke Links ist überall größer oder gleich der Taste Rechts davon ⭐\n&#10233; Keine Änderung notwendig.");
    writeLine();
    return false;
}

function sortByCount() {
    coffeeList.sort((a, b) => (a.count < b.count) ? 1 : -1);
}

function createButtons() {
    document.getElementById("coffeeButtons").innerHTML = addButtons();
}

function addButtons() {
    let str = "<table><tr>";
    for (i = 0; i < 3; i++) str += getButtonText(coffeeList[i]);
    str += "</tr>\n<tr>";
    for (i = 3; i < 6; i++) str += getButtonText(coffeeList[i]);
    return str + "</tr></table>";
}

function getButtonText(coffee) {
    return '<td><button id="btn' + coffee.id + '" type="button" class="coffeeButton" onclick="increment(' + coffee.id + ')" title="Klick here f&uuml;r ein Kaffee aus &quot;">' + coffee.name + "<br>" + coffee.count + "x</button></td>";
}