3 Punkte von GN⁺ 2023-07-03 | 1 Kommentare | Auf WhatsApp teilen
  • Ein JavaScript-Entwickler mit 10 Jahren Erfahrung nimmt das Lesen von package.json als Prüfung des Schmerzes und zeigt, wie viel angesammelte Komplexität des Ökosystems in einer einzigen Konfigurationsdatei steckt
  • Felder wie main, browser und type: module rufen alte Migrationswunden rund um Isomorphic JavaScript, Browser-Bundles sowie den Übergang von CommonJS zu ESM in Erinnerung
  • In scripts, dependencies und devDependencies verknüpfen sich alte Tools und Probleme der Paketverwaltung wie Lint-Varianten, watchman, gulp, left-pad, moment.js und resolutions
  • eslint, postcss, jest sowie Build- und Test-Tools erhöhen durch strenge Regeln, plattformspezifische Installationsfehler und die schwierige Konfiguration von ESM und TypeScript die Kosten für die Pflege der Entwicklungsumgebung
  • Dass man trotz Node-engines nicht zu Bun oder Deno wechselt, liegt an der Überzeugung, dass der Schmerz nicht verschwindet, sondern noch größer würde; der Entwickler schließt die Datei und überlebt erst einmal

Die Prüfung, package.json zu lesen

  • Die Geschichte beginnt damit, dass ein Entwickler, der seit 10 Jahren JavaScript nutzt, vor dem Computer sitzt, die Datei package.json öffnet und sie bis zum Ende lesen muss
  • Diese Datei ist nicht einfach nur eine Konfigurationsdatei, sondern ein Objekt, das alle Erinnerungen und Schmerzen aus dem JavaScript-Ökosystem auf einmal wachruft
  • version hat noch immer nicht 1.0 erreicht, und SemVer bleibt als unangenehme Erinnerung daran, dass man schon vor langer Zeit gelernt hat, es zu ignorieren
  • name zeigt die Situation, dass man den gewünschten Namen nicht verwenden kann, weil ihn schon vor 7 Jahren jemand durch ein Paket belegt hat, das seitdem nicht mehr aktualisiert wurde

Alte Wunden von Modulen und Bundling

  • Dass die Felder main und browser gemeinsam vorhanden sind, legt Spuren von Isomorphic JavaScript offen
  • Die Erinnerung daran, in einem Browser-Bundle per require das Modul fs laden zu wollen, und an die Hacks, die nötig waren, damit es funktioniert, bleibt als unangenehme Erfahrung zurück
  • type: module ist mit der Migration von require zu import verbunden
  • Der Versuch, CommonJS- und ESM-Module gegenseitig zu importieren, ist als großer Schmerz in Erinnerung geblieben

Das Durcheinander aus Scripts und Abhängigkeiten

  • scripts ist das Zentrum eines heißen, schmerzhaften Durcheinanders
    • Es gibt Varianten wie lint, lintall, lintfast und lintdiff
    • Parallele Ausführung, unklare Argumente und doppelt escapte Argumente im JSON-Format tauchen auf
    • Auch nach dem Wechsel zu yarn und pnpm rufen einige Unterbefehle weiterhin npm auf
  • Einige Scripts verwenden noch immer watchman
    • Man muss sich daran erinnern, dass man es nicht verwenden sollte, weil es keine Symlinks unterstützt
    • Das zugehörige Issue ist seit 2015 offen
  • Auch gulp-basierte Scripts sind noch vorhanden; man würde sie gern durch etwas Moderneres ersetzen, aber niemand hat den Mut dazu

