Pourquoi met-on à jour une application, par exemple sur son téléphone ?
En général, un nouveau design, une nouvelle fonctionnalité sont disponibles et nous intéressent. Parfois l’application avait quelques bugs, et ils sont corrigés dans la nouvelle version. Ou bien on change de téléphone et il faut la version qui correspond au système d’exploitation du nouvel appareil.
Il paraît évident du point de vue de l’utilisateur, de télécharger la nouvelle version d’un logiciel, car en général, on a tout à y gagner.
Pourquoi maintenir ses applications à jour ?
Alors pourquoi ça semble si difficile de maintenir à jour les frameworks et packages de nos projets ?
C’est vrai que c’est un peu plus complexe de ce côté de l’appareil. Il faut gérer les breaking changes, qui modifient parfois le comportement de l’application ou empêchent totalement son fonctionnement. Il faut aussi tout retester pour s’assurer qu’il n’y ait pas de loupé. Et puis, il faut prendre le temps. Et du temps, les équipes n’en ont jamais assez !
Beaucoup d’équipes fonctionnent alors sur le modèle : “on mettra à jour quand ce sera nécessaire / pour une bonne raison”. Ceci afin de s’éviter tout ce travail “inutile”, chronophage, et parfois un peu risqué.
Et puis un jour vient, où on est forcé de mettre la dépendance à jour. Par exemple, tel framework n’est plus maintenu par l’équipe de développement (et il est dangereux de ne plus avoir les correctifs de sécurité), ou bien on a absolument besoin de la nouvelle fonctionnalité de tel package nuget pour le projet.
Et là, c’est la catastrophe : ce qui est une tâche plutôt simple au départ, devient l’epic Jira de l’année.
Les dépendances ont souvent des dépendances, et parfois beaucoup dans certains langages comme JavaScript où les librairies sont petites et fractionnées. On a plusieurs versions majeures de retard, les breakings changes s’empilent et plus rien ne fonctionne.
On est coincé, la dette technique est considérable, et l’issue semble parfois de tout recommencer à zéro, pour “partir sur une base propre”. Beaucoup de projets sont ainsi refondus tous les 3-4 ans !
Pourtant, garder à jour ses applications a beaucoup d’avantages : pour commencer, comme pour son téléphone, les correctifs apportés améliorent beaucoup la sécurité du projet et le confort du développeur ; moins de correctifs “maison” à fournir et moins de tests spécifiques à ces vulnérabilités, aussi.
Cela permet également de rester informé sur les nouveautés, de pouvoir détecter les nouvelles fonctionnalités qui peuvent simplifier la vie de l’équipe et ajouter une vraie plus value au projet, ou bien au contraire, chercher une autre solution qui convient mieux, quand la dépendance prend une autre direction que celle de l’équipe.
Enfin, les breaking changes tant redoutées sont bien plus digestes sur une mise à jour d’une version, plutôt que sur 5 versions majeures.
Qui va s’occuper de la migration ?
Une fois bien décidés à mettre (et garder) ses applications à jour, une question se pose : qui va s’occuper de la migration ? L’équipe responsable du projet peut parfaitement s’en occuper, il faut simplement… le planifier. Tout comme pour l’entretien du code et de la couverture de test, les code review, les bug fest, … l’équipe devra accorder du temps pour la veille et pour maintenir à jour les dépendances du projet, qu’elle connaît en profondeur.
Elle peut pour cela combiner une approche Boy Scout Rule : “nettoyer” un peu à chaque passage, et la migration périodique un peu plus importante, en suivant les releases majeures des dépendances fondamentales du projet (par exemple, tous les six mois pour un projet sous Angular).
Quand les équipes ont pris trop de retard et que les migrations sont plus douloureuses, il est aussi envisageable qu’une équipe dédiée soit désignée. Certaines organisations comportent une équipe responsable de la partie technique de tout un panel d’applications : choix des frameworks, des packages et des workflows. Ils sont donc tous désignés pour cette tâche.
Ces équipes sont souvent composées de développeurs expérimentés qui pourront enchaîner assez efficacement les migrations sur les projets de leur spectre d’action habituel, forts de l’expérience qu’ils ont de la migration précédente. Il faut garder à l’esprit qu’une mise à jour globale, parfois assez indigeste, nécessite des compétences assez spécifiques.
Bien que la connaissance poussée du projet en lui-même ne soit pas toujours nécessaire, celle des dépendances avant et après la migration est primordiale. De plus, des capacités pour le débogage, troubleshooting, et parfois même débrouillardise, sont bien plus importantes que les compétences de développement “pures”. Cela s’adresse donc plutôt à des développeurs seniors ou au moins expérimentés. Dans cette configuration, la communication avec l’équipe projet sera primordiale afin de garder une cohérence.
Comment s’y prendre ?
Préparation
L’équipe responsable du projet, si ce n’est pas elle qui s’occupe de la migration, peut amorcer le travail en effectuant les migrations de packages triviales et sans grand impact. Étant au plus proche du projet, elle est la plus à même de s’occuper de petits bugs fonctionnels ou techniques qui prendraient inutilement du temps à une équipe externe.
Au préalable, si la couverture de tests est améliorable, c’est le moment idéal pour la peaufiner afin d’éviter les effets de bords dus à la migration.
Une fois le travail dégrossi, la migration des éléments plus impactants pourra commencer.
Méthodologie
Il est conseillé de mettre en place une méthodologie de travail par cycles, par exemple SCRUM. C’est d’autant plus important si l’équipe chargée de la migration n’est pas celle du projet, et/ou si elle s’occupe successivement de la migration de plusieurs projets.
D’une part, communiquer efficacement avec les collaborateurs qui ont la connaissance fonctionnelle et technique du projet se révèle crucial pour que la migration se passe bien.
Et d’autre part, revenir régulièrement sur le travail effectué et planifier les actions suivantes permet d’améliorer continuellement le processus et d’identifier des erreurs récurrentes pour les gérer au mieux pour la suite de la migration. Pour revenir sur la méthode SCRUM, cela correspondrait aux réunions de Backlog Refinement et de Sprint Retrospective.
Parmi les difficultés fréquemment rencontrées :
Une couverture de tests insuffisante.
En tout premier lieu, il faut s’assurer que le projet ait une couverture de tests, unitaires et d’intégration, suffisante afin d’éviter les régressions. Ces tests doivent être facilement accessibles aux développeurs chargés de la migration, par exemple avec la possibilité de les lancer sur sa propre machine, ou les droits nécessaires pour le faire sur la plateforme d’intégration.
Les breaking changes sur plusieurs versions majeures.
Les versions majeures sont synonymes de changements radicaux, souvent non rétrocompatibles. En cas de gros retard sur une dépendance, la migration peut devenir délicate. Pour éviter des anomalies dans le projet, il sera peut-être nécessaire de les passer l’une après l’autre, après avoir étudié au préalable le changelog de l’équipe de développement.
Migrations croisées entre les projets.
Certaines organisations partagent du code entre les équipes afin de garder une certaine cohérence entre les projets : authentification, connexion aux bases de données, … Ce code partagé doit évidemment être identifié pour être migré en premier, tout en s’assurant que du code legacy reste disponible pour que la transition soit douce.
Des setup distincts.
A l’inverse, parfois les équipes sont totalement autonomes sur l’architecture de leur application, les choix des dépendances, voire la manière de structurer leur code. Cela peut être déroutant et un temps d’adaptation sera nécessaire pour une équipe extérieure au projet.
Réticence des équipes
Sortir de sa zone de confort peut demander un effort important pour une équipe. Il faut s’assurer de l’implication de tous afin d’éviter les blocages, et intégrer pleinement la migration aux sprints des équipes. Dans le cas d’une équipe dédiée, un membre de l’équipe projet peut être désigné pour servir de relais, voire participer à la migration. La participation aux meetings quotidiens des deux équipes est recommandée, afin de laisser une certaine visibilité dans les deux sens et favoriser la communication, qui est indispensable.
Quelques outils pour nous faciliter la tâche
Pour s’éviter une tâche rébarbative et fastidieuse, voici quelques outils pour nous aider à garder les dépendances de nos projets à jour, au quotidien.
Visual Studio : le package manager
C’est tout simple et efficace, bien que manuel : jeter régulièrement un œil aux mises à jour des paquets installés sur notre solution.
Dotnet-outdated
https://github.com/dotnet-outdated/dotnet-outdated
C’est le même genre d’outil, mais en ligne de commande pour .net Core. Il peut être intégré à un processus d’intégration continue pour alerter quand des mises à jour sont disponibles.
Dependabot
https://github.com/dependabot
Il s’utilise avec votre gestionnaire de repository préféré (Github, Gitlab ou Azure DevOps). Il s’occupera de scanner votre projet Ruby, JavaScript, Python, PHP, Python.NET, Rust ou Elixir, à une fréquence que vous aurez définie, et créera même les Pull/Merge Request pour faire les mises à jour. De quoi bien alléger la charge mentale de l’équipe.
Les Release Note de nos dépendances
Cela peut paraître évident, mais suivre régulièrement les nouveautés et les appliquer au bon moment est primordial. On peut décider d’appliquer la dernière version ou garder quelques versions de retard pour les dépendances les plus critiques, afin de s’assurer de leur stabilité.
En quelques mots
Garder les dépendances de ses projets à jour s’inscrit dans une démarche d’amélioration continue autour d’un projet. Cela améliore la durée de vie du projet limitant la dette technique et en évitant des refontes périodiques, réduit le risque de faille de sécurité, et favorise la veille ainsi que la remise en question du choix d’une dépendance.
Une équipe formée des compétences adéquates, une bonne organisation et les bons outils allègent énormément la tâche qui peut paraître énorme et superflue au premier abord.
Carole CHEVALIER