J'ai embauché de nombreux développeurs au fil des ans. Beaucoup sont arrivés avec la ferme conviction que notre code avait besoin d'une refonte en profondeur. Mais voilà : dans presque tous les cas, les autres développeurs ont trouvé que leur code nouvellement remanié était plus difficile à comprendre et à maintenir. En outre, il était généralement plus lent et comportait plus de bogues.
Ne vous méprenez pas. Le remaniement n'est pas mauvais en soi. C'est une partie cruciale du maintien d'une base de code en bonne santé. Le problème est qu'un mauvais remaniement est, eh bien, mauvais. Et il est étonnamment facile de tomber dans le piège d'empirer les choses en essayant de les améliorer.
Voyons donc ce qui distingue un bon refactoring d'un mauvais, et comment éviter d'être ce développeur que tout le monde redoute de voir s'approcher de la base de code.
Le bon, le mauvais et le côté sombre du refactoring
Les abstractions peuvent être bonnes. Les abstractions peuvent être mauvaises. La clé est de savoir quand et comment les appliquer. Examinons quelques pièges courants et comment les éviter.
1. Modifier considérablement le style du code
Le changement complet du style de code au cours d'une refonte est une erreur que j’ai souvent vue. Cela se produit souvent lorsque quelqu'un vient d'un milieu différent ou a des opinions bien arrêtées sur un paradigme de programmation particulier.
Prenons un exemple. Imaginons que nous ayons un morceau de code qui a besoin d'être nettoyé :
Mauvaise refactorisation
Bien que cette version remaniée puisse plaire aux amateurs de programmation fonctionnelle, elle introduit une nouvelle bibliothèque (Ramda) et un style de codage complètement différent. Pour une équipe qui n'est pas familière avec cette approche, cela pourrait être un cauchemar à maintenir.
Bonne refactorisation :
Cette version améliore le code original en utilisant des méthodes JavaScript standards comme filter
et map
. Elle est plus concise et plus facile à lire, mais elle n'introduit pas de paradigme complètement nouveau ni de dépendances externes.
2. Abstractions inutiles
J'ai un jour embauché quelqu'un qui a ajouté des tonnes de nouvelles abstractions sans comprendre le code sous-jacent. Il a commencé à regrouper des choses qui ne devraient pas l'être et qui étaient en train de diverger (intentionnellement) au fil du temps. Ils ont consolidé certaines configurations qui n'auraient pas dû l'être (des API différentes nécessitaient des configurations différentes).
Avant :
Mauvaise refactorisation
Ce remaniement introduit une classe avec de multiples méthodes, ce qui peut sembler plus « orienté objet », mais qui est en fait plus complexe et plus difficile à comprendre au premier coup d'œil.
Bonne refactorisation
Cette version décompose la logique en petites fonctions réutilisables sans introduire de complexité inutile.
3. Ajouter de l'incohérence
J'ai vu des cas où des développeurs mettent à jour une partie de la base de code pour qu'elle fonctionne complètement différemment du reste, dans une tentative de la rendre « meilleure ». Cela entraîne souvent de la confusion et de la frustration pour les autres développeurs qui doivent passer d'un style à l'autre en fonction du contexte.
Supposons que nous ayons une application React dans laquelle nous utilisons systématiquement React Query pour récupérer des données :
Imaginons maintenant qu'un développeur décide d'utiliser Redux Toolkit pour un seul composant :
Cette incohérence est frustrante car elle introduit un modèle de gestion d'état complètement différent pour un seul composant.
Une meilleure approche serait de s'en tenir à React Query :
Cette version maintient la cohérence, en utilisant React Query pour la récupération des données dans l'ensemble de l'application. Elle est plus simple et n'oblige pas les autres développeurs à apprendre un nouveau modèle pour un seul composant.
La cohérence de votre base de code est essentielle. Si vous devez introduire un nouveau modèle, envisagez de refactoriser l'ensemble de la base de code pour utiliser ce nouveau modèle, plutôt que de créer des incohérences ponctuelles.
4. Ne pas comprendre le code avant de le remanier
L'un des plus gros problèmes que j'ai vus est de refactoriser le code tout en l'apprenant, afin de l'apprendre. C'est une idée terrible. J'ai vu des commentaires selon lesquels vous devriez travailler avec un morceau de code donné pendant 6 à 9 mois. Sinon, vous risquez de créer des bogues, de nuire aux performances, etc.
Avant :
Mauvaise refactorisation
Le remanieur peut penser qu'il simplifie le code, mais il a en fait supprimé un important mécanisme de mise en cache qui avait été mis en place pour réduire le nombre d'appels à l'API et améliorer les performances.
Bonne refactorisation
Ce refactor maintient le comportement de la mise en cache tout en l'améliorant potentiellement par l'utilisation d'un gestionnaire de cache plus sophistiqué avec expiration.
5. Comprendre le contexte de l'entreprise
J'ai rejoint un jour une entreprise avec un horrible bagage de codes hérités. J'ai mené un projet de migration d'une entreprise de commerce électronique vers une technologie nouvelle, moderne, plus rapide, meilleure... angular.js
Il s'est avéré que cette entreprise était fortement dépendante du référencement, et nous avons construit une application à page unique lente et gonflée.
Pendant 2 ans, nous n'avons rien livré d'autre qu'une copie conforme du site web, plus lente, plus buggée et moins facile à maintenir. Pourquoi ? Les personnes en charge de ce projet (moi - je suis le trou du cul de ce scénario) n'avaient jamais travaillé sur ce site auparavant. J'étais jeune et stupide.
Prenons un exemple moderne de cette erreur :
Mauvaise refactorisation
Cette approche peut sembler moderne et propre, mais elle est entièrement rendue côté client. Pour un site de commerce électronique qui dépend fortement du référencement, cela pourrait être désastreux.
Bonne refactorisation
Cette approche basée sur Next.js permet un rendu côté serveur dès le départ, ce qui est crucial pour le référencement. Elle offre également une meilleure expérience utilisateur avec des chargements de pages initiaux plus rapides et des performances améliorées pour les utilisateurs ayant des connexions plus lentes. Remix serait tout aussi bon pour cet objectif, offrant des avantages similaires pour le rendu côté serveur et l'optimisation du référencement.
Comment refactoriser correctement
Il convient de noter qu'il est nécessaire de remanier le code. Mais faites-le correctement. Notre code n'est pas parfait, notre code a besoin d'être nettoyé, mais restez cohérent avec la base de code, familiarisez-vous avec, faites attention aux abstractions.
- Soyez incrémentiel : Effectuez de petits changements gérables plutôt que des réécritures radicales.
- Comprendre le code en profondeur avant de procéder à des remaniements importants ou à de nouvelles abstractions.
- Adaptez-vous au style du code existant : La cohérence est essentielle à la maintenabilité.
- Évitez de multiplier les nouvelles abstractions : Restez simple, sauf si la complexité est vraiment justifiée.
- Évitez d'ajouter de nouvelles bibliothèques, en particulier celles dont le style de programmation est très différent, sans l'accord de l'équipe.
- Écrivez des tests avant la refonte et mettez-les à jour au fur et à mesure. Cela permet de s'assurer que vous conservez la fonctionnalité d'origine.
- Demandez à vos collègues de respecter ces principes.
Outils et techniques pour un meilleur remaniement
Outils d'analyse (linting)
Utilisez des outils de linting pour appliquer un style de code cohérent et détecter les problèmes potentiels. Prettier peut aider à formater automatiquement le code dans un style cohérent, tandis qu'Eslint peut aider avec des contrôles de cohérence plus nuancés que vous pouvez facilement personnaliser avec vos propres plugins.
Revues de code approfondies
Mettez en place des revues de code approfondies pour obtenir les commentaires de vos pairs avant de fusionner le code remanié. Cela permet de détecter rapidement les problèmes potentiels et de s'assurer que le code remanié est conforme aux normes et aux attentes de l'équipe.
Tests
Écrivez et exécutez des tests pour vous assurer que votre code remanié ne casse pas les fonctionnalités existantes. Vitest est un programme de test particulièrement rapide, solide et facile à utiliser, qui ne nécessite aucune configuration par défaut. Pour les tests visuels, envisagez d'utiliser Storybook. React Testing Library est un excellent ensemble d'utilitaires pour tester les composants React (il y a aussi Angular et d'autres variantes).
Les (bons) outils d'IA
Laissez l'IA vous aider dans vos efforts de refactorisation, du moins ceux qui sont capables de correspondre à votre style de codage existant et à vos conventions.
Un outil particulièrement utile pour maintenir la cohérence lors du codage des frontends est Visual Copilot. Cet outil alimenté par l'IA peut vous aider à transformer vos conceptions en code tout en respectant votre style de codage existant et en exploitant correctement les composants et les jetons de votre système de conception.