1 Punkte von GN⁺ 21 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Ein persönliches Projektexperiment, bei dem ein Rust-Webapp-Crate nach Ruby on Rails portiert wird; Ziel sind 14.943 Zeilen Code auf Basis von Tera und Axum
  • Die bisherige Rust-Struktur erfordert für Tests Playwright-E2E, isolierte Datenbank-Namespaces, Mocking-Services und sogar ein internes API-Crate, wodurch der Testaufwand hoch ist
  • Im LLM-Vergleich erzielt Rails mit insgesamt 710 Punkten mehr als Rust/Axum/Diesel mit 480; als Stärken gelten Entwicklungsgeschwindigkeit und einfache Unit-Tests
  • Die One-Shot-Konvertierung mit Local Qwen3.6 dauerte etwa 30 Minuten, der Ruby-Code schrumpfte auf 3.322 Zeilen, wurde aber noch nicht durch Ausführung verifiziert
  • Rails punktet mit vielen Grundfunktionen und kompakten Tests; die fehlende Typsicherheit von Ruby lässt sich mit Sorbet oder agentenbasierter Typ-Ergänzung abmildern

Hintergrund des Umstellungsexperiments

  • Ein Rust-Webapp-Crate als Teil eines privaten Projekts wurde als Kandidat für die Migration zu Ruby on Rails ausgewählt
    • Das Gesamtprojekt umfasst etwa 30.000 Zeilen, und das Ziel-Crate kommt einer mit Tera und Axum geschriebenen Webapp nahe
    • Der zu migrierende Rust-Code umfasst insgesamt 14.943 Zeilen, die Kompilierung dauert etwa 10 Sekunden
    • Der Code selbst ist nicht groß, hängt aber an einer Struktur mit vielen Abhängigkeiten
  • Die bestehende Rust-Konfiguration verursacht hohe Testkosten
    • Für E2E-Tests ist eine Playwright-Konfiguration erforderlich
    • Weil Mocking schwierig ist, werden isolierte Datenbank-Namespaces und Mocking-Services benötigt
    • Zusätzlich ist ein internes API-Crate nötig, damit Playwright im Headless-Modus mit der App interagieren kann
  • Ruby und Ruby on Rails werden als kompakte Alternative betrachtet
    • Ruby ist untypisiert, wodurch die Stabilität geringer sein kann als bei Rust
    • Mit Sorbet lässt sich die Typsicherheit in Ruby teilweise ergänzen
  • Ein Vergleich mit mehreren LLM-Instanzen zu Komplexität, Stabilität, Testbarkeit und weiteren Kriterien fiel zugunsten von Rails aus
    • Rust/Axum/Diesel kommt insgesamt auf 480, Rails auf 710, Rails + Sorbet auf 695
    • Rails wurde mit 90 für Eignung für Einzelentwickler, 90 für Entwicklungsgeschwindigkeit und 90 für einfache Unit-Tests hoch bewertet
    • Rust/Axum/Diesel erzielt zwar 95 bei Sicherheit und 95 bei Performance, liegt aber bei Unit-Tests mit 20 und bei Integrationstests mit 30 deutlich niedriger
    • Auf Basis der einfachen Gesamtsumme wurde geschlossen, dass eine Rails-App ein 1,47-fach besseres Ergebnis liefern könnte

