Serie: Programmieren lernen mit KI

Der Samstag, an dem der Build aufhörte zu streiten

Unspektakuläre Aufräumarbeiten, die den Build zum Schweigen brachten.

Der Build hörte auf zu streiten Achtzehn DLLs grün unter Linux, Namespace stabil, Nullzeiger-Sicherheit durchgefegt 18 / 18 DLLs grün -fvisibility=hidden im gesamten GCC-Build /api/v2/ zu /api/raku/ überall Nullzeiger-Prüfungen an der Grenze von stillen Abstürzen zu lauten, begrenzten Fehlern
Kein Screenshot. Nur die Erlaubnis, die nächsten acht Monate lang auszuliefern.

Die Fixes, aus denen kein Screenshot wird, sind diejenigen, die entscheiden, ob deine Runtime überhaupt ausgeliefert werden kann. RakuAI hat eine Woche damit verbracht, die weichen Stellen auszumerzen, damit die nächsten acht Monate Entwicklungsarbeit nicht mehr mit dem Build streiten.

Es gibt eine Kategorie von Arbeit, aus der kein Screenshot entsteht. Sie erzeugt einen Build, der auf Maschinen grün wird, auf denen er vorher rot war. Sie erzeugt einen CI-Lauf, der aufhört, in ein Timeout zu laufen. Sie erzeugt einen Stack-Trace, der gar nicht erst entsteht, weil der Nullzeiger, der ihn verursacht hat, jetzt an der Grenze abgefangen wird. Die Woche, die an diesem Samstag endete, war genau diese Art von Woche.

Achtzehn DLLs grün unter Linux

Die Runtime wird als achtzehn native DLLs ausgeliefert. Bis vor zwei Wochen bauten diese DLLs sauber unter Windows, bauten sauber unter macOS und scheiterten unter Linux an einem Symbol-Sichtbarkeitsfehler. Der Fix landete in PR #1463 am 11. April.

Die Geschichte dahinter ist die unspektakuläre Sorte. GCC macht standardmäßig jedes Symbol in Shared Libraries sichtbar. MSVC macht standardmäßig jedes Symbol unsichtbar, sofern es nicht explizit exportiert wird. Der Runtime-Code war unter der Annahme des MSVC-Standardverhaltens geschrieben, mit __declspec(dllexport) auf den Symbolen, die die DLL-Grenze überschreiten mussten. Unter Linux sind diese Deklarationen wirkungslos, was bedeutet, dass jedes Symbol sichtbar war, was bedeutet, dass der Linker die korrekten DLL-internen Bindungen nicht ermitteln konnte, was bedeutet, dass ein Teil der achtzehn DLLs nicht baute.

Der Fix ist -fvisibility=hidden als Compiler-Flag für die GCC-Builds, mit expliziten Sichtbarkeitsattributen auf den Symbolen, die exportiert werden müssen. Der Diff ist klein. Der Wirkungsradius ist groß. Der Linux-Build ist jetzt ein Ziel erster Klasse. Der Agent, der eine Runtime-Änderung ausliefert, kann sie jetzt automatisch über die CI-Matrix unter Linux testen lassen. Server-Deployments, Container-Images, Cloud-Build-Farmen – all das ist jetzt in Reichweite.

Das ist die Art von Fix, die sich wie eine Steuerabschreibung anfühlt. Er bringt das Produkt nicht voran. Er blockiert es aber auch nicht mehr. Das reicht.

Prompt-Längen-Absicherungen bei jedem LLM-Aufruf

Der andere Commit aus dem April, der es wert ist, festgehalten zu werden, ist die Prompt-Längen-Absicherung im großen Stil. Die Runtime ruft Claude inzwischen an mehreren Stellen auf: das NPC-Verhaltensgehirn, das Dialogmodell, den Weltereignis-Erzähler, den prozeduralen Content-Seeder. Jeder dieser Aufrufer baut einen Prompt durch Aneinanderreihung von Kontext, Anweisungen und aktuellem Zustand. Jeder dieser Aufrufer kann einen Prompt erzeugen, der das Eingabefenster des Modells überschreitet, wenn der Kontext groß genug wird.

Der naive Fehlerfall ist, dass der LLM-Aufruf einen Fehler zurückgibt. Der gefährliche Fehlerfall ist, dass der LLM-Aufruf eine abgeschnittene Antwort zurückgibt, der Aufrufer das Abschneiden nicht erkennt und die Engine auf Basis einer fehlerhaften Antwort handelt. NPCs, die mitten im Satz aufhören. Dialogbäume, die ins Leere verzweigen. Weltereignisse, die mit den falschen Parametern ausgelöst werden.

PR #1465 fügte jedem LLM-Aufrufer eine Absicherung hinzu: die Tokenanzahl vor dem Aufruf schätzen, den Versand verweigern, wenn die Anzahl das Budget überschreitet, und einen strukturierten Fehler ausgeben, auf den der Aufrufer reagieren kann. PR #1466 prüfte die gesamte Codebasis, um sicherzustellen, dass die Absicherung an jeder Aufrufstelle vorhanden ist. PR #1467 fügte Dokumentation hinzu, damit der nächste Aufrufer die Disziplin kennt.

Das ist deshalb wichtig, weil KI-Aufrufe innerhalb einer Runtime keine Ausnahme sind. Sie passen viele Male pro Frame bei Spitzenlast. Ein Bug im Prompt-Aufbau, der einen Prompt um ein paar Token pro Aufruf wachsen lässt, ist bei hundert Frames unsichtbar und bei tausend Frames katastrophal. Die Absicherung macht den Fehlerfall laut und begrenzt statt still und unbegrenzt.