Das Trauma, das die Paketliste auslöst

  • In dependencies stehen sehr viele Pakete, und jedes Paket belebt seine eigenen Trauma-Erinnerungen wieder
  • left-pad ist ein kleines Paket, bleibt aber als legendäres Paket in Erinnerung, das „das ganze Internet kaputtgemacht“ hat
  • Jedes Mal, wenn die Abhängigkeitsliste geändert wird, geben einige Pakete lange Spendenaufrufe oder Warnungen vor Breaking Changes in der Konsole aus
  • Der Entwickler befindet sich in einem Zustand, in dem er nur noch hofft, dass kein bösartiges Paket Geheimnisse stiehlt oder den Computer zerstört
  • moment.js bleibt als Bibliothek mit einer guten API in Erinnerung, doch das Internet kam zu dem Urteil, dass sie zu mutable, zu groß ist und kein Treeshaking unterstützt; nun steht die Migration zu date-fns an
  • Die Paketversionen liegen deutlich hinter dem heutigen Stand zurück und müssten aktualisiert werden, doch aufgrund früherer Upgrade-Erfahrungen ist großer Schmerz zu erwarten
  • resolutions bleibt als weiterer Schmerz zurück, den man gewählt hat, um Paket-Upgrades zu vermeiden

Eine weitere Komplexität durch Entwicklungswerkzeuge

  • Beim Blick auf devDependencies fällt es schwer, sich an die Zeit zu erinnern, in der man nur non-dev dependencies brauchte
  • Die eslint-Konfiguration ist so streng geworden, dass schon bei kleinen Fehlern rote Unterstreichungen erscheinen und CI Lint-Probleme wie das Ende der Welt behandelt
    • Junior Engineers im Team gibt sie ein falsches Gefühl von Sicherheit
    • Es gab mehrfach Streit darüber, welche Regeln aktiviert werden sollen
    • Der Schmerz wächst mit der Zahl der eslint-ignore-Einträge überall in der Codebase
  • postcss wird nicht direkt verwendet, existiert aber als Paket, das von einer Abhängigkeit einer Abhängigkeit verlangt wird
    • Bei jedem Versuch, auf einer neuen Plattform npm install auszuführen, verursacht es unklare C++-Kompilierfehler
  • jest begann als schneller Test-Runner, ist inzwischen aber groß und schwer geworden und hängt von einigen Babel-Paketen ab
    • Der Rest der Anwendung wird mit einer Kombination aus esbuild und swc transpiliert
    • Es korrekt für ESM und TypeScript einzurichten, ist schwierig wie ein „PhD-Wissenschaftsprojekt“
  • In der Codebase sind viele Tools und Parser beteiligt, darunter TypeScript, esbuild, swc, babel, eslint, prettier, jest, webpack, rollup und terser
  • In engines ist node angegeben, und selbst wenn man zu Bun oder Deno wechseln würde, geht man davon aus, dass der Schmerz nicht aufhört, sondern schlimmer wird
  • Nachdem die letzte schließende geschweifte Klammer gelesen ist, schließt er den Tab, holt Luft und überlebt erst einmal mit intakten Händen und intaktem Gehirn

