Adoption d’une nouvelle technologie et migration sans douleur

J’ai personnellement, comme beaucoup je crois, un rapport ambigu à React. D’une part, il y a son lien avec Facebook, la toxicité d’une partie de sa communauté et sa réputation pas toujours usurpée d’usine à gaz. D’autre part, bien employée, je pense que c’est la technologie la plus efficace pour le développement d’applications complexes avec laquelle j’ai pu travailler en quinze ans de web.

Il y a quelques mois, en rejoignant une nouvelle équipe en tant que développeur front, j’ai proposé un plan de migration vers React pour l’application dont nous nous occupons, qui souffrait des symptômes d’un prototype mûri trop vite. J’ai tenté d’importer le meilleur des méthodes pratiquées sur mon projet précédent, pour lequel j’avais travaillé sur une longue migration de Backbone/Marionette à React.

Dans les deux cas, il s’agit de migrations progressives, par opposition à des réécritures depuis zéro, ce qui a de nombreux avantages :

  • assurer la continuité du service pour ses utilisateurs⋅rices,
  • ne pas fractionner le travail de développement et de maintenance,
  • permettre de doser et de mesurer facilement l’avancement de la migration,
  • introduire progressivement de nouvelles pratiques de développement.

Ce dernier point est fondamental. Les enjeux d’une migration ne sont pas seulement techniques, ils sont également humains. Et les migrations sont des périodes compliquées pour la vie d’une équipe. En apportant des bouleversements durables dans des pratiques connues, elles sont forcément une charge de travail supplémentaire et peuvent être perçues comme une remise en question de compétences établies.

Cela fait partie du métier de développeur⋅se de savoir s’adapter aux changements. Mais tout le monde n’a pas le même niveau d’expérience ou d’implication dans le projet, la même énergie ou le même temps disponibles, la même appétence pour la nouveauté ou la même résistance à la JavaScript fatigue. Le rejet a priori du changement est possible et compréhensible.

Cela est encore pire si le choix vient de l’extérieur ou est imposé unilatéralement. Même dans les équipes sans structure hiérarchique explicite, qui appliquent des processus de décision collectifs, des dynamiques de pouvoir existent. Les principales orientations techniques sont souvent initiées par les développeurs⋅ses les plus expérimenté⋅e⋅s. C’est leur rôle, c’est peut-être le vôtre, mais celui-ci ne saurait être séparé de la responsabilité de construire l’adhésion collective de l’équipe et d’amener chacun de ses membres vers davantage d’autonomie et d’expérience.

Plus qu’un challenge technique, la problématique de la migration est bien l’appropriation d’un nouveau choix technologique par tou⋅te⋅s et la montée en compétence de tou⋅te⋅s. Dans cette démarche, voici quelques conseils méthodologiques, parfois illustrés d’exemples liés à React mais qui se veulent généralisables à tout autre contexte.

Convaincre

Le choix de remplacer une solution technique par une autre ne va pas de soi. Dans le cas d’une bibliothèque aussi populaire que React, l’effet de mode peut même jouer contre vous et rendre cette option « suspecte » aux yeux des autres membres de l’équipe. Plutôt que de nier toute partialité, il sera toujours intéressant de questionner ses propres automatismes et de revenir aux raisons du changement, pour savoir si celui-ci est justifié.

La migration envisagée doit être une réponse appropriée à un constat clair sur des problèmes réels du projet, constat qui doit être construit et partagé par tous les membres de l’équipe. Par exemple, les principaux problèmes de l’application sur laquelle je travaille étaient :

  • une couche d’interface conçue avec un outil peu adapté (doT.js, simple moteur de templating) et mal utilisé, augmenté de beaucoup de « plomberie » pour gérer des aspects comme les événements, les changements de style et les manipulations de DOM ;
  • de nombreux bugs d’UX liés à des changements d’état incohérents, notamment sur la gestion des URLs et des boutons « suivant/précédent », avec beaucoup d’effets de bord entre les différents modules ;
  • de multiples incohérences visuelles, liées à peu de réutilisations de composants et des styles CSS trop spécifiques.

Un problème global d’architecture donc, justifiant au moins un gros travail de refactorisation. Maintenant, si on revient aux fondamentaux de React pour le décrire en quelques lignes…

Bibliothèque JavaScript de construction d’interfaces, qui favorise le développement par composants réutilisables, en leur imposant un modèle de rendu et un cycle de vie stricts et en gérant automatiquement le DOM. Ainsi, les développeurs⋅ses peuvent se focaliser sur le résultat HTML qui correspond à un état donné, ce qui rend React particulièrement adapté aux applications complexes.

… on voit que cette bibliothèque semble apporter une réponse technique adaptée au contexte. Du moins en théorie.

