10 points par GN⁺ 2026-03-23 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • Dans l’écosystème npm, l’emballement de l’arbre de dépendances est pointé comme un problème majeur, dû au support d’anciens runtimes, aux structures de paquets atomiques et à l’usage de ponyfills obsolètes
  • De petits paquets utilitaires conservés pour la compatibilité avec des moteurs anciens et la sécurité cross-realm restent inutilement présents même dans des environnements modernes
  • L’architecture atomique visait à accroître la réutilisabilité, mais elle fonctionne en pratique comme une structure inefficace qui augmente les coûts de duplication, de sécurité et de maintenance
  • D’anciens paquets ponyfill pour des fonctionnalités déjà prises en charge par tous les moteurs ne sont pas supprimés, ce qui entraîne des téléchargements inutiles et une charge de gestion supplémentaire
  • La communauté pousse le nettoyage des dépendances inutiles et la bascule vers les fonctionnalités natives via des outils comme e18e, knip et module-replacements

Les trois axes de l’emballement des dépendances JavaScript

  • Avec la croissance de la communauté e18e, les contributions axées sur la performance se multiplient, et des activités de cleanup sont en cours pour retirer les paquets inutiles ou non maintenus
  • Dans l’écosystème npm, l’emballement de l’arbre de dépendances (dependency bloat) est identifié comme un problème majeur, dont les causes principales sont le support d’anciens runtimes, les structures de paquets atomiques et l’usage de ponyfills obsolètes

1. Support des anciens runtimes (y compris sécurité et realms)

  • L’arbre npm contient de nombreux petits paquets utilitaires comme is-string et hasown, maintenus pour trois raisons principales
    • le support de moteurs très anciens (par ex. ES3, IE6/7, premiers Node.js)
    • protection contre l’altération de l’espace de noms global

      • gestion des valeurs cross-realm
  • Support des anciens moteurs

    • Dans un environnement ES3, des fonctionnalités ES5 comme Array.prototype.forEach, Object.keys ou Object.defineProperty n’existent pas
    • Dans ce type d’environnement, il faut les implémenter soi-même ou utiliser un polyfill
    • La meilleure solution est de mettre à niveau l’environnement, mais certains utilisateurs conservent encore d’anciennes versions
  • Protection contre l’altération de l’espace de noms global

    • Node utilise en interne le concept de primordials pour encapsuler les objets globaux au démarrage et les protéger contre les altérations
    • Par exemple, si Map est redéfini, Node lui-même peut se retrouver cassé ; Node conserve donc des références vers les originaux
    • Certains mainteneurs de paquets appliquent aussi cette méthode à des paquets génériques, en utilisant des paquets centrés sur la sûreté comme math-intrinsics
  • Valeurs cross-realm

    • Lorsqu’un objet passe d’un iframe à un autre, les vérifications instanceof peuvent échouer
    • Exemple : window.RegExp !== iframeWindow.RegExp
    • Des frameworks de test comme chai utilisent la méthode Object.prototype.toString.call(val) pour effectuer des vérifications de type entre realms
    • Des paquets comme is-string existent pour cette compatibilité cross-realm
  • Problèmes

    • La plupart des développeurs utilisent aujourd’hui Node moderne ou des navigateurs evergreen, donc cette compatibilité n’est pas nécessaire
    • Pourtant, ces paquets se retrouvent dans le « hot path » des arbres de dépendances génériques, ce qui fait que tout le monde en paie le coût

