Warum Compilertheorie noch zählt, wenn die KI den Code für Sie schreibt
KI erzeugt in Minuten einen Parser, sagt Ihnen aber nicht, wann Sie einen brauchen. Wie kleine Sprachen und deterministische Regeln Prompts schlagen und warum Sie der Engpass sind.
Eine KI schreibt auf Anfrage in wenigen Minuten einen Compiler. Ist Compilertheorie als Fach damit erledigt? Keineswegs. Das Bauen war nie der schwierige Teil. Schwierig ist zu merken, dass man einen braucht, und das sagt einem kein Modell.
Ein alter Streit mit neuer Ausrede
Universitäten streichen Compilerkurse schon länger aus ihren Lehrplänen. Das Argument dagegen klingt vernünftig: Einen Compiler zu schreiben ist Spezialwissen, das nur in wenigen Jobs gebraucht wird, also wird die Mehrheit der Programmierer in ihrer ganzen Laufbahn nie einen schreiben.
Das Gegenargument lautet: Einen C- oder JavaScript-Compiler zu schreiben ist das eine, alles, was Compilertheorie ermöglicht, ist etwas anderes. Wer sie nur für den Bau von Programmiersprachen nützlich hält, unterschätzt sie. Dieselben Ideen stecken hinter anspruchsvoller Textverarbeitung, und sie machen Code außerdem einfacher und lesbarer.
Jetzt hat die KI den Skeptikern neue Munition geliefert. Ein Modell kennt die ganze Theorie und erzeugt den Parsing-Code für einen, wozu also etwas davon lernen? Ist das ein echtes Argument oder nur eine Ausrede?
Anwendungen, nach denen niemand fragt
Nehmen wir eine kleine Taschenrechner-Übung für Studierende, bei der die Tests vorgegeben sind und die Lernenden das Verhalten implementieren. Es gibt Dutzende Fälle, und jeder Test sieht ungefähr so aus:
test('Simple addition', () => {
const calculator = new Calculator();
calculator.press(4);
calculator.addition();
calculator.press(2);
calculator.equals();
expect(calculator.getDisplay()).equals('6');
});
Für sich genommen liest sich das gut, aber es ist mühsam zu verfolgen. Mal vierzig, und man verliert den Überblick, was abgedeckt ist und was nicht. Mit etwas Compiler-Denken schreibt man einen einzigen Helfer, der den Nutzer spielt und alles prüft, und ein Test schrumpft auf eine Zeile:
test('Simple addition', () => {
runCase('4 + 2 = [6]');
});
Das ist deutlich lesbarer. Und es geht noch weiter. Statt vierzig Testblöcken kann man eine Tabelle schreiben, in der jede Zeile ein Fall ist, mit der erwarteten Anzeige in Klammern und einem Kommentar dazu, was der Fall belegt:
test.each([
'[0] # display starts at zero',
'1 [1] # pressing a digit shows it',
'12 [12] # digits concatenate',
'4 + 2 = [6] # a basic sum',
'5 × 3 = [15] # product works the same',
'7 - 9 = [-2] # negative results',
'9 ÷ 0 = [Error] # division by zero errors',
'2 + 3 × 4 = [14] # respects precedence',
])('%s', runCase);
Einige Fälle sind hier gekürzt, aber der Unterschied ist sichtbar. Stellen Sie sich vor, Sie müssten dieselbe Abdeckung Fall für Fall ausgeschrieben reviewen.
Ein weiterer Bereich, in dem sich das auszahlt, ist das Verdrahten von Abhängigkeiten zwischen Modulen. Im Code würde man etwa so etwas schreiben:
const system = new System();
system.createModule('A');
system.createModule('B');
system.createModule('C');
system.createModule('D');
system.connect('A', 'B');
system.connect('A', 'C');
system.connect('B', 'C');
system.connect('C', 'D');
Mit ein klein wenig Sprachdesign schreibt man stattdessen das hier:
const system = compileSystem(`
A -> B -> C -> D
A -> C
`);
Bei der ersten Version braucht man oft Stift und Papier, um den Graphen im Kopf zu rekonstruieren. Bei der zweiten sieht man den Graphen einfach. Die Implementierung ist fast peinlich simpel: in Zeilen aufteilen, dann jede Zeile am Pfeil aufteilen.
Es gibt noch viele weitere Beispiele, und manchmal muss man den Compiler nicht einmal selbst schreiben, weil es schon einen gibt. Viel Konfiguration kann einfach YAML sein. Ein Entity-Component-System, wie es in Spielen üblich ist, wächst und passt sich leicht an, wenn Entitäten so beschrieben werden:
goblin:
extends: enemy
health: 30
damage: 5
ai: aggressive
drops: [gold, dagger]
resistances: [poison]
Bevor wir weitermachen, fragen Sie sich, wie viele dieser Ideen ein Modell von sich aus vorschlägt, wenn Sie um Code bitten. In der Praxis fast keine.
KI macht es noch nötiger
Man könnte argumentieren, KI sei der ultimative Compiler: Sie versteht fast jede Sprache und kann sie interpretieren. Es gibt nur einen Haken. Sie liegt nicht immer richtig. Das ist eines der größten Probleme für alle, die repetitive Arbeit mit KI automatisieren, denn die Ausgabe ist nicht deterministisch. Heute erledigt sie die Aufgabe gut, morgen richtet sie Chaos an.
Man arbeitet also zwischen zwei sehr verschiedenen Arten von Sprache. Natürliche Sprache ist, wie man mit der KI redet. Code ist, womit man die KI steuert oder ihr sogar hilft. Muss der Code-Teil geändert werden, bittet man entweder die KI darum oder studiert, was sie geschrieben hat.
Das muss nicht so sein. Schauen Sie sich Gherkin an. Es liest sich wie natürliches Englisch, aber jeder Satz ist einer vorprogrammierten Aktion zugeordnet, die genau eine Sache tut. Es wurde fürs Testen gebaut und damit Menschen nachlesen können, wie sich ein System verhält. Weil hinter jeder Formulierung echter Code steht, ist es deterministisch und läuft jedes Mal gleich ab:
Scenario: User withdraws cash from ATM
Given john's balance is $200
When john withdraws $100 from the ATM
Then john's balance should be $100
So eine Sprache zum Anweisen einer KI wäre großartig, aber es gibt keinen Grund, bei Testsyntax stehen zu bleiben. Gherkin dient Tests, und Tests sind für jedes Unternehmen wichtig, doch Unternehmen brauchen mehr als Tests. Regeln lassen sich in einer Form schreiben, die näher an der eigenen Domäne liegt oder einfacher ist als Code. Geschäftsregeln, Leitplanken und lebendige schriftliche Spezifikationen gehören alle dazu, jeweils auf die eigenen Bedürfnisse zugeschnitten. Sie sind auf einen Blick lesbar, leicht zu ändern und bleiben deterministisch.
Betrachten Sie es als dritte Sprache: kein Code, keine natürliche Sprache, aber deterministisch. Sie könnte so aussehen:
rule "refund window":
when order.status is "delivered"
and days_since(order.delivered_at) <= 30
then allow refund
else deny "refund window expired"
guardrail "no medical advice":
if response mentions [diagnosis, dosage, prescription]
then block and escalate to human
Die KI würde die Maschinerie bauen und pflegen, die das ausführt, und könnte Ihnen sogar helfen, die Sprache zu entwerfen, die Ihr Problem braucht. Aber noch einmal: Wie oft bietet sie das von sich aus an?
Sie sind der Engpass
Damit sind wir wieder bei der These. Wie viele dieser Optionen schlägt ein Modell von sich aus vor? Meist keine.
KI hat erstaunliche Fähigkeiten und eine reiche Theorie im Rücken, und nichts davon hilft, wenn man nicht weiß, wie man es einsetzt. Eine seltsame Lage. Selbst wenn das Modell die Theorie kennt, kann man sie nicht nutzen, solange man nicht weiß, dass es sie gibt.
Die gute Nachricht: Die ganze Theorie braucht man nicht mehr. Es reicht zu wissen, was möglich ist und wie es helfen könnte. Die Fähigkeit hat sich vom Wissen, wie man etwas macht, zum Wissen, worum man bitten muss, verschoben. Sobald man das weiß, kann man auf alles zurückgreifen, was das Modell weiß.
Einen Prompt entfernt
Die Idee der Universitäten, Compilerkurse zu streichen, bleibt also eine schlechte. Vielleicht muss die Tiefe nicht mehr dieselbe sein wie früher, aber Studierende müssen wissen, dass es das Fach gibt. Seltsamerweise hängt gerade die KI davon ab, dass man weiß, was möglich ist.
Dasselbe gilt für viele andere Bereiche. Ein Modell enthält enorm viel Wissen, vieles davon in seinem latenten Raum vergraben, wo es auf den richtigen Prompt wartet, der es weckt. All das ist nur einen Prompt entfernt, vorausgesetzt, man weiß, wonach man fragen muss.