Le meilleur moyen de démontrer les qualités de la solution proposée est encore de les illustrer par la pratique. Pour ça, les démos en live coding sont particulièrement efficaces. Vous pouvez créer ou récupérer un petit projet d’exemple, mais ce sera plus parlant encore de travailler directement sur votre application. C’est ce que j’ai fait, en construisant un petit arbre de composants React simples dans une branche du projet. C’était l’occasion d’introduire les React Dev Tools (extension pour Firefox ou Chrome), qui sont le support parfait pour illustrer le modèle de composants arborescent, son lien avec le DOM et la propagation des props.

Prenez le temps qu’il faut sur ces séances. Et préparez cet exercice en amont, pour ne pas passer le premier quart d’heure à cafouiller en direct avec Webpack ou autre et réduire à néant vos arguments sur la simplicité ! Encouragez les questions et même les critiques, car à ce stade elles seront toutes légitimes. Sachez les recevoir et y répondre honnêtement, sans nier le fait que chaque choix technique comporte sa part de concessions.

Et surtout, évitez de présenter votre nouvelle solution technique comme quelque chose de « révolutionnaire », venu sauver les développeurs de pratiques démodées. Démystifiez l’inconnu et réaffirmez les compétences des gens. J’insiste pour ma part sur le fait que React (et c’est vrai pour tout framework bien conçu) ne remet aucunement en cause les aptitudes fondamentales du développement web : HTML sémantique, accessibilité et CSS. Au contraire, en séparant clairement le traitement des données de ce qui est rendu par le navigateur, il permet à mon sens de les exploiter au mieux.

Préparer

Si le principe de la migration est validé par toute l’équipe, il reste un travail en amont à faire pour s’assurer qu’elle se fera dans de bonnes conditions.

La première priorité devrait être de s’assurer que vous avez des outils de non-régression. Pour une migration de l’interface de votre application, les tests unitaires seront probablement peu sollicités, en revanche des tests de plus haut niveau simulant le comportement d’un⋅e véritable utilisateur⋅rice prennent tout leur sens. Ils seront l’outil le plus précieux pour garantir que le changement progressif de technologie sous-jacente n’a pas de conséquences visibles et donneront confiance aux développeurs⋅ses pour avancer. Si vous n’avez pas encore de tests fonctionnels, c’est le meilleur moment pour commencer, en veillant à tester au moins vos fonctionnalités critiques avant de les migrer.

L’accroissement du poids d’une application web est une forme de régression en elle-même. C’est un compromis qui peut se justifier, mais votre nouveau choix technique sera peut-être plus lourd que l’ancien. Et dans tous les cas, la cohabitation de deux technologies pendant la migration apportera un surplus temporaire au poids de votre application. Il faut pouvoir mesurer cet impact, pour éventuellement adopter des stratégies pour le limiter, par exemple avec une meilleure séparation de l’application en bundles. On peut utiliser périodiquement un outil tel que Webpack Bundle Analyzer ou mieux encore, mettre en place des scripts automatiques de comparaison de taille avec des seuils d’alerte.

C’est également le bon moment pour installer ou mettre à jour certains outils de votre environnement de développement qui ne sont pas liés particulièrement à la migration mais qui amélioreront le confort des développeurs⋅ses. Je pense à des choses comme un linter, du live reload, etc.
Ne sous-estimez pas cette tâche et n’allez pas trop vite en introduisant de nouveaux outils, au risque d’embrouiller des gens qui ne les connaissent pas avant même le début de la migration. Faites-en au contraire une occasion pour chacun⋅e de découvrir de nouvelles pratiques.

Vous pouvez également chercher à corriger certains problèmes de votre code qui pourraient rendre la migration encombrée par beaucoup de refactorisation. Par exemple, en restant dans le cadre de votre framework historique, faites émerger des composants génériques élémentaires ou simplifiez des styles devenus trop spécifiques. Il y a toutefois un risque de sur-anticiper et de faire du travail qui sera défait très vite. Limitez-vous aux résolutions les plus évidentes et qui auront un impact significatif sur le confort de développement.

Une dernière étape de préparation indispensable est de constituer une documentation spécifique à la migration et de la rattacher au projet. Pour commencer, vous pouvez y agréger les éléments suivants :

  • un rappel de la démarche : pourquoi la migration est nécessaire, son périmètre, etc. ;
  • des ressources liées à la technologie cible : documentation officielle, article pertinent, etc. ;
  • des recettes pour réaliser la conversion d’un module de l’application : des indications de nommage, de pratiques de code, d’écriture de tests, etc.

Cette documentation permettra le partage de la connaissance sur le sujet et pourra être enrichie par tou⋅te⋅s tout du long.

Accompagner

Quand la migration est officiellement lancée, incitez tout le monde à proposer rapidement du code avec le nouveau framework. Si possible, l’ancien ne devrait plus être utilisé, à part lors de corrections de bugs.

