7 points par GN⁺ 2025-05-10 | 3 commentaires | Partager sur WhatsApp
  • Le système de gestion des dépendances de Rust facilite le développement, mais le nombre de dépendances et les problèmes de qualité deviennent préoccupants
  • Même un crate bien utilisé peut ne plus être à jour, au point qu’il vaut parfois mieux l’implémenter soi-même
  • Après avoir ajouté des crates populaires comme Axum et Tokio, le nombre total de lignes de code, dépendances incluses, atteint 3,6 millions, ce qui devient difficile à gérer
  • Le code que j’ai réellement écrit ne représente qu’environ 1 000 lignes, mais il est en pratique impossible de revoir et d’auditer tout le code autour
  • Il n’existe pas de solution claire concernant une éventuelle extension de la bibliothèque standard de Rust ni sur la manière d’implémenter les infrastructures essentielles, et toute la communauté doit réfléchir ensemble à l’équilibre entre performances, sécurité et maintenabilité

Aperçu du problème des dépendances dans Rust

  • Rust est mon langage préféré, et sa communauté ainsi que son ergonomie sont excellentes
  • La productivité de développement est élevée, mais j’ai récemment commencé à m’inquiéter de la gestion des dépendances

Les avantages des crates Rust et de Cargo

  • Cargo permet la gestion des paquets et l’automatisation du build, ce qui améliore fortement la productivité
  • Il est facile de passer d’un système d’exploitation ou d’une architecture à l’autre, sans devoir gérer soi-même les fichiers ni la configuration des outils de build
  • On peut se mettre à écrire du code immédiatement, sans se soucier d’une gestion de paquets séparée

Les inconvénients de la gestion des crates Rust

  • En prêtant moins attention à la gestion des paquets, on devient aussi moins vigilant sur la stabilité
  • Par exemple, j’utilisais le crate dotenv, avant d’apprendre via une Security Advisory que sa maintenance avait été abandonnée
  • En envisageant un crate de remplacement (dotenvy), j’ai finalement choisi d’implémenter directement uniquement la partie nécessaire, en une trentaine de lignes
  • Comme les problèmes de paquets non maintenus sont fréquents dans de nombreux langages, le vrai fond du problème est une dépendance inévitable aux dépendances externes

L’explosion du volume de code provoquée par les dépendances

  • J’utilise des paquets importants et de bonne qualité de l’écosystème Rust, comme Tokio et Axum
  • J’ai ajouté comme dépendances Axum, Reqwest, ripunzip, serde, serde_json, tokio, tower-http, tracing et tracing-subscriber
  • Le but principal est d’avoir un serveur web, la décompression de fichiers et des fonctions de log, donc le projet lui-même reste simple
  • J’ai utilisé la fonctionnalité Cargo vendor pour télécharger localement tous les crates dépendants
  • En analysant le nombre de lignes avec tokei, j’arrive à environ 3,6 millions de lignes dépendances incluses (environ 11 136 lignes sans les crates vendorisés)
  • À titre de comparaison, on dit que le noyau Linux entier fait environ 27,8 millions de lignes, ce qui signifie que mon petit projet représente déjà un septième de ce volume
  • Le code que j’ai réellement écrit ne fait qu’environ 1 000 lignes
  • Surveiller et auditer un tel volume de code dépendant est en pratique impossible

Réflexions sur les solutions possibles

  • Pour l’instant, il n’y a pas de solution évidente
  • Certains proposent, comme en Go, d’étendre la bibliothèque standard, mais cela crée aussi de nouveaux problèmes comme la charge de maintenance
  • Rust vise la haute performance, la sécurité et la modularité, avec l’ambition de convenir à l’embarqué et de rivaliser avec le C++, donc l’élargissement de la bibliothèque standard doit être envisagé avec prudence
  • Par exemple, même un runtime avancé comme Tokio est géré de manière très active sur Github et Discord
  • En pratique, implémenter soi-même des infrastructures essentielles comme un runtime asynchrone ou un serveur web dépasse les capacités d’un développeur individuel
  • Même un grand service comme Cloudflare utilise directement tokio et les dépendances de crates.io, sans que l’on sache à quelle fréquence les audits sont réalisés
  • Clickhouse a lui aussi évoqué les problèmes liés à la taille des binaires et au nombre de crates
  • Avec Cargo, il est difficile d’identifier précisément les lignes de code réellement incluses dans le binaire final, et il subsiste aussi du code inutile selon les plateformes
  • Au final, il ne reste qu’à poser la question à l’ensemble de la communauté

