Introduction : Pourquoi le code legacy n'est pas une fatalité, mais un défi
\n\nChaque développeur rencontre tôt ou tard du code legacy — un code obsolète, mal documenté et souvent non testé en JavaScript (Node.js). Travailler avec un tel code peut susciter peur et frustration, mais c'est une étape inévitable de la croissance professionnelle. Le réusinage de code legacy n'est pas une simple réécriture, mais un processus systématique d'amélioration de la structure interne sans modifier le comportement externe.
\n\nDans cet article, nous allons détailler une approche étape par étape pour le réusinage de code legacy en JavaScript, incluant des stratégies, des outils et des exemples concrets pour Node.js. Vous apprendrez comment transformer un code "cauchemardesque" en une base de code maintenable, évolutive et moderne.
\n\n1. Analyse et préparation : par où commencer le réusinage ?
\n\n1.1. Audit du code existant
\n\nAvant de modifier quoi que ce soit, il est essentiel de comprendre complètement le système actuel. Utilisez les méthodes suivantes :
\n\n- \n
- Analyse statique : ESLint avec des plugins (eslint-plugin-import, eslint-plugin-security) identifiera les schémas problématiques. \n
- Recherche de "drapeaux rouges" : fonctions de plus de 50 lignes, variables globales, callback hell, absence de modularité. \n
- Documentation du comportement actuel : notez toutes les entrées et sorties des fonctions clés. \n
// Exemple : recherche de schémas "mauvais" avec ESLint\n// npm install eslint @eslint/js eslint-plugin-security --save-dev\n\n// eslint.config.js\nimport js from "@eslint/js";\nimport security from "eslint-plugin-security";\n\nexport default [\n js.configs.recommended,\n security.configs.recommended,\n {\n rules: {\n "no-var": "error",\n "prefer-const": "error",\n "max-lines-per-function": ["warn", 40],\n },\n },\n];\n\n\n1.2. Création d'un filet de sécurité : les tests
\n\nRègle n°1 du réusinage : ne réusinez jamais du code sans tests. Pour Node.js, utilisez Jest ou Mocha. Commencez par des tests d'intégration qui vérifient les scénarios clés de l'application.
\n\n// Exemple : test pour une fonction legacy\n// legacy.js\nfunction processOrder(orderData) {\n // ... 150 lignes de code confus\n return result;\n}\n\n// processOrder.test.js\nconst processOrder = require('./legacy');\n\ndescribe('processOrder', () => {\n it('doit traiter une commande standard', () => {\n const input = { id: 1, items: ['book', 'pen'], total: 25 };\n const result = processOrder(input);\n expect(result.status).toBe('completed');\n expect(result.total).toBe(25);\n });\n\n it('doit lever une erreur pour une commande vide', () => {\n expect(() => processOrder({})).toThrow('Invalid order');\n });\n});\n\n\n2. Stratégies de réusinage : du chaos à l'ordre
\n\n2.1. Stratégie "Strangler Fig" (Figuier étrangleur)
\n\nCette approche est particulièrement efficace pour les grands projets Node.js. Vous remplacez progressivement les parties du système legacy par de nouveaux modules, sans interrompre le fonctionnement de l'application entière.
\n\n// Exemple : remplacement d'un ancien client HTTP par un nouveau\n// Ancien code (legacy)\nconst http = require('http');\n\nfunction fetchUserData(userId) {\n return new Promise((resolve, reject) => {\n http.get(`http://api.example.com/users/${userId}`, (res) => {\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => resolve(JSON.parse(data)));\n }).on('error', reject);\n });\n}\n\n// Nouveau module (moderne)\nconst axios = require('axios');\n\nasync function fetchUserDataModern(userId) {\n const response = await axios.get(`https://api.example.com/users/${userId}`);\n return response.data;\n}\n\n// Couche intermédiaire (façade)\nasync function getUserData(userId) {\n // On essaie d'abord la nouvelle API, en cas d'erreur on bascule sur l'ancienne\n try {\n return await fetchUserDataModern(userId);\n } catch (error) {\n console.warn('Fallback vers l'API legacy