Petit guide de survie sur le Legacy Code

Slim Ayache
Jan 4 · 6 min read

Le cauchemar du développeur

Soyons honnêtes, à moins d’être le descendant direct de MacGyver, aucun de nous ne raffole de travailler sur du legacy code. Sa maintenance est loin de présenter le coté le plus sexy du métier du développeur. Gérer le legacy est un défi, une opération laborieuse; il faut être doté du bon état d’esprit et des bons outils avant d’y plonger.


Mais qu’est ce que le “legacy” ?

Par habitude, on appelle un code legacy (pour dire « héritage ») si :

  • Il ne contient pas des tests unitaires / d’intégration
  • Il n’est pas (assez) documenté, ne contient pas (suffisamment) de logs
  • On ne comprend plus toutes ses subtilités et toute sa logique (boite noire)
  • Il est fait avec des vieilles technos (le monde de l’informatique change rapidement)
  • On souffre d’une perte de connaissance, avec des départs, des changements d’équipes ou d’ownership
  • Par conséquence de tous ces points dessus, son coup de maintenance est trop élevé

Pour résumer : un code qu’on ne maîtrise plus, partiellement ou totalement.

Pour la petite histoire

Toutes les startups sans exception ont un stock de Legacy code, et personne ne niera ce fait. Cela fait partie intégrante du modèle économique des startups : Manque d’infrastructure au départ, la grande vitesse de livraison, focus sur le produit minimal viable, time-to-market extrêmement court, turn-over, changement constants des besoins, etc..

A un moment ou un autre, il faut affronter ce code. Ceci fait aussi partie du cycle de vie de toute les entreprises, y compris les startups et scaleups.

La boite noire

Le legacy code peut comporter une certaine complexité (involontaire) qui en fait un objet… hmm… magique !

Un flux d’entrée => [quelque chose de magique] => un flux de sortie

Il faut parfois jouer avec l’application telle quelle, faire varier ses paramètres un par un pour pouvoir faire les bons diagnostics. Se jeter directement dans le code en espérant trouver des réponses peut, dans certains cas, se montrer frustrant et inefficace. Commencer par voir le legacy code comme une boite noire, se concentrer sur la “grande image”, le décortiquer au fil de l’eau est plus intéressant.

Logs, encore et toujours

Placer des sondes d’exploration dans des endroits stratégiques de l’application peut vous apporter le salut. Il existe plusieurs façon d’implémenter les logs, soit simplement avec des fichiers (log4net), ou bien avec des outils plus évolués tels que des moteurs de recherche (ElasticSearch) ou mieux un système complet de monitoring tel que AppInsights (pour ceux qui ont les sous). [voir cet article sur appInsight]

La présence de logs clairs peut répondre à plusieurs interrogations quant au comportement du legacy code. On peut en abuser en environnement de développement.

Audit de la base de données

Quand l’application est massivement orientée base de données, et quand ces données sont modifiées de partout, y compris manuellement (par le support, par exemple) il faut pouvoir tracer tous ces changements. Comprendre qui a fait quoi et quand.

Créer des tables d’audit et archiver les insertions, les updates et les suppressions de la base de données, avec le nom de l’application, l’utilisateur, l’heure de modification et la raison, peut faire éviter les maux de tête du jeu d’inspecteur lorsqu’il faut expliquer un bug ancestral.

(Nous parlerons dans un futur article de l’outil que nous avons développé pour nous aider dans cette mission)

Nettoyage

  1. Il est bien d’adopter la règle du boyscout lors de nos visites du code. Changer le nom d’une variable, d’une méthode, ajouter des énumerations, extraire du code dans une méthode, et même formater le code sont des refactoring qui ne coûtent presque rien vu que la plupart des IDE les intègrent et avec un très faible risque de régression. [voir cet article sur la programmation par intention]
  2. Un refactoring moins automatique consisterait à se débarrer du code mort, de la duplication, et à améliorer des fonctionnalités pour éliminer des aberrations telles que des appels redondants à la base de données, des algorithmes facilement optimisables (penser à un tri avec une complexité O(n²)), ou un code illisible. Cette partie comporte un risque non négligeable. Elle n’est pas toujours faisable sans peine. Un arbitrage effort/résultat doit être fait surtout si une décision de refonte a été prise.
  3. Le troisième type de nettoyage consiste à faire le tour des dépendances externes du legacy code. Faire le ménage dans les services appelés, les dossiers de l’application, la sécurité, le possibilité de scaling horizontal, etc. C’est des transformations globales et architecturales.

