Bei unserer letzten Teambuilding-Veranstaltung habe ich einen Vortrag darüber gehalten, warum JavaScript-Entwickler ihre Werkzeuge auf Rust umstellen. Der Vortrag kam ziemlich gut an, daher entschieden wir, dass es cool wäre, ihn mit der Welt zu teilen. Dies ist die bearbeitete Version des Vortrags zum Lesen. Zusätzlich gibt es auch einen Screencast auf unserem Youtube-Kanal in englischer Sprache. Aber genug des Vorgeplänkels.
Ein weit verbreiteter Trend in der Softwarebranche ist es, die Werkzeuge, die wir als Entwickler bei der Erstellung von Anwendungen verwenden, in der gleichen Sprache zu schreiben, in der die Anwendungen selbst geschrieben werden. Pip ist zum Beispiel in Python geschrieben, Composer in PHP, Maven in Java, usw. Das ergibt Sinn, sind es doch meistens dieselben Menschen, welche die Werkzeuge entwickeln, und diese dann auch einsetzen. Daher ist es oft einfacher und effizienter, in der ihnen bekannten und gewohnten Sprache zu bleiben.
JavaScript ist da keine Ausnahme. Von Gulp und Bower bis hin zu Babel und Webpack haben wir alles in JavaScript geschrieben. Das hat überwiegend gut funktioniert, aber es gab auch ein paar Probleme.
Um das Problem zu verstehen, müssen wir uns die Sprache JavaScript genauer ansehen. JavaScript ist eine leichtgewichtige Skriptsprache für Webseiten. Natürlich verwenden wir sie heute überall, von Backends bis hin zu Datenbanken, aber sie wurde ursprünglich dafür entwickelt, um die Darstellung von Webseiten aufzuhübschen und erst in zweiter Linie für alles andere, ein Fakt, den man unweigerlich bemerkt.
Es handelt sich um eine interpretierte Sprache, die just-in-time kompiliert werden kann und single-threaded ist. Ja, ja, ich weiß, dass man mehrere Instanzen aufsetzen und mit Web-Workern kommunizieren kann... aber meiner Meinung nach ist das kein Multi-Threading, sondern ein seltsamer Voodoo-Workaround, der uns von der Tatsache ablenken soll, dass wir kein natives Multi-Threading haben.
Ein größeres Problem ist, dass es auf der Konsole ziemlich langsam ist, verglichen mit anderen Sprachen.
Während JavaScript auf einer Seite großartig ist, ist es auf einer Konsole ziemlich ätzend.
Also trafen sich eines Tages ein paar JavaScript-Entwickler in einem dunklen Raum und nach einem Drink zu viel sagte jemand:
„Hey Leute, was wäre, wenn … lasst mich ausreden … was wäre, wenn wir die Werkzeuge, die unser JavaScript bauen, nicht in JavaScript schreiben?“
Es gab kollektive Schnappatmung, Stühle wurden geworfen, Streitereien brachen aus, und als sich der Staub gelegt hatte, waren alle einverstanden, es auszuprobieren.
Ok, das habe ich mir vielleicht ausgedacht, aber das Ergebnis ist immer noch dasselbe: Wir schreiben jetzt JavaScript-Erstellungswerkzeuge in anderen Sprachen als JavaScript.
In einem Wort … Geschwindigkeit und Pufferüberläufe 😉.
Rust ist eine Mehrparadigmen- und Allzweckprogrammiersprache, die auf Leistung und Sicherheit ausgelegt ist. Es ist wie C++, aber für Millennials und Zoomer. Es ist eine Systemsprache, die Speichersicherheit erzwingt und „sichere“ Gleichzeitigkeit bietet. Sie ist syntaktisch ähnlich wie C++ und, was noch wichtiger ist, sie ist fast so schnell wie C++.
Es gibt inzwischen eine Reihe von Projekten, die versuchen, JavaScript-Werkzeuge durch Rust-basierte zu ersetzen. Ein paar Beispiele sind ParcelJS, Deno, ESBuild und der Speedy Web Compiler (SWC). Sie alle behaupten, schneller zu sein als das, was sie zu ersetzen versuchen, und wenn du was zum Schmunzeln haben möchtest, schau dir den ESBuild-Vergleich an.
In diesem Beitrag werde ich nicht alle unter die Lupe nehmen, aber ich werde mir SWC über Next.js genauer ansehen.
SWC ist eine erweiterbare, auf Rust basierende Plattform für die nächste Generation von schnellen Entwickler-Werkzeugen. Das heißt, es ist ein Framework zum Erstellen von Buildern. Sie können SWC sowohl zum Kompilieren als auch zum Bündeln verwenden. Er nimmt JavaScript-/TypeScript-Dateien mit modernen JavaScript-Funktionen auf und spuckt gültigen Code aus, der von allen wichtigen Browsern unterstützt wird.
Next.js hat in Version 12 einen Rust-Compiler eingeführt, der auf SWC basiert. Auf der Next.js-Website wird eine ~3x schnellere lokale Aktualisierung und ~5x schnellere Produktions-Builds behauptet.
Das klingt für mich nach einer überprüfbaren Behauptung. Also habe ich es getestet.
Ich habe das exakt gleiche Projekt in Version 11 und 12 erstellt und die gleichen 400 generierten Komponenten hinzugefügt. Dazu habe ich den React Benchmark Generator verwendet. Der einzige Unterschied zwischen den Projekten war die Version von Next.js, alles andere war identisch.
Die Ergebnisse waren ziemlich überzeugend. Hier sind sie für Next.js 11:
Für Next.js 12 sieht es folgendermaßen aus:
Next.js 12 brauchte 12 Sekunden für das, was Next.js 11 in 1 Minute und 40 Sekunden schaffte. Das ist ungefähr 8 Mal schneller. Sie haben also eindeutig nicht übertrieben.
Ich hatte auch nicht erwartet, dass Next.js 12 12 Sekunden braucht. Ich schätze, das war ein glücklicher Zufall.
Nun stellt sich natürlich die Frage: Lohnt sich der Aufwand?
Die Anwendung, die wir entwickeln, ist doch letztendlich die gleiche, oder? Das ändert nichts an der Erfahrung des Endbenutzers, sondern nur an der des Entwicklers, warum also die Mühe? Warum etwas reparieren, das bereits gut funktionierte?
Weil es nicht gut funktioniert hat. Wie ich bereits sagte, ist JavaScript auf einer Seite großartig, aber auf einer Konsole stinkt es ab. Entwickler-Maschinen sind mächtige Biester, und diese Leistung nicht zu nutzen, ist eine schreckliche Verschwendung. Es ist auch eine teure Verschwendung.
Stell dir vor, du arbeitest in einem Team von 20 Entwicklern an einem großen Projekt, das über ein cloudbasiertes CI bereitgestellt und getestet wird. Jeden Tag gibt jedes Teammitglied mehrere Korrekturen und Funktionen heraus.
Wenn Ihr CI unbegrenzt viele gleichzeitige Builds zulässt, aber nach Build-Minuten oder Sekunden Prozessorzeit abrechnet, dann wird Next.js dich buchstäblich 8x mehr kosten als Next.js 12.
Wenn du hingegen einen festen Preis und eine feste Anzahl von gleichzeitigen Builds hast, wirst du eine Weile warten müssen, wenn deine Build-Warteschlange wächst, oder du musst zahlen, um die Anzahl der gleichzeitigen Builds zu erhöhen, die du ausführen kannst.
So oder so, langsame Builds kosten mehr Zeit oder Geld oder beides.
JavaScript ist eine erstaunliche Sprache, ohne die das Internet nicht das wäre, was es ist. Aber wir müssen sie nicht verwenden, um unsere Werkzeuge zu entwickeln, denn dafür gibt es härtere, bessere, schnellere und stärkere Sprachen wie Rust, und das ist in Ordnung. Es nimmt JavaScript nichts weg und es ist kein Verrat, JavaScript-basierte Tools für schnellere Rust-basierte aufzugeben. Und seien wir ehrlich, ich werde mich nicht durch mehrere JavaScript-Instanzen quälen, nur um Voodoo-Multi-Threading zu bekommen.