1 Kommentare

 
GN⁺ 2023-07-03
Hacker-News-Kommentare
  • Ziemlich lustig. Ich habe etwa 18 Jahre lang JavaScript geschrieben und vor ungefähr 3 Jahren damit aufgehört, und die meisten Sorgenpunkte auf dieser Liste stammen aus den letzten 10 Jahren.
    Besonders auffällig ist, dass Polyfills fehlen, die 2013 zwingend dabei gewesen wären. Was für JavaScript-Albträume aus dem Jahr 2013 sind heute sonst fast verschwunden? Natürlich wurden sie durch diese lustige Liste ersetzt.
    Mein Lieblingsabschnitt war der, in dem man die funktionierenden Tools und Parser in der Codebasis zu zählen beginnt und dann auf TypeScript, esbuild, swc, babel, eslint, prettier, jest, webpack, rollup und terser kommt und der Schmerz so groß wird, dass man gar nicht mehr wissen will, was man noch vergessen hat.

    • Grunt, Gulp, CoffeeScript, IIFE, überschriebenes undefined, with, per Closure nachgebildete private Felder, veränderte Prototypen, implizite globale Variablen, this, das sich je nach Aufrufstelle verändert, automatische Typumwandlung bei Vergleichen …
      Ja, so sind mehr als 10 Jahre JavaScript vergangen.
    • Dem Teil über Callbacks stimme ich zu. Aber wenn ich auf mein JavaScript von vor 10 Jahren zurückblicke, war der größte Schmerz all die Fake-Klassen, die durch Änderungen an SomeFunction.prototype gebaut wurden.
      Das wurde dann in eine sofort ausgeführte anonyme Funktion gepackt und als Objekt zurückgegeben, sodass man new SomeFunction() oder SomeFunction.actualFunction() aufrufen konnte, als wäre SomeFunction eine Klasse. Dazu kam noch, dass es keine Arrow Functions gab, weshalb man zur Bindung von Events an den Scope gleich Function.prototype selbst veränderte und .apply() benutzte, oder in eine endlose .bind(this)-Hölle für jeden Event-Callback geriet.
    • Ich habe in Firefox entwickelt, um Firebug zu verwenden, und musste ie6, ie7, ie8 jeweils separat unterstützen.
    • Es gab auch eine Zeit, in der man Header in kleine Flash-Applets verwandelte, um beliebige Schriftarten verwenden zu können und nicht auf die beim Nutzer installierten Fonts beschränkt zu sein.
    • Callback-Hölle und das generelle Fehlen von Typen waren ein großes Problem.
      Heute hatte ich wieder das Pech, eine Testaufgabe für eine Firma zu schreiben, die immer noch pures JavaScript ohne Promises oder Deklarationsdateien verwendet, und es fühlte sich an, als wäre ich zurück im Jahr 2012, als ich Node zum ersten Mal begegnet bin. Die Vorgabe, JavaScript zu verwenden, habe ich zwar mit JSDoc umgangen, aber es war viel schlimmer, als ich es in Erinnerung hatte.
  • Jedes Mal, wenn ich ein neues JavaScript- oder genauer gesagt TypeScript-Projekt starte, durchlaufe ich immer denselben Ablauf.
    Meist ist es Frontend, also entscheide ich mich für React, obwohl ich auch mal Svelte ausprobieren möchte. Beim Bundler waren es früher create-react-app oder Webpack-Wrapper, heute ist Vite wegen der geringen Konfiguration und der Unterstützung aller wichtigen Frameworks die beste Wahl.
    Dann installiere und konfiguriere ich den TypeScript-Compiler, meist per Copy-paste aus einem Template. Als Ziel setze ich aktuelles stabiles JavaScript und ESM-Module, richte eslint und prettier passend ein und füge Plugins für TypeScript und React hinzu. Außerdem baue ich Hooks ein, die vor Commits Prettier und eslint ausführen.
    Ich installiere auch IDE-Erweiterungen und lege ein .devcontainer oder eine Liste empfohlener Extensions an. Für den Stil selbst interessiere ich mich nicht, wichtig sind mir Code-Konsistenz und das Erkennen von Fehlern, deshalb nutze ich bei eslint meist bestehende Konfigurationen.
    Danach richte ich Unit-Tests wie jest oder vitest ein, und schon kommen wieder TypeScript-bezogene Einstellungen und eslint-Regeln dazu. Als CSS-Framework mag ich tailwind, also installiere ich auch styled-components und twin.macro, damit es gut mit React zusammenspielt, dazu noch ein Google-Fonts-Plugin oder eine Font-Bibliothek.
    Erst nach etwa einem Tag kann ich überhaupt mit dem Programmieren anfangen. Warum ist das so viel Arbeit? So viele Leute nutzen Kombinationen wie Vite + TypeScript + React/Vue/Svelte + eslint/prettier + tailwind/bootstrap + jest/vitest — warum gibt es keine Zero-Config-Bibliothek, die für 90 % der Anwendungsfälle sofort passt?

    • Selbst jemand, der das alles schon kennt, braucht einen Tag dafür. Als Entwickler eher im Backend- und Ops-Bereich habe ich ein paarmal versucht, in solche Stacks einzusteigen, aber ehrlich gesagt bin ich oft einfach gescheitert.
      Selbst wenn ich die Toolchain irgendwie zum Laufen gebracht habe, hatte ich nie das Gefühl, sie unter Kontrolle zu haben; es wirkte, als würde alles schon bei der kleinsten Erschütterung zusammenbrechen, und als könnte ich es dann auch nicht reparieren.
      In letzter Zeit habe ich beschlossen, den Großteil dieses JavaScript-Stacks als Hindernis für das zu sehen, was ich eigentlich machen will, und habe wieder eine serverseitig gerenderte App auf Basis von Flask und Jinja2 gebaut. Sie ist nicht modern-glänzend und ohne CSS momentan ziemlich hässlich, aber mir wurde geraten, einfach mit reinem modernem CSS weiterzumachen. Trotzdem ist es erstaunlich, wie schnell man in diesem Stack Features bauen kann.
    • Es gibt zwei Linter. Allein das ist schon verrückt. Der Rest ist noch schlimmer, aber selbst das kleinste Detail bei der Tool-Auswahl im JavaScript-Ökosystem ist voller Schmerz.
    • Ich kopiere einfach mein TypeScript-Starter-Verzeichnis und ändere nur den Namen in der package.json. Zugegeben, ich mache eigentlich nur Backend, aber könnte man sich nicht auch fürs Frontend so ein persönliches Template bauen?
    • Macht das nicht npm create?
      Wenn man npm create vue ausführt, bekommt man eine ziemlich gute, funktionierende Vue-Konfiguration, bei der der Großteil schon eingerichtet ist. Ich stimme zu, dass es viel Boilerplate und allerlei Spielereien gibt, aber die Apps, die wir heute fürs Web bauen, sind auch viel größer als früher. Man könnte JavaScript mit ESModules auch ganz ohne Tooling verwenden, aber meist tut man das nicht, weil diese Tools tatsächlich etwas leisten. Es ist nicht die eleganteste Lösung, aber es funktioniert immerhin.
    • Man könnte wohl einfach ein Stub-Projekt mit all den oben genannten Lieblingskonfigurationen erstellen und auf GitHub hochladen.
      Wenn man ein neues Projekt startet, klont man es einfach und ändert nur ein paar Namen, dann ist alles bereit.
  • Das ist wieder so ein Fall, in dem Leute JavaScript sagen, aber eigentlich „zusammengewürfelter Kram aus dem Node-Ökosystem“ meinen.
    Es fängt schon damit an, dass man eine package.json-Datei öffnen und lesen soll, und danach bleibt es die ganze Zeit in dieser Spur. Von den erwähnten Dingen hat fast nichts wirklich mit JavaScript selbst zu tun.

    • Wie hoch ist wohl der Anteil der JavaScript-Entwickler, die NodeJS nicht benutzen?
      Es stimmt schon, dass JavaScript und NodeJS nicht dasselbe sind, aber die meisten werden wohl verstanden haben, was der Autor gemeint hat. Präziser wäre besser gewesen, aber komplett falsch ist es auch nicht.
    • Stimmt, dann ist also das Ökosystem kein legitimes Ziel für Sprachkritik.
    • Schreib doch mal wirklich JavaScript ohne einen Bundler, der package.json verwendet. Bis ECMA einen Paketmanager oder Bundler in die offizielle Spezifikation aufnimmt, ist package.json faktisch Teil von JavaScript.
    • Wenn dich das so sehr stört, dann denk einfach jedes Mal, wenn jemand JavaScript sagt, dass eigentlich ECMAscript gemeint ist.
    • Genau. Das ist Kritik am JavaScript-Ökosystem.
  • Ich mag den scherzhaften Ton. Nachdem einem Haut und Nervenenden ein paar Schichten tief weggebrannt wurden, ist es hilfreich, sich solche Schmerzpunkte noch einmal ins Gedächtnis zu rufen.
    Anders gesagt: Man kann einem motivierten Junior-Entwickler ein neues MacBook geben und ihn ins kalte Wasser werfen, und innerhalb eines Tages ist er produktiv. Er muss nichts über Container, .NET, nginx oder 100 oft benutzte Kommandozeilen-Tools wissen, und wenn etwas schiefläuft, sucht man einfach danach.

    • Stimmt. Die Ironie dieses Kompromisses ist, dass man einen Senior-Entwickler, der seit 20 Jahren Dinge baut, die im Browser laufen, hineinwerfen kann und er trotzdem nicht innerhalb eines Tages startklar ist.
      Obwohl man weiß, dass man in ein paar Tagen ein ganzes Schiff bauen könnte, wenn man es nur direkt mit den Händen anfassen dürfte, kämpft man stattdessen mit Schichten unnötiger Komplexität, während man mit dem bereitgestellten schäbigen Werkzeug ein Schiff in der Flasche repariert. Genau so fühlt es sich an, wenn man bei einem privaten Projekt mit nur JavaScript, HTML und CSS schnell vorankommt und dann in moderne Frontend-Arbeit geworfen wird.
    • Und dann bastelt man ein bis drei Tage lang irgendetwas zusammen, bereut es die nächsten zehn Jahre und schreibt anschließend solche Texte. Der Kreislauf geht weiter.
    • Die Juniors, die ich kenne, sagen dann: „Lasst uns ein Framework bauen, das diese Komplexität handhabt.“
      Dass das Monate dauert und ein Teil des Problems genau in diesem Framework-Übermaß liegt und es am nächsten Tag schon veraltet ist, wissen sie in ihrer Naivität nicht.
    • Inzwischen sucht man nicht mehr, sondern fragt ChatGPT.
  • „Ich habe die Anleitung befolgt, und dann hieß es, grunt sei nicht installiert. Ich bin inzwischen ein alter Mann und wusste nicht so genau, was grunt ist, und auch meine grunt-Datei stimmte irgendwie nicht. Also habe ich ein bisschen gesucht und herausgefunden, was grunt ist. Grunt ist … ehrlich gesagt weiß ich immer noch nicht genau, was das ist.“
    --Joe Armstrong, https://www.youtube.com/watch?v=lKXe3HUG2l4
    Vor ein paar Tagen habe ich gem install wayback_machine_downloader ausgeführt. Ich mochte Ruby nie genug, um wirklich in dieses Ökosystem einzusteigen, aber von außen betrachtet scheint dieses Ökosystem erstaunlich stabil gepflegt worden zu sein. gem ist immer noch das Werkzeug, das man verwendet, und Rails gibt es immer noch.
    Ich warte auf ein neues Projekt, das komplexes Frontend braucht und mehr als 0 bis 30 Zeilen JavaScript rechtfertigt. Dann wäre ich mit HTMX vermutlich glücklicher als mit dem derzeit beliebtesten riesigen Framework.

  • Das ist kein Artikel über JavaScript, sondern über npm.
    Wegen genau so eines Durcheinanders hasse ich npm und meide es, wo ich kann. Privat nutze ich Deno, und bei der Arbeit verwenden wir etwas Monorepo-basiertes, Bazel-ähnliches, das dafür sorgt, dass wir npm nicht brauchen.
    Trotzdem finde ich JavaScript/TypeScript angenehme Sprachen zum Programmieren und greife bei fast allem zuerst dazu. Es ist traurig, wenn Leute sagen „JavaScript ist Mist“, obwohl sie eigentlich meinen, dass es Mist ist, mit npm eine React-App zu bauen. Modernes JavaScript-/TypeScript-Coding macht wirklich Spaß.

    • Stimme zu. Ich baue gerade ein Videospiel und habe beschlossen, überhaupt keine Abhängigkeiten zu benutzen, und seitdem macht das Coden wirklich Spaß.
  • Ich habe das Gefühl, JavaScript und sein Ökosystem haben uns allen PTSD hinterlassen, sodass wir die Komik dieses Textes gar nicht mehr genießen können.
    Es gibt viel zu viele „ernste“ Diskussionen über einen lustigen Text. Man kann ihn gern nach Belieben sezieren, aber es wäre gut, im Kopf zu behalten, dass die eigentliche Absicht Humor ist.

    • Etwas ist nicht deshalb nicht ernst, nur weil es humorvoll ist.
  • Ehrlich gesagt war Node das erste Ökosystem, mit dem ich angefangen habe, und die Dinge, die ich später gelernt habe, hatten jeweils ebenfalls genug irre Probleme.
    Haskell war auf mehreren Maschinen extrem schwer zu benutzen, selbst 2019 war die Installation auf MacOS schmerzhaft, und auch die Pflege des Ökosystems war schwierig. Makros und Erweiterungen sind stark verwirrend, und die Abwärtskompatibilität der Sprache ist ebenfalls nicht gut. Dagegen kann man bei JavaScript von 2010 normalerweise erwarten, dass es 2023 noch funktioniert.
    Schon das saubere Aufsetzen von Docker-Images oder Umgebungen wie codespace/gitpod ist schwierig, und auch die von GitHub bereitgestellten Standard-Images sind nicht besonders gut und haben Probleme.
    C ist auf den meisten Plattformen größtenteils okay, aber die Sprache selbst ist ehrlich gesagt komplex, und man braucht riesiges Wissen über das gesamte System. Das führt leicht zu wenig portablem Code, der für die meisten allgemeinen Programmierzwecke ungeeignet ist.
    Ich könnte noch weitermachen, aber wer mehrere Sprachen benutzt hat, weiß, dass jedes Ökosystem diverse Schwächen hat und wir alle weiter leiden. Bei PHP habe ich ein Projektsetup noch nie in wenigen Tagen abgeschlossen, und Elm ist zwar weniger schmerzhaft, hat aber seine eigenen Probleme mit seinem wohlwollenden Diktator-Modell und der Entwicklungsgeschwindigkeit.

    • Deshalb versuche ich, für fast alles Go zu verwenden. Selbst wenn es nicht die optimale Wahl ist, hat man am wenigsten Nebenkriegsschauplätze.
    • C ist komplex? Es ist fast die einzige Sprache, die ich unabhängig davon, wie oft ich sie benutze, komplett im Kopf behalten kann. Manche moderne JavaScript-Syntax verflüchtigt sich bei mir leicht, obwohl ich sie täglich verwende.
  • Ich bin ein polyglotter Entwickler, der sich mit mehreren Programmierparadigmen und Sprachen ebenso wohlfühlt wie mit Frontend und Backend
    Jedes Mal, wenn ich sehe, dass eine package.json länger als 20 Zeilen ist, reagiere ich fast genau so

    • Ich starre gerade auf ein neues Angular-Frontend, das derzeit entwickelt wird; es hat vier Frontend-Komponenten, und jede einzelne package.json ist über 200 Zeilen lang
      Ich spreche laut vor mich hin, als wollte ich einen Dämon abwehren. „Du darfst keine Angst haben. Angst tötet den Verstand. Angst ist der kleine Tod, der völlige Vernichtung bringt. Ich werde mich meiner Angst stellen. Ich werde zulassen, dass sie über mich hinweggeht. Und wenn sie vorüber ist, werde ich mit dem inneren Auge ihren Weg betrachten. Wo die Angst vergangen ist, wird nichts zurückbleiben. Nur ich werde bleiben.“
      weint
    • Geht mir genauso. Nur als Offtopic-Anmerkung: Der Link im Profil wird von uBlock Origin blockiert und führt in einen Kaninchenbau seltsamer Websites. Ich habe hackernewsers nie benutzt, daher weiß ich nicht, ob das normales Verhalten ist
  • Ein altmodischer JavaScript-Entwickler liest den Artikel und leidet unter PTSD und Flashbacks
    Du darfst keine Angst haben. Angst tötet den Verstand. Angst ist der kleine Tod, der völlige Vernichtung bringt
    Ich werde mich meiner Angst stellen. Ich werde zulassen, dass sie über mich hinweggeht. Und wenn sie vorüber ist, werde ich mit dem inneren Auge ihren Weg betrachten
    Wo die Angst vergangen ist, wird nichts zurückbleiben. Nur ich werde bleiben
    Er beendet die Lektüre des Artikels und merkt, dass er überlebt hat, ist aber bereits schweißgebadet