Documentation

Bosser sur du Legacy c’est un peu comme faire de l’archéologie… ou de la spéléologie ! Dès que l’on tombe sur une découverte, il faut la noter et l’expliquer aux autres !

La documentation est souvent négligée au profit d’autres tâches plus croustillantes (comme… le dev !) mais elle fait gagner un temps fou à tout le monde et permet un vrai travail d’équipe. Une bonne description de l’application décentralise la connaissance et enlève le fardeau d’être le référent unique qu’on aimerait appeler même pendant sa lune de miel en Tanzanie pour des questions sur quelques parties du code.

Blâmer !… avec GIT

Git Blame ! Quelle belle invention !

Parfois, il faut déchiffrer l’intention du programmeur à travers l’historique de son code. Suivre l’évolution de ce code et examiner les différences pour comprendre le contexte et l’intérêt de certaines parties obscures.

Par la même occasion, faire un petit tour sur JIRA ou VSTS et relire quelques user stories peut éviter beaucoup de moments de solitude. L’historique peut révéler bien des mystères.

Refaire ou parfaire

Il est un important de se donner du temps pour dresser le bilan complet du legacy code afin de prendre une décision : rebâtir à neuf ou sauver l’immeuble.

La plupart des développeurs frissonnent à l’idée de recommencer dès le début ! Mais cette idée alléchante est très risquée. Il faut, entre autres :

  • Assurer toutes les fonctionnalités existantes
  • Répondre à toutes les contraintes techniques (antérieures et futures)
  • Garantir la qualité du code
  • Gérer l’historique
  • Gérer la migration
  • Gérer le parallel running
  • Ne pas dépasser les budgets !
  • Ne pas se retrouver face à un futur legacy (ça arrive très souvent !)

Ainsi, Il est sage de savoir réparer petit à petit et d’itérer jusqu’à arriver au résultat désiré, à savoir, un code propre, testé, documenté et facilement maintenable. Une étape à la fois :

  • Isoler les domaines fonctionnels
  • Découpler les composants
  • Insérer des tests basés sur la compréhension accumulée du produit
  • Mettre à jour les librairies
  • Automatiser ce qui est automatisable (comme le déploiement)
  • Remplacer progressivement les couches métier
  • Développer des API et des middlewares pour la communication
  • Un coup de pinceau à la couche UI / UX
  • Optimiser (performance, mémoire, nombre d’instance)

Tirer le meilleur du moins bon

Il est toujours facile de critiquer ceux qui étaient là avant nous, de regarder un code et se dire qu’on peut mieux faire. Mais il faut toujours remettre les choses dans leur contexte. Le legacy code peut ne pas être beau, mais il a satisfait un besoin à un moment donné. Les circonstances dans lesquelles il a été écrit peuvent avoir été dures. Il nous permet d’avoir une étape de départ très avancée pour revoir le projet tel qu’il devrait être.

Dans le cas où l’on décide de l’entretenir, le legacy nous permet d’apprendre à améliorer sans avoir systématiquement tout casser pour refaire. Sinon, il nous apprend à ne pas refaire les mêmes erreurs, si l’on décide de le reconstruire. D’ailleurs certains qualifient de legacy ce qui a été écrit par un autre développeur, et il y a toujours une leçon à tirer de l’expérience des autres.

Lire ce que les Gurus ont dit

PS : Je remercie mes collègues (géniaux) pour leur relecture et leurs suggestions.

YounitedTech

Le blog Tech de Younited, où l’on parle de développement, d’architecture, de microservices, de cloud, de data… Et de comment on s’organise pour faire tout ça. Ah, et on recrute aussi, on vous a dit ?

Thanks to Nicholas Suter.

Slim Ayache

Written by

Software developer (and I like it). Speak AR, FR, EN. Interests in photography, music, and one billion other things. Follow me : @nemantro

YounitedTech

Le blog Tech de Younited, où l’on parle de développement, d’architecture, de microservices, de cloud, de data… Et de comment on s’organise pour faire tout ça. Ah, et on recrute aussi, on vous a dit ?