JavaScript Gom Jabbar
(frantic.im)- En prenant la scène d’un développeur JavaScript avec 10 ans d’expérience en train de lire
package.jsoncomme une épreuve de la douleur, le texte montre à quel point la complexité accumulée de l’écosystème tient dans un seul fichier de configuration - Des champs comme
main,browser,type: moduleravivent les vieilles cicatrices de migration liées à JavaScript isomorphe, aux bundles navigateur et à la transition entre CommonJS et ESM - Dans
scripts,dependencies,devDependencies, s’entremêlent anciennes variantes de lint,watchman, gulp,left-pad, moment.js et problèmes de gestion de paquets commeresolutions eslint,postcss,jestet les outils de build et de test augmentent le coût de maintenance de l’environnement de développement à cause de règles strictes, d’erreurs d’installation spécifiques aux plateformes et de la difficulté de configuration d’ESM et TypeScript- Même en voyant
nodedansengines, la raison de ne pas migrer vers Bun ou Deno est la certitude que la douleur ne disparaîtra pas, mais empirera ; le développeur referme donc le fichier et survit pour l’instant
L’épreuve de lire package.json
- L’histoire commence lorsqu’un développeur qui utilise JavaScript depuis 10 ans ouvre un fichier
package.jsonsur son ordinateur et doit le lire jusqu’au bout - Ce fichier n’est pas un simple fichier de configuration, mais un objet qui fait remonter d’un coup les souvenirs et la douleur vécus dans l’écosystème JavaScript
versionn’a toujours pas atteint1.0, et SemVer reste le souvenir désagréable d’une convention qu’on a appris à ignorer depuis longtempsnamemontre qu’il est impossible d’utiliser le nom souhaité, parce qu’un paquet a déjà été réservé il y a 7 ans par quelqu’un qui ne l’a jamais mis à jour
Les vieilles blessures des modules et du bundling
- La présence simultanée des champs
mainetbrowserrévèle les traces de JavaScript isomorphe - Le souvenir d’avoir essayé de
requirele modulefsdans un bundle navigateur, ainsi que les hacks utilisés pour le faire fonctionner, reste une expérience pénible type: modulerenvoie à la migration derequireversimport- La tentative d’importer réciproquement des modules CommonJS et ESM reste un très grand moment de douleur
La confusion des scripts et des dépendances
scriptsest le cœur d’un chaos brûlant et douloureux- On y trouve des variantes comme
lint,lintall,lintfast,lintdiff - Exécution en parallèle, arguments peu clairs et double échappement d’arguments au format JSON s’y invitent
- Même après une migration vers yarn et pnpm, certains sous-commandes continuent d’appeler npm
- On y trouve des variantes comme
- Certains scripts utilisent encore
watchman- Il faut se souvenir qu’il ne faut pas l’utiliser avec les symlinks, faute de support
- Le ticket correspondant est ouvert depuis 2015
- Des scripts basés sur gulp subsistent aussi, et même si on aimerait les remplacer par quelque chose de plus moderne, personne n’ose s’en charger
La liste de paquets qui ravive les traumatismes
dependenciescontient énormément de paquets, et chacun réactive à sa manière des souvenirs traumatiquesleft-padest un minuscule paquet, mais reste dans les mémoires comme le paquet légendaire qui a « cassé tout l’internet »- À chaque modification de la liste de dépendances, certains paquets affichent dans la console de longues demandes de dons ou des avertissements de breaking change
- Le développeur en vient simplement à espérer qu’aucun paquet malveillant ne volera ses secrets ou ne détruira son ordinateur
- moment.js reste dans les mémoires comme une bibliothèque dotée d’une bonne API, mais internet a jugé qu’elle était trop mutable, trop volumineuse et incompatible avec le treeshaking, si bien qu’il faut désormais migrer vers date-fns
- Les versions de paquets sont très en retard par rapport à l’état actuel, et une mise à niveau est nécessaire, mais les précédentes expériences d’upgrade laissent présager une grande souffrance
resolutionsreste une autre douleur choisie pour éviter de mettre à niveau certains paquets
Une autre couche de complexité créée par les outils de développement
- En regardant
devDependencies, il devient difficile de se souvenir d’une époque où seules les non-dev dependencies étaient nécessaires - La configuration de
eslintest devenue si stricte qu’une petite erreur suffit à faire apparaître des soulignements rouges, et la CI traite les problèmes de lint comme si c’était la fin du monde- Cela donne un faux sentiment de sécurité aux ingénieurs juniors de l’équipe
- Il y a eu de nombreuses disputes sur les règles à activer
- La douleur augmente à la mesure du nombre de
eslint-ignoredisséminés dans la base de code
postcssn’est pas utilisé directement, mais existe parce qu’une dépendance de dépendance l’exige- À chaque tentative de
npm installsur une nouvelle plateforme, il provoque d’obscures erreurs de compilation C++
- À chaque tentative de
jesta commencé comme un runner de tests rapide, mais il est désormais gros et lourd, et dépend de certains paquets Babel- Le reste de l’application est transpilé avec une combinaison d’esbuild et de swc
- Le configurer correctement pour ESM et TypeScript relève d’un « projet scientifique de doctorat » tant c’est difficile
- De nombreux outils et parseurs interviennent dans la base de code : TypeScript, esbuild, swc, babel, eslint, prettier, jest, webpack, rollup, terser, etc.
enginesmentionnenode, et même en migrant vers Bun ou Deno, la douleur ne s’arrêterait pas ; elle empirerait probablement- Après avoir lu jusqu’à la dernière accolade fermante, on referme l’onglet, on reprend son souffle et on se félicite d’avoir, pour l’instant, survécu avec ses mains et son cerveau intacts
1 commentaires
Commentaires Hacker News
Plutôt drôle. J’ai fait du JavaScript pendant environ 18 ans, et j’ai arrêté il y a à peu près 3 ans, donc la plupart des sources d’angoisse de cette liste datent des 10 dernières années
Il est particulièrement frappant qu’il manque les polyfills, pourtant incontournables si ce texte avait été écrit en 2013. Quelles autres horreurs JavaScript de 2013 ont aujourd’hui presque disparu ? Bon, elles ont certes été remplacées par cette liste amusante
Mon passage préféré, c’est quand on commence à compter les outils et parseurs qui fonctionnent dans la base de code, puis qu’on pense à TypeScript, esbuild, swc, babel, eslint, prettier, jest, webpack, rollup, terser, et que la douleur devient si grande qu’on ne veut même plus savoir s’il en manque encore
undefinedécrasé,with, les closures pour imiter les champs privés, les prototypes modifiés, les variables globales implicites,thisqui change selon le site d’appel, la coercition automatique de types lors des comparaisons…Oui, voilà à quoi ont ressemblé plus de 10 ans de JavaScript
SomeFunction.prototypeOn enveloppait ça dans une fonction anonyme auto-exécutée pour retourner un objet, afin de pouvoir appeler
new SomeFunction()ouSomeFunction.actualFunction()comme siSomeFunctionétait une classe. L’absence de fonctions fléchées pesait aussi lourd : pour lier les événements au scope, on modifiait carrémentFunction.prototypelui-même pour utiliser.apply(), ou bien on tombait dans un enfer sans fin de.bind(this)dans chaque callback d’événementAujourd’hui encore, j’ai eu la malchance de rédiger un exercice technique pour une entreprise qui utilise toujours du JavaScript pur, sans Promise ni fichiers de déclaration, et j’avais l’impression de revenir à ma première rencontre avec Node en 2012. L’exigence d’utiliser JavaScript a été contournée avec JSDoc, mais c’était bien pire que dans mon souvenir
Chaque fois que je lance un nouveau projet JavaScript — ou plus exactement un projet TypeScript — je suis toujours la même procédure
Comme c’est généralement du front-end, je choisis React, même si j’aimerais bien essayer Svelte aussi. Pour le bundler, avant c’était create-react-app ou un wrapper autour de Webpack, mais aujourd’hui Vite est la meilleure option grâce à sa configuration légère et à son support des principaux frameworks
J’installe et configure le compilateur TypeScript, en copiant-collant le plus souvent depuis un template. Je cible du JavaScript stable récent et des modules ESM, puis je configure eslint et prettier pour qu’ils s’accordent, en ajoutant aussi des plugins comme TypeScript et React. J’ajoute également des hooks pour lancer Prettier et eslint avant chaque commit
J’installe aussi des extensions d’IDE et je prépare un
.devcontainerou une liste d’extensions recommandées. Je me fiche du style en soi ; ce qui m’importe, c’est la cohérence du code et la détection d’erreurs, donc pour eslint j’utilise généralement une configuration existanteEnsuite, je mets en place les tests unitaires avec jest ou vitest, ce qui entraîne encore des réglages TypeScript et des règles eslint. Côté framework CSS, j’aime bien tailwind, alors j’installe aussi styled-components et twin.macro pour bien les intégrer à React, ainsi qu’un plugin Google Fonts ou une bibliothèque de polices
Il faut à peu près une journée avant de pouvoir commencer à programmer. Pourquoi y a-t-il autant de travail ? Tant de gens utilisent la combinaison Vite + TypeScript + React/Vue/Svelte + eslint/prettier + tailwind/bootstrap + jest/vitest ; pourquoi n’existe-t-il pas une bibliothèque sans configuration qui couvre directement 90 % des cas d’usage ?
Même quand j’arrivais à démarrer la chaîne d’outils d’une manière ou d’une autre, je n’avais jamais l’impression d’en avoir le contrôle ; au moindre petit à-coup, tout semblait prêt à s’effondrer, et j’avais le sentiment de ne pas pouvoir réparer quoi que ce soit
Récemment, j’ai décidé de voir l’essentiel de cette stack JavaScript comme un obstacle à ce que j’essaie de faire, et j’ai construit à la place une application à rendu côté serveur, à l’ancienne, avec Flask et Jinja2. Ce n’est pas moderne ni clinquant, et comme il n’y a pas encore de CSS, c’est assez laid pour l’instant, mais on m’a conseillé d’essayer avec du CSS moderne pur. Malgré tout, la vitesse à laquelle on peut créer des fonctionnalités avec cette stack est assez stupéfiante
package.json. Bon, je ne fais que du back-end, mais on pourrait peut-être se faire un template personnel de la même manière pour le front-end ?npm createne fait-il pas déjà ça ?Avec
npm create vue, on obtient une configuration Vue assez correcte et fonctionnelle, avec la plupart des réglages déjà en place. Je suis d’accord pour dire qu’il y a beaucoup de boilerplate et de bidouilles diverses, mais les applications qu’on construit aujourd’hui pour le web sont aussi bien plus grosses qu’avant. On peut toujours utiliser JavaScript avec seulement ESModules, sans outillage, mais si on ne le fait généralement pas, c’est parce que ces outils apportent réellement quelque chose. Ce n’est pas la manière la plus propre, mais ça fonctionneC’est encore un cas où les gens disent JavaScript, mais veulent en réalité dire « le bric-à-brac de l’écosystème Node »
Dès le départ, on te dit qu’il faut ouvrir et lire un fichier
package.json, et ensuite tout continue dans cette logique. Parmi les choses mentionnées, presque rien n’a vraiment à voir avec JavaScript lui-mêmeC’est vrai que JavaScript et NodeJS ne sont pas la même chose, mais la plupart ont sans doute compris ce que l’auteur voulait dire. Il aurait pu être plus précis, mais difficile de dire que c’est complètement faux
package.json. Tant qu’ECMA n’aura pas mis de gestionnaire de paquets ou de bundler dans la spécification officielle,package.jsonfera de fait partie de JavaScriptJ’aime bien le ton enjoué. Une fois qu’on s’est brûlé la peau et les terminaisons nerveuses sur plusieurs couches, c’est utile de se remémorer ce genre de point douloureux
Inversement, ça veut dire qu’on peut donner un MacBook neuf à un junior motivé et le jeter dans le grand bain, il sera productif en moins d’une journée. Pas besoin de connaître les conteneurs, .NET, nginx, ni 100 outils en ligne de commande courants ; quand il y a un problème, il suffit de chercher
Alors même qu’il sait qu’il pourrait construire le bateau entier en quelques jours s’il pouvait simplement y toucher directement, il se retrouve à réparer un bateau en bouteille avec les outils bancals fournis, en luttant contre des couches de complexité inutile. C’est exactement l’impression qu’on a quand on avance vite sur un projet perso en n’utilisant que JavaScript, HTML et CSS, puis qu’on se retrouve catapulté sur du frontend moderne
Ils ignorent naïvement qu’il faut des mois pour construire ça, et qu’une partie du problème vient justement de la surabondance de frameworks, si bien que le leur sera obsolète dès le lendemain
« J’ai suivi les instructions, puis il m’a dit que grunt n’était pas installé. Comme je suis vieux maintenant, je ne savais pas très bien ce qu’était grunt, et mon fichier grunt n’avait pas l’air correct non plus. Alors j’ai fait quelques recherches et j’ai compris ce qu’était grunt. Grunt, c’est… en fait je ne sais toujours pas bien ce que c’est. »
--Joe Armstrong, https://www.youtube.com/watch?v=lKXe3HUG2l4
Il y a quelques jours, j’ai exécuté
gem install wayback_machine_downloader. Je n’ai jamais assez aimé Ruby comme langage pour entrer dans son écosystème, mais vu de l’extérieur, cet écosystème semble avoir été maintenu avec une stabilité remarquable. gem est toujours l’outil qu’on utilise, et Rails est toujours làJ’attends un nouveau projet qui nécessitera un frontend assez complexe pour dépasser 0 à 30 lignes de JavaScript. À ce moment-là, je serai peut-être plus heureux avec HTMX qu’avec l’énorme framework le plus populaire du moment
Ce texte parle de npm, pas de JavaScript
Je déteste npm à cause de tout ce fatras, et je l’évite autant que possible. Personnellement, j’utilise Deno, et au travail on utilise une sorte de bazel basé sur un monorepo qui permet de se passer de npm
Mais je trouve que JavaScript/TypeScript est un langage agréable pour programmer, et c’est le premier langage que je choisis quel que soit le projet. Ça m’attriste quand les gens disent « JavaScript est nul » alors qu’ils veulent en fait dire que construire une app React avec npm est nul. Le développement JavaScript/TypeScript moderne est vraiment plaisant
J’ai l’impression que JavaScript et son écosystème nous ont tous laissé un PTSD, au point qu’on n’arrive plus à apprécier l’humour de ce texte
Il y a beaucoup trop de discussions « sérieuses » autour d’un texte drôle. On peut le disséquer autant qu’on veut, mais il faut se rappeler que l’intention première, c’est l’humour
Honnêtement, l’écosystème dans lequel j’ai commencé, c’était Node, et ce que j’ai appris ensuite avait aussi largement son lot de problèmes délirants
Haskell était très difficile à utiliser sur plusieurs machines, son installation sur MacOS restait pénible en 2019, et la gestion de l’écosystème était compliquée. Les macros et les extensions sont extrêmement confuses, et la rétrocompatibilité du langage n’est pas très bonne. À l’inverse, on peut généralement s’attendre à ce que du JavaScript de 2010 fonctionne encore tel quel en 2023
Même mettre en place correctement une image Docker ou un environnement comme codespace/gitpod est difficile, et les images de base fournies par GitHub ne sont pas terribles et posent problème
Le C est globalement correct sur la plupart des plateformes, mais le langage lui-même est franchement complexe et demande une connaissance énorme de l’ensemble du système. Le résultat, c’est qu’on produit facilement du code peu portable, inadapté à la plupart des usages de la programmation générale
Je pourrais continuer, mais quiconque a utilisé plusieurs langages sait que tous les écosystèmes ont divers défauts et que nous continuons tous à souffrir. Avec PHP, je n’ai jamais réussi à finir la configuration d’un projet en quelques jours, et Elm est moins douloureux, mais a ses propres problèmes avec son statut de dictateur bienveillant et la vitesse de développement
Je suis un développeur polyglotte qui se sent à l’aise avec plusieurs paradigmes et langages de programmation, aussi bien en frontend qu’en backend
Chaque fois que je vois un
package.jsondépasser 20 lignes, j’ai à peu près exactement cette réactionpackage.jsonde plus de 200 lignesJe récite à voix haute, comme pour repousser le diable. « Je ne dois pas avoir peur. La peur tue l’esprit. La peur est la petite mort qui mène à l’annihilation totale. J’affronterai ma peur. Je la laisserai passer sur moi et à travers moi. Et lorsqu’elle sera passée, je tournerai mon regard intérieur sur son chemin. Là où la peur sera passée, il n’y aura plus rien. Il ne restera que moi. »
En train de pleurer
On dirait un développeur JavaScript à l’ancienne en train de lire l’article, tourmenté par un PTSD et des flashbacks
Je ne dois pas avoir peur. La peur tue l’esprit. La peur est la petite mort qui mène à l’annihilation totale
J’affronterai ma peur. Je la laisserai passer sur moi et à travers moi. Et lorsqu’elle sera passée, je tournerai mon regard intérieur sur son chemin
Là où la peur sera passée, il n’y aura plus rien. Il ne restera que moi
Il finit l’article et réalise qu’il a survécu, mais il est déjà trempé de sueur