Einleitung: Warum Legacy-Code kein Todesurteil, sondern eine Aufgabe ist
\n\nJeder Entwickler stößt früher oder später auf Legacy-Code – veralteten, schlecht dokumentierten und oft ungetesteten Code in JavaScript (Node.js). Die Arbeit mit solchem Code kann Angst und Frustration auslösen, ist aber ein unvermeidlicher Teil der beruflichen Entwicklung. Refactoring von Legacy-Code ist nicht einfach nur Umschreiben, sondern ein systematischer Prozess zur Verbesserung der internen Struktur ohne Änderung des externen Verhaltens.
\n\nIn diesem Artikel werden wir einen schrittweisen Ansatz zum Refactoring von veraltetem JavaScript-Code behandeln, einschließlich Strategien, Werkzeugen und konkreten Beispielen für Node.js. Sie erfahren, wie Sie "albtraumhaften" Code in eine wartbare, skalierbare und moderne Codebasis verwandeln.
\n\n1. Analyse und Vorbereitung: Wo beginnt man mit dem Refactoring?
\n\n1.1. Audit des vorhandenen Codes
\n\nBevor Sie etwas ändern, müssen Sie das aktuelle System vollständig verstehen. Verwenden Sie die folgenden Methoden:
\n\n- \n
- Statische Analyse: ESLint mit Plugins (eslint-plugin-import, eslint-plugin-security) deckt problematische Muster auf. \n
- Suche nach "roten Flaggen": Funktionen länger als 50 Zeilen, globale Variablen, Callback-Hell, fehlende Modularität. \n
- Dokumentation des aktuellen Verhaltens: Notieren Sie alle Ein- und Ausgabedaten der Schlüsselfunktionen. \n
// Beispiel: Suche nach "schlechten" Mustern mit 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. Erstellen eines Sicherheitsnetzes: Tests
\n\nRegel Nr. 1 des Refactorings: Refactoren Sie niemals Code ohne Tests. Verwenden Sie für Node.js Jest oder Mocha. Beginnen Sie mit Integrationstests, die die wichtigsten Anwendungsszenarien überprüfen.
\n\n// Beispiel: Test für eine Legacy-Funktion\n// legacy.js\nfunction processOrder(orderData) {\n // ... 150 Zeilen verworrener Code\n return result;\n}\n\n// processOrder.test.js\nconst processOrder = require('./legacy');\n\ndescribe('processOrder', () => {\n it('sollte eine Standardbestellung verarbeiten', () => {\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('sollte bei leerer Bestellung einen Fehler werfen', () => {\n expect(() => processOrder({})).toThrow('Invalid order');\n });\n});\n\n\n2. Refactoring-Strategien: Vom Chaos zur Ordnung
\n\n2.1. Die "Strangler Fig"-Strategie (Würgefeige)
\n\nDieser Ansatz ist besonders effektiv für große Node.js-Projekte. Sie ersetzen schrittweise Teile des Legacy-Systems durch neue Module, ohne den Betrieb der gesamten Anwendung zu unterbrechen.
\n\n// Beispiel: Ersetzen eines alten HTTP-Clients durch einen neuen\n// Alter 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// Neues Modul (modern)\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// Zwischenschicht (Fassade)\nasync function getUserData(userId) {\n // Zuerst die neue API versuchen, bei Fehler auf die alte zurückfallen\n try {\n return await fetchUserDataModern(userId);\n } catch (error) {\n console.warn('Fallback auf Legacy-API