Ergebnis der Konvertierung und Prüfpunkte

  • Mit Local Qwen3.6 wurde ein relativ kleines Projekt in einem One-Shot konvertiert
    • Die Konvertierung dauerte etwa 30 Minuten
    • Es wurde noch nicht ausgeführt, daher ist die tatsächliche Funktionsfähigkeit nicht bestätigt
  • Die größte Veränderung ist die Verringerung der Codezeilen
    • Gesamte Zeilenzahl der Rust-Dateien: 14.943
    • Gesamte Zeilenzahl der Ruby-Dateien: 3.322
    • Die Zeilenzahl sank um 77 %, und eine Ruby-Zeile entspricht ungefähr 4,49 Rust-Zeilen
  • Der konvertierte Ruby-Code wirkt im überflogenen Umfang sauber und idiomatisch
    • Die Möglichkeit von Bugs bleibt bestehen
    • Eine genauere Prüfung ist für später geplant
  • Weitere Prüfpunkte sind Typ-Ergänzung, die Grundfunktionen von Rails und die Vereinfachung von Tests
    • Wenn ein Agent Typen ergänzt, lässt sich das Problem fehlender Typsicherheit abmildern
    • Ruby/Rails wird als näher an „batteries + kitchen sink included“ bewertet und damit besser als 3 GiB kompilierter Abhängigkeiten
    • Es wird erwartet, dass Tests deutlich einfacher werden
  • Das Ruby-Testbeispiel kapselt den LLM-Aufruf mit VCR.use_cassette("llm_call") und prüft in kurzer Form die Größe des Ergebnisses
      VCR.use_cassette("llm_call") do
        result = LlmClient.match(entry, data_list)
        expect(result.results.size).to eq(data_list.size)
      end
    
  • Das Rust-Testbeispiel ist länger, weil ein Mock-Provider direkt implementiert werden muss
    • Verwendet werden Arc<RwLock<Vec<Response>>>, AtomicUsize, async_trait, tokio::test usw.
    • Es wird ein MockProvider erstellt, der die Antwortliste und die Anzahl der Aufrufe verwaltet; anschließend wird match des Provider-Traits implementiert und im Test werden Ergebnis und Anzahl der Aufrufe geprüft
  • Da es sich um ein privates Projekt handelt, sind mutige Entscheidungen möglich; die Umstellung von Rust auf Ruby soll in Zukunft noch eingehend geprüft werden

