Handwerk im Zeitalter generierten Codes
Jedes Studio bekommt dieses Jahr dieselbe Frage gestellt: Wenn ein Modell den Code schreiben kann, wofür genau nehmen Sie dann Geld? Unsere Antwort, in Form von Dingen, die unser eigener Build nicht durchgehen lässt.
Auf dieser Seite
Code zu schreiben hat aufgehört, der teure Teil zu sein. Ihn zu lesen ist nie billiger geworden, und diese eine Asymmetrie ist die ganze Geschichte dieses Jahres.
Ein Modell produziert vierhundert Zeilen plausibles TypeScript, während Sie einen Satz zu Ende schreiben. Ihre Fähigkeit, vierhundert Zeilen im Kopf zu behalten und zu fragen was müsste wahr sein, damit das hier falsch ist, ist exakt dieselbe wie 2019. Der Engpass ist auf den einen Teil des Prozesses gewandert, der nicht schneller wird. Jede Debatte über KI-gestütztes Engineering, die ich interessant finde, beginnt dort; die, die mich langweilen, drehen sich insgeheim alle darum, ob Tippen jemals der Punkt war.
Generierter Code scheitert außerdem auf eine besondere und fiese Weise: Er scheitert plausibel. Handgeschriebene Fehler sehen meist wie Fehler aus – eine falsch benannte Variable, ein Zweig, der offensichtlich nie läuft, ein Kommentar, der der Zeile darunter widerspricht. Die Fehler eines Modells kommen korrekt gekleidet. Die Benennung ist gut, die Struktur konventionell, die Kommentare stimmen mit dem Code überein, und die zweite DNS-Antwort wird nie geprüft.
Also haben wir aufgehört zu fragen, wer eine Zeile geschrieben hat, und angefangen zu fragen, was im Build Einspruch erheben würde, wenn die Zeile falsch wäre.
Prüfungen, die ohne mich laufen
Das Orrery propagiert Satellitenbahnen im Browser aus Elementsätzen, die CelesTrak veröffentlicht. In der Entwicklung läuft, bevor auch nur ein Pixel davon gerendert wird, ein Selbsttest. Gibt man ihm einen Elementsatz ISS-Klasse – mittlere Bewegung 15.495 Umläufe pro Tag –, muss eine Umlaufzeit von 92.933 Minuten und eine Geschwindigkeit von 7.660 km/s zurückkommen. Gibt man ihm einen geostationären Satz, muss die Länge über sechs Stunden um 0.010° driften. Diese Zahlen sind nicht unsere. Sie kommen aus der Physik und waren lange vor dieser Datei wahr.
Der Wert dieser Prüfung liegt vollständig in dem, was sie verhindert: eine Satellitenkarte, die überall falsch und nirgends erkennbar falsch ist. Bahnfehler kündigen sich nicht an. Jeder Punkt bewegt sich weiterhin flüssig, der Planet dreht sich, und nichts auf dem Bildschirm verrät, dass der Kepler-Löser bei jemandes Aufräumarbeit eine Iteration verloren hat. Assertions verraten es.
Derselbe Instinkt im Spielmaßstab. Slipstream ist ein Bahnenläufer, dessen Hindernisreihen aus generierten Mustern kommen, und ein Muster, das bei Tempo keine erreichbare Bahn übrig lässt, ist kein schweres Level – es ist ein kaputtes. Dieser Defekt übersteht Playtests hervorragend, weil ein Tester, der stirbt, annimmt, er sei schlecht gewesen. Also werden die Muster beim Laden des Moduls validiert: vorwärts propagieren, welche Bahnen das Gefährt bei Worst-Case-Tempo von 32 Einheiten pro Sekunde physisch erreichen kann, und zusichern, dass jede Reihe mindestens eine davon überlebbar hält. Der Kommentar in dieser Datei ist die gesamte Philosophie in acht Wörtern – fair für den Validator heißt fair für den Spieler.
| Was läuft | Was es zusichert | Warum ein Mensch das nicht abdeckt |
|---|---|---|
| Orrery-Selbsttest der Propagation | Bahn der ISS-Klasse → 92.933 min Umlaufzeit, 7.660 km/s; geostationäre Drift 0.010° über 6 h | Bahnfehler sind mit bloßem Auge unsichtbar – die Karte sieht richtig aus, während sie falsch ist |
| Slipstream-Mustervalidator | Jede Hindernisreihe lässt bei Worst-Case-Tempo eine Bahn erreichbar | Playtests können die unfaire Reihe verfehlen; beweisen, dass es keine gibt, können sie nicht |
| Blueprint-Schema, Grenzen und Sanitizer | 3–14 Knoten, exakt 3 Phasen, keine hängenden Kanten, keine unbekannten Options-IDs | Modellausgabe liest sich flüssig, auch wenn sie strukturell unmöglich ist |
Drei Prüfungen, verdrahtet in Codepfade, die ein Entwickler ohnehin durchläuft. Keine davon interessiert sich dafür, wer den Code geschrieben hat, den sie prüft.
Diese letzte Eigenschaft zählt mehr als die Prüfungen selbst. Eine Verifikation, an die man sich erinnern muss, ist eine Verifikation, die man irgendwann auslässt – meist an dem Tag, an dem man am schnellsten unterwegs ist, und das sind inzwischen die meisten Tage.
Das Modell schlägt vor, die Engine entscheidet
Bei Blueprint mussten wir diese Frage kommerziell statt philosophisch beantworten. Ein Besucher beschreibt ein Produkt, Claude entwirft eine Architektur, die Seite zeichnet sie und hängt einen Preis daran. Die offensichtliche Katastrophe ist ein Sprachmodell, das eine Zahl erfindet, die ein Fremder anschließend zu Recht als Angebot von uns behandelt.
Also steht in diesem Code eine Regel, geschrieben als Satz und nicht als Config-Flag: Das LLM bepreist niemals irgendetwas – es schlägt nur Auswahlen vor, und selbst die werden vorher bereinigt.
In der Praxis wählt das Modell aus einem geschlossenen Katalog: Plattformen, Features, KI-Fähigkeiten, eine Designstufe, eine Anzahl Integrationen. Unbekannte IDs fliegen beim Eintreffen raus. Pflichtfelder mit Einfachauswahl, die leer zurückkamen, werden aus Defaults gefüllt. Danach passiert die Arithmetik an einem Ort, den das Modell nicht erreicht – Basis-Units über die Auswahlen summiert, multipliziert mit Produktkomplexität, QA-Stufe und Launch-Umfang, umgerechnet über ein Unit-Rate-Band; die Laufzeit mit parallel laufenden Plattformen, der Rest zu fünfzehn Units pro Woche, plus zwei Wochen Puffer. Es ist dieselbe deterministische Engine hinter unserer Preisseite, die dieselben Regeln liest. Die Enum-Liste im Ausgabeschema, der Optionskatalog im Prompt und die Allowlist des Sanitizers sind alle aus demselben Regelobjekt abgeleitet und können deshalb nicht still auseinanderdriften.
Das Modell darf bei Architektur fantasievoll sein. In die Nähe einer Zahl, auf die ein Kunde handelt, darf es nicht.
Zwei kleinere Entscheidungen im selben Geist. Die Idee des Besuchers wird in Tags gewickelt und als Daten behandelt, nicht als Anweisungen, denn ein Textfeld auf einer öffentlichen Seite ist nicht vertrauenswürdige Eingabe, so gesprächig es auch aussieht. Und wenn aus einem Blueprint eine Angebotsanfrage wird, wird die Schätzung auf dem Server aus den bereinigten Auswahlen neu berechnet; die Zahlen, die aus dem Browser kamen, werden schlicht ignoriert. Der Preis eines Kunden sollte nie ein Wert sein, der durch einen Ort gelaufen ist, den ein Fremder editieren kann.
Wissen, wogegen man sich nicht abgesichert hat
Ein Server, der von Fremden getippte URLs abruft, ist eine geladene Waffe, die auf das eigene Netz zeigt – und genau das ist das Reliquary: Link einfügen, unser Server holt die Seite, parst sie und gibt ein Exponat zurück. Die lohnenden Angriffsziele stehen nicht im öffentlichen Internet. Unter 169.254.169.254 parken Cloud-Anbieter ihre Instanz-Metadaten; in den privaten Bereichen liegt der Rest Ihrer Infrastruktur. Über diesen Build haben wir im Juni geschrieben; die Absicherung ist der unglamouröseste Teil davon und der, den ich zuerst verteidigen würde.
Nur http und https, nur Port 80 und 443, keine im Userinfo-Feld eingeschmuggelten Zugangsdaten. Eine Denylist über private, reservierte, Link-local- und Carrier-Grade-NAT-Bereiche. DNS wird mit allen zurückgegebenen Adressen aufgelöst, und jede einzelne muss öffentlich sein, denn nur die erste Antwort zu validieren ist der klassische Beinahe-Treffer. Jeder Redirect-Hop wird von Grund auf neu geparst und neu validiert – eine öffentliche URL, die auf eine Loopback-Adresse weiterleitet, ist der ganze Angriff. IPv6-Präfixe werden vor dem punktierten Vierergruppen-Ende klassifiziert, in dieser Reihenfolge, denn umgekehrt würde eine gemappte Adresse mit öffentlich aussehendem Ende durchgewunken.
Und dann der Teil, auf den ich stolzer bin. Ein Kommentar in dieser Datei benennt den Angriff, den wir nicht gestoppt haben: DNS-Rebinding. Zwischen dem Moment, in dem wir einen Hostnamen validieren, und dem Moment, in dem der Socket verbindet, kann die Antwort zu etwas Privatem wechseln. Das sauber zu schließen hieße, die Verbindung an die bereits validierte Adresse zu binden, also einen eigenen HTTP-Dispatcher und eine neue Abhängigkeitsfläche – und das war es uns für ein Feature, das Auktionsangebote abruft, nicht wert. Das Urteil kann falsch sein. Es ist immerhin aufgeschrieben, sodass die nächste Person, die die Datei liest, die Begründung erbt statt des Eindrucks, es sei alles abgedeckt.
Das ist der Teil, den kein Generator für einen erledigt. Ein Modell schreibt bereitwillig einen URL-Validator, und vielleicht sogar einen guten. Was es nicht kann, ist zu entscheiden, welches Restrisiko Ihr Unternehmen zu tragen bereit ist, und das dann laut an einer Stelle zu sagen, an der man ihm widersprechen kann. Urteilsvermögen ist nicht die Fähigkeit, die abgesicherte Version zu produzieren. Es ist zu wissen, welche Form das Ungesicherte hat, es bewusst zu wählen und den eigenen Namen unter die Wahl zu setzen.
Zwei weitere Dinge sind hier hartnäckig menschlich geblieben. Das eine ist die Invariante – der Satz, den ein System nicht verletzen darf –, weil Invarianten kodieren, was einem wichtig ist, und nichts in einem Trainingskorpus weiß, was Ihnen wichtig ist. Das andere ist die Entscheidung, was man nicht baut. Das Reliquary hält jedes Exponat im lokalen Speicher Ihres Browsers und gar nichts auf unseren Servern. Das hat echte Features gekostet: kein Teilen, kein Sync zwischen Laptop und Telefon, eine harte Grenze von sechzig Exponaten und keinerlei Ahnung, was irgendwer mit dem Ding anstellt. Kein System, das auf dem letzten Jahrzehnt Software trainiert wurde, hätte vorgeschlagen, die Daten abzulehnen. Es war eine Geschmacksentscheidung, und Geschmack ist genau das, wozu Zaubern als Standard geschrieben wurde.
Womit ich beim Wort des Jahres wäre. Slop ist keine Eigenschaft von KI, sondern eine Eigenschaft des Reviews. Code, über den niemand sagen kann was müsste wahr sein, damit das hier falsch ist, ist Slop – egal ob er aus einem Modell kam, von einem Dienstleister oder von mir um ein Uhr nachts. Unsere Branche hat davon Ozeane ausgeliefert, lange bevor irgendetwas eine Funktion vervollständigen konnte. Die Modelle haben dieses Versagen nicht erfunden. Sie haben die Kosten seiner Produktion gesenkt, was ein Argument dafür ist, Reviews ernster zu nehmen, nicht weniger ernst.
Das Unbequeme an dieser Position ist, dass sie schlecht skaliert, und ich glaube, die Leute, die die Zukunft verkaufen, wissen das. Generieren ist fast gratis und wird noch gratiser. Verifizieren ist Arbeit, sie bleibt hartnäckig linear, und sie ist das, wofür wir tatsächlich Geld nehmen. Wenn ein Kunde 2026 fragt, wofür er einen Entwickler bezahlt, lautet die ehrliche Antwort: nicht fürs Tippen. Für die Verweigerungen. Für die Zahl, die aus dem Rechner kommen musste, die Reihe, die überlebbar bleiben musste, das Risiko, das in einem Kommentar benannt werden musste, wo jemand ihm widersprechen kann.