2. Architecture atomique

  • Certains développeurs estiment qu’il faut découper les paquets en unités aussi petites que possible pour construire des briques réutilisables
  • On se retrouve ainsi avec de nombreux paquets extrêmement granulaires comme shebang-regex, arrify, slash, path-key, onetime ou is-wsl
  • Exemple : shebang-regex ne contient qu’une seule ligne de regex (/^#!(.*)/)
  • Problèmes

    • La plupart des paquets atomiques sont peu réutilisés, voire n’ont qu’un seul consommateur
    • Exemples :
      • shebang-regex → utilisé uniquement par shebang-command
      • cli-boxes → utilisé uniquement par boxen et ink
      • onetime → utilisé uniquement par restore-cursor
    • Dans ces cas, cela revient à du code inline, mais avec les coûts supplémentaires des requêtes npm, de la décompression et de la bande passante
  • Problème de duplication

    • Exemple : dans l’arbre de dépendances de nuxt@4.4.2, is-docker, is-stream, is-wsl et path-key existent en double version
    • En les remplaçant par du code inline, il n’y a plus de conflit de versions ni de coût de résolution, donc le coût de duplication devient quasi nul
  • Extension du risque supply chain

    • Plus il y a de paquets, plus les risques de sécurité et de maintenance augmentent
    • Il y a déjà eu des cas où un mainteneur gérait de nombreux petits paquets, puis s’est fait compromettre son compte, ce qui a endommagé des centaines de paquets en même temps
    • Un code simple comme Array.isArray(val) ? val : [val] peut être géré inline sans nécessiter un paquet séparé
  • Conclusion

    • L’architecture atomique a fini, à rebours de son intention initiale, par se transformer en structure inefficace et risquée
    • Sans bénéfice concret pour la plupart des utilisateurs, tout l’écosystème en supporte le coût

3. Ponyfills obsolètes

  • Un polyfill est du code qui ajoute à un environnement des fonctionnalités qu’un moteur ne prend pas en charge, tandis qu’un ponyfill est une implémentation alternative utilisée via import direct sans modifier l’environnement
  • Exemple : @fastly/performance-observer-polyfill fournit à la fois un polyfill et un ponyfill
  • Problèmes

    • Les ponyfills ont été utiles par le passé, mais ne sont pas retirés même lorsque la fonctionnalité cible est déjà prise en charge par tous les moteurs
    • Exemples :
      • globalthis (pris en charge depuis 2019, 49 millions de téléchargements hebdomadaires)
      • indexof (pris en charge depuis 2010, 2,3 millions de téléchargements hebdomadaires)
      • object.entries (pris en charge depuis 2017, 35 millions de téléchargements hebdomadaires)
    • Ces paquets subsistent dans la plupart des cas simplement parce qu’ils n’ont pas été supprimés
    • Dès que tous les moteurs LTS prennent en charge une fonctionnalité, le ponyfill devrait être retiré

Comment réduire cet emballement

  • En raison de l’imbrication profonde des arbres de dépendances, le nettoyage est difficile, mais il reste possible grâce à la coopération de la communauté
  • Chaque développeur devrait se demander : « Ce paquet est-il vraiment nécessaire ? » et, si ce n’est pas le cas, ouvrir une issue ou chercher un paquet de remplacement
  • Le projet module-replacements fournit une liste de paquets remplaçables par des fonctionnalités natives
  • Utiliser knip

    • knip est un outil de détection des dépendances inutilisées et du code mort
    • Ce n’est pas une solution directe, mais il constitue un bon point de départ pour le nettoyage
  • Utiliser le CLI e18e

    • La commande @e18e/cli analyze permet de détecter les dépendances remplaçables
    • Exemple : migration automatique de chalk vers picocolors
    • À l’avenir, l’outil devrait aussi recommander des fonctionnalités natives comme styleText de Node selon l’environnement
  • Utiliser npmgraph

    • npmgraph.js.org est un outil de visualisation des arbres de dépendances
    • Exemple : dans l’arbre de eslint@10.1.0, la branche find-up est isolée
    • Une simple fonction d’exploration de fichiers n’a pas besoin de 6 paquets ; il est donc possible d’utiliser une alternative plus légère comme empathic
  • Projet module replacements

    • La communauté maintient un jeu de données qui associe paquets remplaçables et fonctionnalités natives
    • Des migrations automatiques sont également prises en charge via le projet codemods

Conclusion

  • L’emballement actuel fait que tout le monde supporte le coût pour répondre aux besoins d’une minorité qui veut conserver une compatibilité ancienne ou des structures atypiques
  • C’était autrefois inévitable, mais c’est aujourd’hui une charge inutile, alors que les moteurs et API modernes ont largement progressé
  • À l’avenir, cette minorité devrait maintenir sa propre pile séparée, tandis que le reste de l’écosystème devrait évoluer vers une base de code légère et moderne
  • Des projets comme e18e et npmx soutiennent déjà cette transition via la documentation et l’outillage, et chaque développeur devrait aussi examiner ses dépendances et se demander : « Pourquoi est-ce nécessaire ? »
  • Tout le monde peut participer au nettoyage

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.