مقدمة: لماذا الكود القديم ليس حكماً بالإعدام، بل مهمة قابلة للحل
\n\nكل مطور يواجه عاجلاً أم آجلاً الكود القديم (legacy code) - كود متقادم، ضعيف التوثيق، وغالباً غير مُختبر بلغة JavaScript (Node.js). العمل مع هذا الكود قد يسبب الخوف والإحباط، لكنه جزء لا مفر منه من النمو المهني. إعادة هيكلة الكود القديم ليست مجرد إعادة كتابة، بل عملية منهجية لتحسين الهيكل الداخلي دون تغيير السلوك الخارجي.
\n\nفي هذه المقالة، سنشرح نهجاً خطوة بخطوة لإعادة هيكلة الكود القديم بلغة JavaScript، بما في ذلك الاستراتيجيات والأدوات والأمثلة المحددة لـ Node.js. ستتعلم كيفية تحويل الكود "الكابوسي" إلى قاعدة كود قابلة للصيانة والتوسع وحديثة.
\n\n1. التحليل والتحضير: من أين نبدأ إعادة الهيكلة؟
\n\n1.1. تدقيق الكود الموجود
\n\nقبل تغيير أي شيء، يجب فهم النظام الحالي بالكامل. استخدم الطرق التالية:
\n\n- \n
- التحليل الثابت: ESLint مع الإضافات (eslint-plugin-import, eslint-plugin-security) ستكشف الأنماط الإشكالية. \n
- البحث عن "الأعلام الحمراء": دوال أطول من 50 سطراً، متغيرات عامة، callback hell، غياب النمطية. \n
- توثيق السلوك الحالي: سجل جميع بيانات الإدخال والإخراج للدوال الرئيسية. \n
// مثال: البحث عن الأنماط "السيئة" باستخدام 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. إنشاء شبكة أمان: الاختبارات
\n\nالقاعدة رقم 1 لإعادة الهيكلة: لا تقم أبداً بإعادة هيكلة الكود بدون اختبارات. بالنسبة لـ Node.js، استخدم Jest أو Mocha. ابدأ باختبارات التكامل التي تتحقق من سيناريوهات العمل الرئيسية للتطبيق.
\n\n// مثال: اختبار لدالة قديمة\n// legacy.js\nfunction processOrder(orderData) {\n // ... 150 سطراً من الكود المعقد\n return result;\n}\n\n// processOrder.test.js\nconst processOrder = require('./legacy');\n\ndescribe('processOrder', () => {\n it('يجب معالجة طلب قياسي', () => {\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('يجب رمي خطأ عند طلب فارغ', () => {\n expect(() => processOrder({})).toThrow('Invalid order');\n });\n});\n\n\n2. استراتيجيات إعادة الهيكلة: من الفوضى إلى النظام
\n\n2.1. استراتيجية "Strangler Fig" (التين الخانق)
\n\nهذا النهج فعال بشكل خاص لمشاريع Node.js الكبيرة. تقوم تدريجياً باستبدال أجزاء النظام القديم بوحدات جديدة، دون إيقاف عمل التطبيق بأكمله.
\n\n// مثال: استبدال عميل HTTP القديم بآخر جديد\n// الكود القديم (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// الوحدة الجديدة (حديثة)\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// الطبقة الوسيطة (واجهة)\nasync function getUserData(userId) {\n // جرب API الجديد أولاً، عند الخطأ ارجع للقديم\n try {\n return await fetchUserDataModern(userId);\n } catch (error) {\n console.warn('الرجوع إلى API القديم