Maintenance des dépendances
[edit 19/04/2019]
L'excellent mais britanique — à moins que ce soit le britanique mais excellent #justkidding — Jake Archibald raconte : il a écrit un site web en 2011 qu'il ne maintient plus vraiment. Un matin, un internaute le contacte pour lui demander pourquoi des bitcoins ou autres sont minés sur son site sans consentement.
« it feels negligent to use packages you haven't audited »
Edito
Je suis en pleine réflexion sur la meilleure façon d'organiser les bibliothèques des projets de mon équipe.
Un de mes collègues a mis en place renovate pour mettre à jours les dépendances automatiquement, ce qui me parait très optimiste : vous devez avoir une bonne confiance pour mettre à jour une dépendance sans risquer de casser le code. Et ce même si vous configurez renovate pour ne mettre à jours les dépendances que lorsqu'une version mineure ou un bugfix est déployé en espérant que l'auteur utilise correctement semver.
Renovate bruite mes notifications github, surtout lorsque les dépendances sont des bibliothèques internes partagées par plusieurs projets et qui sont souvent mises à jour en phase de développement.
Il y a comme une odeur d'un truc qui ne va pas. Et j''aimerai m'expliquer ci-dessous.
Une histoire
Voici une dépendance qui a coûté cher à l'ensemble du web : left-pad (stevemao/left-pad).
Note : je ne parle pas de left_pad, Henry Zhu, de qui vous pouvez parler à votre employeur si vous utilisez babeljs ! Merci à toute l'équipe de maintenir un tel projet essentiel, dépendances de beaucoup de nos applications.
Voici donc deux exemples de dépendances que les développeurs peuvent utiliser dans leurs productions. Quelles versions utilisez-vous dans vos applications ? Êtes-vous à jour ? Est-ce qu'il y a des failles de sécurité ou des bugs qui sont dûs aux bibliothèques que vous utilisez ?
Intérêt des dépendances
Une dépendance est une bibliothèque (librairy) que vous utilisez dans votre code. Elle est pratique à plusieurs égards :
- ne pas refaire la roue
- mutualiser les efforts
- centraliser la maintenance
Pour autant ce n'est pas forcément la peine de créer une bibliothèque pour implémenter du code simple comme ajouter des caractères à gauche d'une chaine de caractères :
const leftPad = (text, size, char = ' ') =>
text.length > size
? text.slice(size)
: [...Array(size - text.length)]
.reduce((acc, item) => [char, ...acc], text.split(''))
.join('');
repl.it/WavyMinorCryptocurrency
Certains projets ont néanmoins ajouté le paquet left-pad
en tant que dépendance : jsdom
dépend de cette bibliothèque.
Mise à jour des dépendances
Quelle soit centrale ou utilitaire, une dépendance va avoir un impact sur l'exécution de notre code, sinon pourquoi l'utiliser.
Les versions des bibliothèques peuvent changer indiquant par là même une mise à jour du code. Ce code peut avoir été mis à jour pour régler un problème d'exécution (bug) ou pour ajouter une fonctionnalité (feature). Sans changelog, sans code source accessible, difficile de savoir ce qui a changé lorsque la version d'une bibliothèque a changé.
D'ailleurs, cela reste vrai que le mainteneur utilise semver ou pas : il faut vérifier ce qui est mis à jour avant d'appliquer cette mise à jour à notre propre projet.
Mais quand faut-il mettre à jour une bibliothèque ?
Il faut suivre les annonces pour savoir si une mise à jour permet de réparer un bug duquel souffrait également notre application principale. Il faut suivre les annonces pour savoir si une faille a été découverte et si une mise à jour est disponible pour palier la faille.
Tout cela est bien fastidieux, surtout si notre application a de nombreuses dépendances. Et après tout, pourquoi perdre du temps si il n'y a aucun comportement (runtime) inadapté à signaler ? On ne change pas une équipe qui gagne. Si une application fonctionne suffisamment bien et qu'aucune faille de sécurité n'est à déplorer, alors il est inutile de mettre à jour ses dépendances.
nota bene : une nouvelle version d'une dépendance améliorerait les performances de l'application principale. Soit l'application est suffisamment utile, utilisable et utilisée, dans ce cas les améliorations de performance ne sont pas indispensables. Soit on perd des utilisateurs et dans ce cas il faut mettre la bibliothèque à jour, puis s'assurer que l'application fonctionne toujours et que rien n'est cassé. Doit-on le faire automatiquement ? C'est ce dont il est question dans la suite.
Système automatique de mise à jour
Il peut être tentant d'installer des automatismes pour exécuter les tâches rébarbatives. C'est d'ailleurs l'une des victoires de l'informatique et de la robotique : libérer l'humain des labeurs sans intérêt.
Mais est-ce que la mise à jour de bibliothèque est une tâche rébarbative ? Ça dépend…
La question est surtout de savoir à quel point la vérification de la stabilité du code est automatisé. Si le code qui utilise la bibliothèque left-pad
est couvert par les tests et idempotent, et qu'il existe un système automatique d'intégration (Continuous Integration ou CI), alors on peut automatiser tout cela.
Dans les autres cas — la majorité — il faudra s'assurer que la mise à jour de la bibliothèque ne vient pas casser notre projet.
Bref, tout est histoire de confiance : confiance dans les procédures en place qui vérifient ce qui est livré et déployé, confiance dans les auteurs et mainteneurs des libs pour qu'ils utilisent bien semver, qu'ils exposent bien un changelog complet, etc.
De toute façon, si une bibliothèque est simple et rapide à mettre à jour, c'est à dire qu'une fois à jour, tout fonctionne comme avant, alors il n'est pas coûteux de la mettre à jour à la main.
À l'inverse, si une bibliothèque est critique, que sa mise à jour est risquée et qu'il faudra passer par une grosse phase de recette pour vérifier le comportement après la mise à jour, alors les systèmes automatiques ne peuvent palier notre flemme des développeurs.
URGENT : besoin des dernières versions
Qu'en est-il des phases de développement pendant lesquels il nous faut absolument la dernière version en date d'une bibliothèque ?
Je suis en train de disrupter le métier du triangle de Pascal et pour cela j'écris un logiciel informatique qui repose sur left-pad
. Ayant de gros besoins sur cette bibliothèque, je décide de participer à son développement et je propose des améliorations quotidiennement.
J'ai donc une application principale qui utilise en dépendance une bibliothèque en version 12.24.48.
Mais il lui manque une fonctionnalité que je décide de coder dans mon application principale tout en proposant son intégration dans la bibliothèque. Cette fonctionnalité a été ajoutée et une nouvelle version de left-pad
est disponible. Je sais que cette nouvelle version va me permettre de virer du code de mon application principale et je décide de faire ces deux actions : mettre à jour la bibliothèque et enlever le code qui palier les manques de la bibliothèque. Je n'ai pas d'urgence à le faire puisque j'avais une application qui fonctionnait avec la version précédente de la bibliothèque.
Cette fois c'est un bug critique que je subis. Mon application en version 2.0.0 introduit une nouvelle façon de construire les étages du triangle, mais elle dépend de left-pad
v12.25.00 qui contient un bug. Il va falloir corriger ce problème dans la bibliothèque avant de pouvoir sortir ma nouvelle version.
Remarque, rien ne presse, la version 1.5.4 est en prod et fonctionne très bien. Ou en tout cas mieux que si l'application n'existait pas, et mieux que si on livrait la version 2.0.0 avec le bug de la version 12.25.0 de left-pad
. On peut très bien s'assurer que la version 12.25.1 règlera le problème et attendre avant de sortir la version 2.0.0 de Triscal.
Dans cette histoire complètement inventée, on voit bien qu'il n'y a pas d'urgence à mettre les versions de ses dépendances à jour. La seule urgence qui existe est d'expliquer à notre Product Owner ou à nos utilisateurs que ce n'est pas parce qu'on écrit URGENT en majuscules que tout s'accélère. Qu'il faut savoir profiter de ce qu'on a déjà et qu'il faut savoir patienter car le développement informatique est un processus lent.
PS : J'ai demandé sur un réseau social l'avis d'autres développeureuses, c'est en anglais et c'est sur hashnode.
[EDIT 13/07/2018]
Il n'y a pas de fumée sans feu : https://mobile.twitter.com/KrasimirTsonev/status/1017768324778872832
An idea - you gain an access to a popular npm account. Then release a new minor version containing your virus. The day after hundred of developers will update their dependencies and your virus will be spread across the web. Don't believe? Read this https://github.com/eslint/eslint-scope/issues/39
— Krasimir Tsonev (@KrasimirTsonev) July 13, 2018
Une idée — vous récupérez les droits sur un compte npm populaire. Vous déployez une nouvelle version mineure contenant votre virus. Le jour suivant des centaines de développeurs vont mettre à jour leurs dépendances and votre virus sera disséminé à travers le web. Vous n'y croyez pas ? Lisez ceci : https://github.com/eslint/eslint-scope/issues/39