Das solid-ml Experiment geht zu Ende. Nicht mit einem dramatischen Scheitern, sondern mit der stillen Erkenntnis, dass es einfach zu viel Aufwand ist.
Das Framework funktioniert durchaus. Die Tests laufen durch. SSR rendert. Hydration funktioniert halbwegs. Aber jedes Mal, wenn ich mich hinsetze, um tatsächlich etwas damit zu machen, laufen LLM-Agenten in DX-Probleme, die nicht da sein sollten. Und diese Probleme zu beheben, ohne dass LLMs die solid-ml Codebase in ein komplettes Chaos verwandeln, hat sich als viel zu aufwendig und viel zu komplex herausgestellt. Ich hatte einfach nicht die Zeit, mich selbst richtig in die Codebase einzuarbeiten und trage jetzt die Konsequenzen davon.
Was schiefgelaufen ist
Ich glaube, ich habe ein paar falsche Abzweigungen genommen.
Die größte: das Ziel auf "volles SSR mit Hydration" zu setzen. Das hat eine Menge Komplexität eingeführt - Hydration-Marker, isomorphe Modul-Interfaces, das Html_intf.S Shared Interface zwischen Server- und Browser-Runtimes, Template-Kompilierung die auf beiden Seiten übereinstimmenden Output produzieren muss. Jedes davon ist ein schwieriges Problem für sich. Sie zusammen in einer LLM-gesteuerten Codebase zu stapeln hat das Ganze auf eine Weise fragil gemacht, die schwer zu debuggen sind.
SSR mit Hydration ist die Art von Feature, bei der Korrektheit wichtig ist. Off-by-one in Marker-Indizes. Whitespace-Unterschiede zwischen Server- und Client-Rendering. Event-Delegation-Timing während der Hydration. Das sind die Bugs, die nicht in Unit-Tests auftauchen, aber echte Anwendungen kaputt machen. Und wenn der Code von KI-Agenten geschrieben wurde, ist es schwer, diese Bugs bis zur Ursache zurückzuverfolgen, weil ich nicht immer im Detail mitverfolgt habe, was gerade passiert.
Was ich eigentlich wollte
Auf jeden Fall wurde mir klar, dass das, was ich wollte, anders war als das, was ich die Agenten habe bauen lassen.
Ich wollte etwas wie Elm, aber mit OCaml-Typen und genug Syntax-Kompatibilität, um Validierungslogik zwischen Frontend und Backend zu teilen. Zu JavaScript kompiliert wie SolidJS - kleine Bundles, feingranulare Reaktivität, kein Virtual-DOM-Overhead.
Im Grunde ein client-seitiges reaktives Framework, verpackt in einer typsicheren OCaml-Schicht, die effizientes JavaScript produziert. Mit SSR allerdings. Hydration sollte meiner Meinung nach vermieden werden, zugunsten von statischen, nicht-interaktiven Seiten mit Platzhaltern, die interaktiv werden, wenn das JavaScript lädt.
Wenn ich dort angefangen hätte, wäre das Projekt ein völlig anderes gewesen.
Was funktioniert hat
MLX als Template-Sprache für OCaml ist ausgezeichnet. Komponenten in .mlx-Dateien zu schreiben fühlt sich natürlich an, wenn man JSX kennt, und der Compiler fängt nicht-übereinstimmende Tags ab. Das ist der Teil des Stacks, den ich behalten würde.
Der KI-gesteuerte Entwicklungsprozess selbst war das eigentliche Experiment, und er hat mir viel darüber beigebracht, wo LLMs ein Projekt tragen können und wo nicht (noch nicht?). Sie sind großartig beim initialen Momentum - ein funktionierender Prototyp mit Tests in Tagen statt Wochen. Sie scheitern an der architektonischen Konsistenz, die man braucht, sobald eine Codebase eine gewisse Größe erreicht. Der reaktive Kern wurde in den ersten zwei Tagen zweimal neu geschrieben, weil die Agenten die SolidJS-Architektur beim ersten Versuch nicht richtig umgesetzt haben.
Was kommt als Nächstes
Ich gebe die Idee von OCaml im Frontend nicht auf. Ich akzeptiere nur, dass solid-ml als Full-Stack-Framework mit SSR nicht das richtige Vehikel dafür ist. Zumindest nicht so, wie solid-ml gerade ist.
Es fühlt sich auch so an, als bräuchte ich solid-ml nicht wirklich. Wenn ich LLM-Agenten direkt SolidJS schreiben lasse - reines TypeScript, kein OCaml - ist die Erfahrung in Ordnung. Die Agenten produzieren brauchbares SolidJS, das Ökosystem ist ausgereift, und ich bekomme SSR geschenkt.
Für Projekte, bei denen ich OCamls Typsicherheit im Frontend will, gibt es leichtgewichtigere Ansätze, die nicht erfordern, ein ganzes Framework zu portieren. Außerdem gibt es noch einen Haufen OCaml-Frameworks und Tools, die ich noch nicht ausprobiert habe und ausprobieren sollte, bevor ich mich entscheide. Eliom bietet Full-Stack OCaml mit eingebautem SSR. Bonsai und Incr_dom sind Jane Streets Ansätze für Web-UIs. Und es gibt immer die Möglichkeit, Melange direkt mit ReasonReact-Bindings zu verwenden.
Das solid-ml Repository bleibt öffentlich. Der Code ist da, die Tests (wie aussagekräftig oder nichtssagend sie auch sein mögen) bestehen.
Aber ich werde nicht weiter Zeit in solid-ml investieren um es funktionsfähig zu machen. Denn ich muss auch irgendwann Geld verdienen.
Wo LLM-gesteuerte OCaml-Entwicklung sich tatsächlich auszahlt, sind allerdings API-Bindings. ocaml-social-sdk ist eine Bibliothek für den Zugriff auf Social Media APIs die ich tatsächlich benutze. Daher wird sie auch besser. Genauso ocaml-stripe und ocaml-oauth2-clients. API-Bindings sind so oder so Slop - und sie öffentlich zu teilen, um auf gemeinsamen Grundlagen aufzubauen, bedeutet, dass wir kampferprobte API-Clients für alle haben werden. Und das Beste daran: LLMs sind in der Lage, API Clients in Continuation-Passing-Style zu schreiben, sodass es einfach ist, alle Concurrency-Runtimes (lwt, eio, Async, miou, etc) zu unterstützen.
solid-ml ist ein SolidJS-inspiriertes Framework für OCaml. Gebaut mit Melange und MLX.