Stabilisierung des API-Namespace

Der dritte Commit ist der leiseste der drei und möglicherweise der folgenreichste. Die Runtime stellt eine REST-API für externe Tools, Dashboards und das SDK bereit. Diese API lebte die letzten sechs Monate unter /api/v2/, weil sie einer früheren, rein internen /api/v1/ nachfolgte.

/api/v2/ ist der falsche Name. Er suggeriert, dass v1 öffentlich war und abgelöst wurde, was nicht stimmt. Er suggeriert außerdem, dass ein v3 unterwegs ist, was ebenfalls nicht stimmt. PR #1464 ging jede Referenz in der Codebasis und jede Referenz in der Dokumentation durch und benannte den Namespace von /api/v2/ in /api/raku/ um. Der Name ist jetzt stabil. Es gibt keine Versionsnummer mehr, über die später verhandelt werden müsste. Abwärtskompatibilität innerhalb des Namespace ist der Vertrag.

Das ist die Art von Umbenennung, die genau einmal passieren muss, und zwar bevor irgendein externer Entwickler Code gegen den Namespace schreibt. Wir haben es rechtzeitig erwischt. Wenn das nächste Mal jemand außerhalb des Teams ein Skript schreibt, das die API anspricht, sieht er /api/raku/ und muss seine Arbeit nicht in sechs Monaten neu machen.

Nullzeiger-Sicherheit als Rundumschlag

Der jüngste Commit auf der Runtime, vor zwei Tagen am 30. April, ist 58c3a806: „fix(build): resolve all build errors, warnings, and null-pointer safety”. Der Ausdruck „Nullzeiger-Sicherheit” leistet in diesem Titel eine Menge Arbeit. Die Änderung dahinter ist ein Rundumschlag durch die Codebasis, der jede Stelle sucht, an der ein Zeiger null sein könnte und ohne Prüfung dereferenziert wurde.

Das Muster, das den Rundumschlag ausgelöst hat, war ein Absturzbericht aus einer der Beispielanwendungen. Ein Textur-Loader bekam einen Nullzeiger für das Asset-Manifest übergeben, dereferenzierte ihn und stürzte ab. Der Fix war eine Prüfung am Einstiegspunkt. Das Audit war die Frage: An wie vielen anderen Stellen in der Codebasis existiert dasselbe Muster.

Die Antwort war mehrere Dutzend. Nicht alle davon waren ausnutzbar. Viele waren Codepfade, die nie durchlaufen wurden, weil der aufrufende Code zufällig nie ein Null übergeben hat. Das ist keine Verteidigung. Die Verteidigung ist die Prüfung an der Grenze.

Der Rundumschlag fügte die Prüfungen hinzu. Er änderte das Verhalten im Erfolgspfad nicht. Er fügte einen Rückgabewert (RAKU_ERR_INVALID_PARAM) und eine Log-Zeile für den Fehlerpfad hinzu. Die Runtime ist jetzt in den Fällen lauter, die früher stille Abstürze waren. Laut ist besser als still. Stille Abstürze in einer AR-Runtime sind die schlimmste Art von Bug.

Warum diese Art von Woche die richtige ist

Die Versuchung, wenn man ein Engine-Projekt im Wochenend-Takt mit einer Flotte von Agenten betreibt, besteht darin, immer weiter neue Features auszuliefern. Jeden Samstagmorgen füllt sich die Warteschlange wieder. Jeden Samstagabend ist der Diff größer als in der Woche davor. Der Takt belohnt Vorwärtsbewegung.

Der Takt toleriert auch technische Schulden. Jeder PR, der in den letzten acht Monaten gemergt wurde, hat eine kleine Annahme über die Build-Umgebung, die API-Oberfläche, das LLM-Eingabefenster oder den Nullsicherheits-Vertrag getroffen. Keine dieser Annahmen war für sich genommen falsch. Zusammen ergaben sie eine Liste weicher Stellen, die irgendwann brechen würden.

Eine Woche, in der die Vorwärtsbewegung angehalten wird, um die weichen Stellen zu reparieren, ist keine verlorene Zeit. Es ist die Woche, nach der die nächsten acht Monate Vorwärtsbewegung passieren können, ohne mit dem Build, der API, der LLM-Eingabe oder dem Nullzeiger zu streiten. Der Streit ist das, was Zeit verschwendet. Der Streit ist das, was die Aufräumarbeiten beendet haben.

Was als Nächstes kommt

Die Agenten sind dieses Wochenende zurück an der Warteschlange. Die nächste Runde von Issues ist die, die ich an diesem Samstagmorgen einreiche. Es sind keine Aufräum-Issues mehr. Es sind Feature-Issues. Der Build wird nicht mehr mit ihnen streiten.

Das ist es, was ein sauberer Build einem verschafft. Keinen Screenshot. Die Erlaubnis, auszuliefern.

Eine Runtime, die sich ihr Vertrauen bei den unspektakulären Dingen verdient

Grün auf jedem Zielsystem, stabile APIs, laute Fehler statt stiller Abstürze. RakuAI ist die Spatial Runtime, gebaut mit der Disziplin, die Produktion verlangt. Sehen Sie, warum Partner darauf aufbauen.

← Alle Beiträge