3 commentaires

 
codemasterkimc 2025-05-11

Quand on passe Trivy, il y a bien moins de vulnérabilités high ou critical qu’avec js NPM ou Java Maven, donc de quel argument sur Rust cet article essaie-t-il de se servir ?

 
GN⁺ 2025-05-10
Avis Hacker News
  • À mon avis, tout système où il est « facile » d’ajouter des dépendances sans pénalité de taille ou de coût finit inévitablement par tomber dans un problème de dépendances. Si on regarde la manière dont les logiciels ont été distribués ces 40 dernières années, dans les années 80 on achetait les bibliothèques, et dans des environnements contraints en taille on ne gardait que les morceaux nécessaires. Aujourd’hui, on empile bibliothèque sur bibliothèque. On peut écrire une seule ligne import foolib, et personne ne se soucie de ce qu’il y a dedans. À chaque étape, on n’a besoin que d’environ 5 % des fonctionnalités, mais plus l’arbre est profond, plus le code inutile s’accumule. Au final, un simple binaire finit par faire 500 MiB, et on importe une dépendance juste pour formater un nombre. Go et Rust encouragent à tout mettre dans un seul fichier, ce qui devient gênant quand on ne veut utiliser qu’une partie. À long terme, la seule vraie solution serait un suivi ultra-fin des symboles et des dépendances, où chaque fonction/type déclare exactement ce dont il a besoin, pour ne garder que le code strictement nécessaire et jeter le reste. Personnellement, je n’aime pas trop cette idée, mais je ne vois pas d’autre moyen de résoudre le système actuel qui aspire tout l’univers depuis l’arbre de dépendances
    • Je suis peut-être à côté de la plaque en tant qu’étudiant, mais le compilateur Rust détecte déjà le code, les variables et les fonctions inutilisés. La plupart des IDE savent aussi le faire dans beaucoup de langages. Du coup, il ne suffit pas de supprimer ces parties ? Le code inutilisé n’est pas compilé
    • En pratique, j’ai essayé de tailler via des feature flags en travaillant sur une bibliothèque Rust avec un arbre de dépendances relativement lourd (Xilem), et presque toutes les dépendances devaient rester selon les fonctionnalités nécessaires (vulkan, décodage PNG, unicode shaping, etc.). Les dépendances inutiles étaient surtout de toutes petites choses, et je n’ai retiré que serde_json avec une modification mineure. Les plus grosses dépendances (winit/wgpu, etc.) demanderaient des changements d’architecture, donc on ne peut pas les retirer facilement
    • Go et C# (.NET) sont de bons contre-exemples. Ils ont une gestion de paquets et un écosystème aussi efficaces que Rust ou JS (Node), mais relativement peu de dependency hell. C’est parce que leurs bibliothèques standard sont excellentes. L’ampleur d’une bibliothèque standard est le genre d’investissement que seules de très grosses entreprises (Google, Microsoft) peuvent se permettre
    • Alors pourquoi les compilateurs actuels ne suppriment-ils pas le code inutilisé ?
    • Avant, on produisait un fichier .o par fonction, puis on les regroupait dans une archive .a, et l’éditeur de liens ne prenait que les fonctions nécessaires. Le namespacing se faisait avec des formes comme foolib_do_thing(). Aujourd’hui, c’est plutôt le pattern du god object, avec toutes les fonctions sur un objet de haut niveau, donc un simple import foolib attire tout. Dans cet état, il est difficile pour l’éditeur de liens de savoir quelles fonctions sont vraiment nécessaires. En revanche, Go est excellent pour l’élimination du code mort, donc ce qui n’est pas utilisé est bien retiré du binaire
    • Les compilateurs et éditeurs de liens modernes font déjà de l’extraction de symboles et de l’élimination de code mort, et Rust le prend aussi en charge via des projets comme min-sized-rust
    • Avant, on gérait tout en intégrant directement toutes les bibliothèques dans le projet et dans les fichiers de build. C’était lourd et pénible, mais ça forçait à une compréhension bien plus profonde qu’ajouter une ligne dans un fichier de deps
    • En réalité, Go n’impose pas du tout le fichier unique ; il permet aussi très facilement un découpage logique en plusieurs fichiers. C’est un point que j’apprécie vraiment
    • Dotnet met déjà cette idée en pratique avec le Trimming et l’Ahead Of Time Compilation. Les autres langages pourraient apprendre de Dotnet
    • Avec le LTO (Link Time Optimization), ce problème est entièrement résolu du point de vue de la taille du binaire. Les parties inutilisées sont supprimées à l’optimisation. Le temps de build, lui, reste un problème
    • Pour moi, le vrai problème n’est pas tant la bibliothèque elle-même que le manque de visibilité, après ajout d’une dépendance, sur ce qui est utilisé à l’intérieur et dans quelle proportion. Il faudrait un environnement qui fournisse facilement un retour sur les performances de chaque paquet, la part de code superflu au build, etc.
    • Le langage Unison introduit quelque chose de partiellement similaire à cette idée. Chaque fonction y est définie selon une structure d’AST, puis chargée et réutilisée depuis un registre global basé sur des hashes
    • Plutôt qu’une maintenance dispersée de petits fragments de bibliothèques comme isEven, isOdd, leftpad dans npm, une grosse bibliothèque générique maintenue par une équipe fédérée offre bien plus de garanties d’avenir et de continuité
    • Au lieu de viser un suivi ultra-fin des symboles/dépendances, on peut aussi imaginer des modules ultra-fins et l’exploitation des systèmes de tree-shaking existants
    • La gestion réelle des dépendances en Go est en fait assez proche de l’idéal décrit dans le billet d’origine. Un module est un ensemble de paquets, et lors du vendoring on n’inclut que les paquets et symboles réellement utilisés (je ne sais pas si ça fonctionne exactement au niveau des symboles)
    • Le système de modules JS prend justement en charge ce genre de gestion ultra-fine des symboles et le tree shaking
    • L’idée de dépendances ultra-fines proposée au départ est déjà couverte en Rust par le découpage de sections, comme avec --gc-sections
    • Rust est un langage où les imports fins fonctionnent très bien via le découpage d’API par crate feature. Ce n’est pas comme Go
    • Selon l’architecture, par exemple avec un thick client centré sur le local, même 800 Mo à la première installation ne posent pas problème si à l’usage le trafic réseau reste extrêmement limité. Dans l’UI, on peut aussi tolérer de grosses dépendances répétitives pour la collaboration
    • La meilleure façon de réutiliser du code, c’est précisément d’utiliser des dépendances. Il faut n’optimiser que là où c’est vraiment nécessaire
    • Dès les années 80, l’idée de composants logiciels réutilisables avait déjà été concrétisée via des langages comme Objective-C. Un des grands succès de Rust est d’avoir largement diffusé cette approche par composants logiciels jusque dans les langages de programmation système
    • Le tree shaking peut régler en partie les problèmes de taille et de gonflement du code (sur serveur, on s’en moque carrément). Le problème plus grave, c’est le risque de supply chain et la sécurité des dépendances. Plus l’entreprise est grande, plus il existe des processus d’approbation pour l’usage d’open source. Augmenter seulement la granularité ne change rien si 1 000 fonctionnalités viennent de 1 000 auteurs NPM différents
    • Si chaque couche d’abstraction d’un paquet n’est utilisée qu’à 50 %, alors à chaque niveau la taille totale devient le double du strict nécessaire. En trois couches, 88 % du code est inutile. Exemple : la calculatrice de Windows 11 embarque des bibliothèques absurdes, jusqu’à un outil de récupération de compte. C’est un cas où la facilité d’ajout de fonctionnalités se traduit en complexité croissante
    • Je suis d’accord sur le fait que l’accumulation de dépendances est un problème. À ce stade, la meilleure défense consiste à être extrêmement strict sur les dépendances système. Il m’arrive même de copier-coller directement du code au lieu d’importer une bibliothèque externe pour une fonction de 10 lignes. Un écosystème de bibliothèques sain est l’exception. J’arrête souvent immédiatement des ingénieurs juniors qui ajoutent des dépendances sans discernement
    • Ça faisait longtemps que je n’avais pas vu quelqu’un affirmer des choses aussi catégoriques sans même connaître les bases de Rust
    • Grâce à l’élimination du code mort, dans un langage compilé comme Rust, un gros arbre de dépendances ne se traduit pas par un gonflement du binaire
  • Le problème que je ressens dans l’écosystème npm, c’est que beaucoup de développeurs ajoutent des dépendances sans vraie réflexion de conception. Par exemple, une bibliothèque glob devrait juste fournir une fonction de globbing simple, mais son auteur y ajoute aussi un outil en ligne de commande et donc une grosse dépendance de parsing. Cela provoque des alertes fréquentes de type « dependency out-of-date ». Il y a aussi débat sur la responsabilité de la bibliothèque glob. Faire uniquement du matching de motifs sur des chaînes est une conception plus souple (plus simple à tester, à abstraire du système de fichiers). Beaucoup d’utilisateurs veulent toutefois une bibliothèque omnipotente qui « fait tout », mais plus c’est le cas, plus les effets secondaires sont importants. Je pense que Rust n’est pas si différent
    • Le sens de la conception est important, et un bon langage ne favorise ni ne freine vraiment ces préférences de développeurs. Rust, Zig, C en sont des exemples. Le problème s’y produit statistiquement moins souvent. Quand une « foule » de développeurs se rassemble, on finit par obtenir un « modèle de bazar » où chacun empile librement des crates. À terme, j’aimerais que Rust dispose lui aussi d’une vraie bibliothèque standard officielle — par exemple stdlib::data_structures::automata::weighted_finite_state_transducer — avec des espaces de noms bien ordonnés et un ensemble « batteries included ». Le langage intègre déjà la gestion des versions et la compatibilité descendante, donc on peut espérer des améliorations
    • La fonction glob de POSIX parcourt réellement le système de fichiers. Pour le simple matching sur chaîne, il y a fnmatch. L’idéal serait que fnmatch soit dans un module séparé et soit une dépendance de glob. Si on essaie d’implémenter glob soi-même, c’est en réalité assez difficile : structure de répertoires, brace expansion, etc. Il faut donc une bonne composition de fonctions bien conçues
    • En Rust, le borrow checker a servi de sorte de bouclier contre les développeurs ayant moins de sens de la conception. On ne sait pas combien de temps cet effet durera
    • Un des grands avantages de Rust, c’est que les développeurs y sont globalement compétents et que la qualité des crates est plutôt élevée
    • Bun inclut aussi une fonctionnalité de glob
  • Pas besoin de cibler Rust de façon particulière : les problèmes de dépendances et les attaques de supply chain sont déjà une réalité partout. Si l’on concevait un nouveau langage, il faudrait intégrer un « capability system » permettant d’isoler en toute sécurité l’ensemble de l’arbre de bibliothèques. Par exemple, si l’on conçoit une bibliothèque de chargement d’images, elle ne devrait traiter qu’un flux et non un fichier, ou bien on devrait pouvoir lui déclarer explicitement « pas le droit d’ouvrir des fichiers », afin de bloquer au moment de la compilation l’usage de fonctions dangereuses. Dans les écosystèmes existants, ce n’est pas simple, mais bien fait, cela permettrait de réduire au minimum la surface d’attaque. Une culture de minimisation des dépendances ne suffit pas à régler le problème de fond, et des langages comme Go ne sont pas non plus à l’abri des attaques de supply chain
    • Il faudrait diffuser activement la culture du Sans-IO, c’est-à-dire d’une conception où les dépendances ne font pas d’I/O directement. Lorsqu’une nouvelle bibliothèque sort, il faudrait aussi pointer du doigt les implémentations qui font leurs propres I/O. Bien sûr, l’examen public ne suffira pas à lui seul, mais ce serait déjà bien si le principe Sans-IO se généralisait
    • Il existe par exemple WUFFS, un langage très spécialisé. En pratique, il ne peut même pas afficher « Hello world » et n’a pas de type chaîne de caractères. À la place, il est entièrement dédié au parsing de formats de fichiers non fiables. Il faudrait davantage de langages spécialisés de ce genre. Ils sont rapides, sans danger, et peuvent donc réduire les vérifications inutiles
    • Java et .NET Framework disposaient déjà il y a des décennies de mécanismes de partial trust / capabilities, mais ils n’ont pas été largement utilisés et ont fini abandonnés
    • On retrouve un peu la même tendance en Rust. Avec #![deny(unsafe_code)], on peut provoquer une erreur de compilation si du code unsafe est utilisé, et informer l’utilisateur de ce fait. Ce n’est toutefois pas une contrainte absolue, puisqu’on peut toujours autoriser explicitement du code unsafe. On peut imaginer l’introduction d’un capability system permettant d’ajuster transitivement les fonctionnalités de la bibliothèque standard, un peu comme des feature flags
    • J’aimerais construire moi-même quelque chose de ce genre, et j’espère que cela existera un jour. En Rust, un suivi partiel des capabilities est déjà possible via des linters. Les problèmes d’unsoundness du compilateur doivent cependant être résolus
    • Il est difficile d’introduire une contrainte statique parfaite dans un langage ou un écosystème existant, mais même une vérification à l’exécution permettrait d’obtenir la plupart des bénéfices. Si l’on compile le code des bibliothèques depuis les sources, on peut interposer un wrapper de contrôle des permissions à chaque syscall. En cas de violation, on déclenche un panic, et il faudrait produire et distribuer un profil de capabilities par bibliothèque. Quelque chose de proche a déjà été démontré dans l’écosystème TypeScript
    • Haskell réalise déjà partiellement cette approche via la monade IO. Une fonction qui ne peut pas faire d’I/O est contrainte par sa signature de type
    • À mon sens, il faudrait changer de fond en comble la manière dont ce système communique avec l’OS. Même lire un flux est piégeux, parce qu’en réalité on peut toujours utiliser des syscalls de lecture de fichier
    • Il existe un projet appelé Capslock qui se rapproche de cette approche en Go
    • Si l’on interdit aux bibliothèques d’importer les API système dès le programme d’entrée, la transmission des capabilities peut se faire uniquement par injection de dépendances. On peut le concevoir même dans les langages actuels, mais en pratique le vrai problème est la rupture de compatibilité avec les bibliothèques existantes
    • Je me demande si quelque chose de similaire a déjà été implémenté auparavant. Cela me paraît très difficile à appliquer aux langages actuels
    • Un seul langage ne suffit pas ; il faut un écosystème multilingue
    • Dans l’écosystème TypeScript, par exemple, si l’environnement ne fournit pas de classe pour manipuler des fichiers, la compilation échoue et la limitation s’applique naturellement
  • C’est un problème général du développement logiciel moderne. La barrière d’entrée est basse, et la réutilisation du code existant est de plus en plus forte. Une dépendance reste au final du code non fiable. S’il n’existe pas de solution technique, quelqu’un devra continuer à faire la revue de code, la maintenance, et à entretenir des systèmes de confiance sociaux et juridiques. Si on intègre cela dans la stdlib de Rust, l’équipe centrale doit alors assumer la responsabilité de tout ce code, ce qui accroît fortement la charge de maintenance
    • Selon les langages, la gravité apparente diffère. Les langages dotés d’une bibliothèque standard puissante peuvent faire beaucoup avec un minimum de dépendances externes, ce qui est un avantage. Dans des langages comme JS/Node, où les fonctionnalités de base sont limitées, les dépendances externes deviennent la norme. La « légèreté » n’est pas toujours une bonne chose
    • Je pense qu’il faudrait davantage d’intégration dans la bibliothèque standard de Rust. Go a une excellente stdlib, alors qu’en Rust, même pour des fonctionnalités de base (web, tls, x509, base64, etc.), le choix et la gestion des bibliothèques restent pénibles
    • Gilad Bracha a proposé une approche intéressante pour le sandboxing des bibliothèques tierces : supprimer les imports et tout faire par injection de dépendances. Si l’on n’injecte pas un sous-système d’I/O, le code tiers ne peut tout simplement jamais y accéder. Si l’on veut seulement accorder la lecture, on peut n’injecter qu’un wrapper de lecture. Il y a toutefois des limites dans le domaine de la programmation système (à cause du code unsafe, etc.)
    • On a aussi proposé une structure façon QubesOS : toutes les bibliothèques tournent dans des environnements isolés, son propre code dans dom0, chaque bibliothèque dans une VM modèle distincte, avec communication via des espaces de noms réseau. Dans des secteurs sensibles, cela pourrait être pratique
    • D’après ce que j’observe, nous ne faisons pas des choses plus complexes ; nous faisons les mêmes choses de façon plus compliquée. Les objectifs eux-mêmes ne sont pas devenus plus difficiles
    • En réalité, la situation varie selon les langages. En C/C++, ajouter une dépendance est difficile, et si l’on veut en plus du support multiplateforme c’est bien plus pénible, donc le même problème se pose moins
    • Le vrai problème, c’est le gonflement inutile de complexité. Presque tous les projets débordent de complexité inutile et de surconception. C’est le mal de cette industrie
  • blessed.rs recommande une liste de bibliothèques utiles difficiles à intégrer à la bibliothèque standard. J’aime ce système, car il permet de garder la plupart des paquets limités à des usages précis et donc gérables
    • cargo-vet mérite aussi d’être recommandé. Il permet de suivre et de définir les paquets de confiance, depuis ceux qui doivent être audités par des experts avant import jusqu’à des politiques semi-YOLO du genre « les paquets maintenus par les mainteneurs de tokio, on leur fait confiance ». C’est un peu plus formel que blessed.rs et c’est un bon moyen de partager en équipe une liste officielle ou quasi officielle de références
    • Ce serait vraiment bien d’avoir un système de ce genre aussi pour Python
    • Après l’avoir examiné, je trouve que c’est effectivement un très bon projet de recommandation
  • Depuis l’affaire leftpad, il reste une mauvaise image des gestionnaires de paquets. Quelque chose comme tokio est en pratique une fonctionnalité de niveau langage, donc si l’OP estime qu’il faudrait auditer soi-même l’ensemble de Go ou même le V8 de Node, ce n’est pas réaliste
    • En pratique, tokio aussi est audité en continu par quelqu’un. Ce n’est pas fait par tout le monde, mais quelqu’un s’en charge malgré tout
    • Le fait que cargo inclue les deux versions lorsqu’il y a deux dépendances vers des versions différentes est un support assez particulier à cargo
  • Les feature flags des paquets cargo sont vraiment excellents. J’ouvre souvent des PR pour cacher des dépendances inutiles derrière ces flags. Avec cargo tree, on peut voir très facilement l’arbre de dépendances. Une vue des lignes de code qui finissent réellement dans le binaire n’a pas beaucoup de sens, car avec l’inlining des fonctions, la plupart finissent absorbées dans main
    • C’est dommage que npm n’ait pas de feature flags. Je me demande s’il existe déjà un gestionnaire de paquets qui le prend en charge. J’aimerais pouvoir isoler, dans une bibliothèque interne, le code dépendant d’un framework donné pour l’étendre
  • Je suis pareil. Ajouter des dépendances avec Cargo est tellement facile que, même si moi-même je reste prudent, quelques ajouts suffisent pour entraîner des dizaines de dépendances transitives. Cela ne veut pas dire qu’il soit réaliste de s’en passer. En C++, ce phénomène est moins visible. En Rust, avec son fort découpage en petits paquets, on a l’impression de récupérer du code aléatoire sur Internet. J’aime Rust lui-même, mais je n’aime pas cette structure
    • Dans le billet lié depuis le subreddit Rust, il est expliqué que si les dépendances sont moins visibles en C++, c’est surtout parce qu’elles sont en général fournies sous forme de bibliothèques dynamiques. D’une certaine manière, le fait de s’appuyer sur la capacité du gestionnaire de paquets de l’OS à assurer stabilité et sécurité est aussi un avantage. Rust gagnerait à introduire au moins une forme de bibliothèque standard étendue
    • Les dépendances C++ sont complexes et les systèmes de build sont désastreux, donc je préfère des dépendances à la Rust, même moins stables. En pratique, les dépendances transitives en C++ sont encore moins visibles parce qu’elles sont précompilées
    • En Rust, ce découpage en petits paquets n’est pas tant une « philosophie » qu’une conséquence de la vitesse de build. Quand un projet grossit, on le découpe en crates. Ce n’est pas pour l’abstraction, mais parce que les performances de compilation y poussent
    • Il ne faut pas forcément accepter sans réfléchir l’argument « alors n’en utilise pas ». Cela mérite d’être creusé davantage
    • C++ et CMake sont tellement difficiles que, dans la pratique, cela conduit aussi beaucoup de logiciels à ne pas être utilisés du tout
  • Pour les bibliothèques centrales, j’utilise des bibliothèques open source, et pour les petites fonctionnalités je m’inspire de l’open source pour copier-coller directement le code dans mon projet. Le code grossit un peu inutilement, mais cela réduit la charge de revue du code externe et l’exposition à la supply chain. Les grosses bibliothèques restent malgré tout un problème, mais on ne peut pas tout écrire soi-même. Ce n’est pas un problème propre à Rust, c’est un problème général
  • Dans le passé, dans d’autres langages, sur des systèmes importants, j’appliquais une politique stricte de minimisation des modules/paquets, et je copiais tous les paquets utilisés dans un dépôt interne, avec audit à chaque branche et à chaque mise à jour. Dans des domaines comme le frontend, une telle rigueur est pratiquement impossible. Récemment, les outils et modèles d’IA open source très médiatisés soulèvent les mêmes questions côté gestion des dépendances. Même dans mes projets personnels en Rust, ce qui me gêne le plus reste l’explosion des dépendances autour des bibliothèques UI/async. Il suffit qu’une seule devienne vulnérable pour ouvrir une brèche, ce n’est qu’une question de temps
    • En pratique, il est raisonnable de ne connecter le système de CI/CD qu’au dépôt interne officiel. Les développeurs peuvent installer ce qu’ils veulent en local, mais les commits non autorisés sont bloqués par le serveur de build
    • Il existe des RFC visant à traiter les risques de sécurité, mais pour des raisons culturelles (sans doute), il n’y a pas de changement brutal
    • Ce qui est formidable avec Rust, c’est que l’async peut aussi être implémenté soi-même de la manière souhaitée. On n’est pas lié à une implémentation particulière
 
iolothebard 2025-05-11

Ce n’est pas un problème propre à Rust.
C’est à la fois un avantage commun et un problème potentiel partagé par tous les langages qui ont un gestionnaire de paquets avec un dépôt public de paquets et la prise en charge des dépendances transitives.
Au final, tout dépend de la façon dont ceux qui les utilisent s’en servent…
Malgré l’affaire leftpad de Node&npm, rien n’a changé.