1 Kommentare

 
Hacker-News-Kommentare
  • Kaum zu glauben. Das klingt für mich nach: „Es gab da einen technischen Juckreiz, den ich kratzen wollte, und lokale KI hat die Arbeit in 30 Minuten erledigt. Ich habe nicht einmal auf Start gedrückt, um zu sehen, ob es läuft, aber den Blogpost habe ich schon geschrieben …“

    • Dass das gerade auf Platz 1 der Startseite steht, heißt wohl, dass es egal ist, ob das Projekt tatsächlich funktioniert. Die Leser werden den Artikel ohnehin nicht richtig gelesen haben
      außer natürlich, Bots haben ihn nicht nach oben gepusht
    • 2016: Schaut euch meine neue JavaScript-Bibliothek an!
      2026: Schaut euch etwas an, das ich nicht selbst geschrieben habe!
      2036: So habe ich 200 Zeilen in C geschrieben, dieser alten lateinischen Sprache
    • Weniger „kaum zu glauben“ als vielmehr etwas, das im Jahr 2026 ziemlich gewöhnlich wirkt
    • Er hat es allerdings 5 Stunden lang überprüft. Also ¯\(ツ)
  • Erst dachte ich, das wird ein interessanter Artikel, aber in dem Moment, als erwähnt wurde, dass für die Migration ein LLM benutzt wurde, war mein Interesse sofort weg. Das ist ungefähr wie: „Ich wollte das machen lassen, also habe ich es einem Untergebenen aufgetragen, und jetzt erzähle ich euch davon.“
    Er hat die Migration weder selbst gemacht noch besonders tief darüber nachgedacht, daher sehe ich keinen Grund, das zu lesen

    • Das größte Problem ist, dass der Autor es nicht einmal verifiziert hat. Ich habe schon erlebt, dass große Tools mit mehreren Modellen 6 Stunden lang eine Migration laufen lassen und, wenn man dann manuell prüft, wie sie heikle Berechnungen oder Logik umgesetzt haben, Stub-Code oder hartcodierte true-Rückgaben eingebaut sind
      Dann kann schon ein einfacher Smoke-Test so aussehen lassen, als wäre alles erfolgreich gewesen
    • Das größere Problem ist, dass sich die Softwareentwicklung künftig genau in diese Richtung bewegt
      Abgesehen von handwerklicher Programmierung wird die Programmiersprache selbst immer weniger wichtig
      Je besser LLMs werden, desto mehr wird es am Ende nur noch darum gehen, Spezifikationen in irgendeiner Art von Sprache erzeugen zu lassen
      Das UML- und RUP-Lager hat damit wohl doch noch seine Rache bekommen
    • Entscheidend ist nicht, ob der Code direkt von Hand geschrieben wurde, sondern dass Unterschiede betrachtet und dabei vermutlich nicht optimale Entscheidungen getroffen wurden
      Wie auch in anderen Kommentaren erwähnt, wurde das Ganze ziemlich umfassend geprüft. Es ist kein großes Projekt, und abgesehen von ein paar heiklen Stellen ist es größtenteils eine Web-App
      Zu behaupten, es sei „nicht nachgedacht“ worden, halte ich für unfair. Das war kein Knopf-drücken-und-YOLO
      Es wurden Trade-offs und Ergebnisse untersucht, die Unterschiede zwischen Rust-Codefragmenten und Rails waren real erheblich, und die Testbarkeit der Rust-App war zwei Monate lang ein Thema der Überlegung
      Wie LLM-Fans gern sagen: Kontext ist wichtig ;)
  • Ich weiß nicht, ob irgendeine Sprache oder irgendein Framework das Glück der Entwickler so stark priorisiert wie Ruby on Rails

    • So unglücklich wie bei der Arbeit an Rails-Projekten war ich fast nie. Ich sehe einen Bug auf der Website, starte grep, um die View zu finden, die falsch rendert. Ich finde den Methodenaufruf, der den betreffenden Abschnitt rendert, grepe dann nach dem Methodennamen und bekomme null Treffer
      Irgendetwas wird irgendwo zusammengesetzt, ich kann nicht herausfinden, wo es ist, und am Ende höre ich mit meiner eigentlichen Arbeit auf und lese eine Stunde lang Dokumentation. Für Leute, die den ganzen Tag nur Rails machen, ist das vielleicht okay, aber Convention over Configuration ist für mich ein riesiges Anti-Pattern
    • Bei Elixir und Phoenix habe ich ein ähnliches Gefühl, aber dort gibt es wenigstens keine Vorrichtung wie method_missing, mit der man sich selbst ins Knie schießt
    • Auch wenn es in Silicon-Valley-geprägten Communities nicht attraktiv wirkt, habe ich auch mit Java- und .NET-Frameworks sehr zufrieden gearbeitet
      Glück führt nicht immer zu Leistung. Mir fällt dabei das berühmte Logo-Beispiel von Twitter ein, bevor der Wechsel auf JVM und Scala kam
      Ruby on Rails wurde zwar berühmt, aber ähnliche Erfahrungen gab es schon mit dem Tcl-basierten AOLServer und Vignette
      In einem portugiesischen Startup wurde sogar eine eigene Abwandlung gebaut, und die Gründer haben später OutSystems geschaffen. Das war eines der frühen grafischen RAD-Tools für die Entwicklung von Websites und verteilten Systemen, als Low-Code/No-Code für JVM- oder CLR-Infrastruktur
      Trotzdem freue ich mich inzwischen darüber, dass JIT in CRuby standardmäßig mitgeliefert wird
    • Bestenfalls ist das kurzfristiges Glück, erkauft um den Preis aller anderen Architektureigenschaften wie Wartbarkeit, Performance, Zuverlässigkeit und Skalierbarkeit
  • Ich habe bereits ein Gem-Bundle namens propel_rails gebaut, das ohnehin schon kompakten Ruby-on-Rails-Code noch weiter auf die Spitze treibt. Es erzeugt Oberklassen wie API-Controller und Concerns, und von dort aus entstehen komplette RESTful Resources (Modelle, Controller, Serializer, Unit-Tests und E2E-Tests) ganz ohne Boilerplate
    Am Ende enthalten Controller nur noch die Liste der Attribute, die die API zulässt, weil die RESTful Actions automatisch generiert werden. Es ist etwas schwer, das vollständig zu erklären, aber die Kraft von Rubys Metaprogrammierung macht wirklich erstaunliche Dinge sehr einfach

    • Das klingt wie eine veredelte Form von CRUD
      Kann man es so verstehen, dass es vom Domain Model ausgeht?
    • Auf rubygems ist es zu finden, aber der dort verlinkte GitHub-Link liefert 404
  • Ich bin in einer ähnlichen Situation
    Ich mag Go und Rust, und beides sind großartige Sprachen mit Vor- und Nachteilen. Aber leider konnte ich mit keiner von beiden eine SaaS-App bauen. Es fühlt sich an, als würde man einen quadratischen Stift in ein rundes Loch stecken
    Vielleicht irre ich mich komplett, aber SaaS-Tools bringen eine Menge Dinge mit, die man nicht neu erfinden will
    RoR ist „gut genug“. Man kann Typisierung einführen, wenn man sie will, man kann schnell bauen, und die Tools sind auch okay
    Mein erster Job war mit PHP, und dort gab es zu viele Fallstricke. Ruby wirkte für mich etwas stärker in Richtung E-Commerce geneigt, was ich interessant fand, und wenn es für Shopify gut genug ist, reicht es wohl auch mir

  • Wenn es sinnvoll ist, von Rust auf Ruby umzusteigen, dann war schon die ursprüngliche Wahl von Rust ein Fehler

    • Das ist eine sehr gute Zusammenfassung des Artikels und auch der Grund, warum ich ihn geteilt habe
  • Wer denkt, Ruby sei langsamer als Rust, wäre vermutlich überrascht zu erfahren, dass Ruby heute in der Praxis schneller als Python ist und langsamer als Go oder Rust

    • Für mich war meist eher der Speicherverbrauch wichtiger als die Geschwindigkeit. Die meisten Ruby-Anwendungen sind nicht wegen Ruby langsam, sondern wegen der Datenbank
      Wenn man aber mehrere Background-Worker hat und jeder davon mehr als 2 GB Speicher frisst, summiert sich das schnell
      Ich habe einen Rust-Service in Produktion geschrieben, und beeindruckender als die Geschwindigkeit war für mich, dass dieser Service mit 30 MB Speicher lief
    • Ich verstehe nicht, warum jemanden die Tatsache überraschen sollte, dass Ruby schneller als Python ist, wenn er ohnehin denkt, dass Ruby langsamer als Rust ist. Was hat das eine mit dem anderen zu tun?
  • Provokant formuliert: Für die gewöhnlichen Leute um dich herum ist Rust vielleicht gut, um mit 900+ IQ anzugeben, aber viele kluge und talentierte Entwickler mögen Rust schlicht nicht besonders. Manche ziehen es sogar vor, ihre eigene Programmiersprache und ihren eigenen Compiler zu bauen, statt auch nur eine Zeile Rust zu schreiben
    Mir fallen da Jonathan Blows Jai und Ginger Bills Odin ein
    Es gibt noch viele weitere Leute mit bewiesener Kreativität und Tiefe, die schöne, weit verbreitete Bibliotheken und Frameworks gebaut haben, aber ich will hier keinen Platz verschwenden
    Rust hat allerdings einen coolen Macho-Club und eine eng verbundene Bruderschaft

    • Die Rust-Community ist ungefähr so weit wie möglich von einem „Macho-Club“ entfernt
  • Claude funktioniert bei Rails-Apps wirklich gut. Wie der Autor des Artikels auch anmerkt, ermöglicht Ruby mit wenig Code sehr viel, und Rails setzt auf Convention over Configuration, wodurch Rails-Apps noch kompakter werden
    Eine mögliche Hypothese, warum Claude Rails-Apps so gut schreiben kann, ist Token-Effizienz
    Ich habe einmal dieses Projekt gesehen, das die Token-Effizienz pro Projekt messen und vergleichen wollte, und Rails hat dort ziemlich gut abgeschnitten
    https://felipemrvieira.github.io/SyntaxTax/dashboard/

  • Ich bin manchmal überrascht, wenn ich die Größe von Projekten sehe. Eine Codebasis mit 30.000 Zeilen soll klein sein? Mir ist klar, dass die Obergrenze hoch liegt, aber 30.000 Zeilen können eine enorme Menge an Information und subtilem Verhalten enthalten
    Vielleicht liegt das an meinem Hintergrund, der eher auf Backend/Netzwerk mit Go fokussiert war. Sobald es über 10.000 bis 15.000 Zeilen hinausging, wurde es für mich normalerweise ziemlich belastend, das Gesamtmodell der Codebasis im Kopf zu behalten

    • Das hängt stark davon ab, was man baut. Eine SaaS-App mit mehreren Frontends erreicht 30.000 Zeilen sehr leicht