Cette transition peut être une phase critique pour la motivation des personnes. Même si elles sont convaincues en théorie de la pertinence de la nouvelle solution technique, elles peuvent avoir l’impression de redevenir débutantes et ne pas savoir par où commencer. Il est important de faciliter leurs premiers pas.

Dans les différentes migrations que j’ai pu effectuer, nous avions établi au préalable une liste des modules de l’application, avec une difficulté indicative pour leur migration. En plus d’aider au suivi du processus, cette liste a pu servir tout du long de « pioche » pour que chaque développeur⋅se décide à quelle brique s’attaquer, en commençant par les plus simples.

Si votre équipe est habituée à merger des diffs de plusieurs centaines de ligne, poussez plutôt pour des modifications très progressives. Valorisez les petites victoires : les premiers composants mergés, les premières factorisations, les premiers diffs négatifs, etc.

Lors des revues de code, sachez vous effacer au profit de revues par des membres de l’équipe confrontés eux-mêmes à la découverte. Exercez également la mesure nécessaire dans la qualité recherchée. Ne visez pas du code parfait, ni même le code que vous auriez écrit du haut de votre expérience personnelle. Cela pourrait priver vos co-équipiers⋅ères d’une phase de découverte importante dans leur appropriation personnelle de la nouvelle technologie. L’objectif principal est bien que chacun⋅e atteigne à son rythme le moment où ça « fera tilt », celui où le potentiel du nouvel outil sera compris et l’expérience de la personne durablement enrichie.

Dans le même ordre d’idée, favorisez d’abord la recherche de solutions par les personnes qui rencontrent les problèmes. N’arrivez pas avec vos réponses toutes faites, qui de toute façon gagneront toujours à être ré-examinées collectivement.

Les points de friction récurrents signaleront des problématiques à l’échelle du projet entier, qu’il convient de formuler comme telles et d’examiner collectivement. Un exemple très commun avec React est la gestion d’un state global à l’application. Un autre, l’arrivée des premiers problèmes de performance visibles. N’hésitez pas à ralentir la migration pour explorer ces sujets en profondeur et collectivement, par exemple dans des séances de mob programming. À l’équipe dans son ensemble de décider ce qui est critique pour la migration en cours, qui peut justifier l’ajout d’un nouvel outil ou des changements de pratique (avec la complexité induite), ou ce qui peut être reporté à après la migration.

Poursuivre

La migration pourra être considérée terminée quand l’ancienne technologie diagnostiquée à l’origine des problèmes et les artifices techniques de cohabitation seront supprimés du code.

Et cela peut prendre du temps. C’est un des risques de cette approche progressive : si la méthodologie mise en place rend la cohabitation des deux technologies relativement confortable, la migration pourra s’éterniser. Cela peut ne pas être gênant ou au contraire empêcher d’exploiter les avantages du nouvel outil. Par exemple, dans mon projet précédent, un routeur d’application en Backbone a longtemps persisté, ce qui nous empêchait de généraliser le SSR de React au site entier.

Pour éviter ce phénomène de stagnation de la migration, il peut être utile de mettre « un coup de boost » pour réellement la terminer, par exemple en dédiant des sprints exceptionnels à cette problématique purement technique.

Mais un projet en développement continu est par définition en perpétuel chantier. Les problématiques reportées précédemment pourront être traitées une par une, portées par les personnes qui les auront soulevées pendant la migration. Le potentiel de la nouvelle base technique pourra être exploité… et nul doute que de nouveaux problèmes surviendront rapidement !

L’important est qu’à ce stade, la connaissance aura été diffusée. Chaque membre de l’équipe aura pu s’approprier la nouvelle technologie de manière opérationnelle et aura gagné en expérience.

2 commentaires sur cet article

  1. jeanMiMi, le samedi 7 décembre 2019 à 10:24

    Merci pour cet article.
    C’est en effet pas toujours facile d’apprendre constamment des nouvelles technos, frameworks, paradigmes… Il faut faire au mieux pour que les dev suivent le mouvement.
    Et j’ai découvert le concept de mob coding, ça me plaît.
    Concernant React et les reproches que vous formulez, Vuejs n’aurait-il pas fait l’affaire ?
    Con

  2. Benjamin Becquet, le lundi 9 décembre 2019 à 11:45

    Merci !

    > Concernant React et les reproches que vous formulez, Vuejs n’aurait-il pas fait l’affaire ?

    Oui tout à fait. Le choix de React ici paraît un peu automatique et j’aurais certainement dû parler de la comparaison avec d’autres technos semblables, Vue.js en tête.

    Je n’en ai pas parlé mais il y avait d’autres paramètres en jeu qui faisaient pencher la balance vers React dans ce contexte, en particulier un projet de rapprochement technique et de partage de composants avec d’autres équipes produits utilisant déjà cette lib. Mais cela seul n’aurait pas suffi à orienter vers ce choix.