إعادة هيكلة الكود القديم بلغة JavaScript: المستوى المتقدم

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

إعادة هيكلة الكود القديم بلغة JavaScript: المستوى المتقدم



العمل مع الكود القديم (legacy) هو واقع لا مفر منه لأي مطور، خاصة في مجال Node.js. غالبًا ما يمثل هذا الكود "مستنقعًا" من المتغيرات العامة، ووظائف الاسترجاع (callbacks) المتداخلة، ودوال يبلغ طولها مئات الأسطر، وغياب تام للهيكل المعياري. الأساليب البسيطة لإعادة الهيكلة لا تعمل هنا؛ فهي تتطلب نهجًا منهجيًا ومعرفة بالتقنيات المتقدمة. في هذه المقالة، سنناقش الاستراتيجيات التي ستساعد في تحويل الفوضى إلى بنية قابلة للصيانة دون إيقاف الإنتاج.



1. تحليل ورسم خرائط التبعيات



قبل تغيير الكود، يجب فهمه. في المشاريع القديمة، غالبًا ما تكون الوثائق مفقودة أو قديمة. الخطوة الأولى هي بناء خريطة للتبعيات.



الأدوات:

  • Madge — أداة لتصور التبعيات بين الوحدات. ستظهر التبعيات الدائرية والملفات "الميتة".
  • ESLint مع الإضافات — يساعد في اكتشاف المتغيرات والوظائف والواردات غير المستخدمة.
  • النشر في بيئة معزولة — قم بتشغيل الاختبارات (إذا لم تكن موجودة، فاكتب على الأقل اختبارات دخان (smoke tests)) وسجل الوحدات التي يتم تحميلها.


مثال: لنفترض أن لدينا ملفًا legacy.js يحتوي على 2000 سطر ويستخدم متغيرًا عامًا config.



// legacy.js (قبل إعادة الهيكلة)var config = {    apiUrl: 'https://api.example.com',    timeout: 5000};

function fetchData(callback) { // 50 سطرًا من المنطق مع if و try/catch متداخلة // يستخدم config العام if (config.timeout > 0) { // ... } callback(null, data);}


الإجراء: باستخدام Madge، نرى أن legacy.js يتم استيراده في 15 ملفًا آخر. هذا "كائن إلهي". مهمتنا هي تقسيمه إلى وحدات ذرية.



2. استراتيجية "التين الخانق" (Strangler Fig)



هذا النمط مثالي لإعادة الهيكلة دون إيقاف النظام. الجوهر: نحن لا نعيد كتابة كل شيء دفعة واحدة، بل نستبدل تدريجيًا أجزاء النظام القديم بأجزاء جديدة، حتى يموت القديم من تلقاء نفسه.



التنفيذ في Node.js:

  • إنشاء وحدة جديدة بهندسة نظيفة.
  • كتابة طبقة وكيل (Proxy) أو واجهة (Facade) تعيد توجيه الاستدعاءات إلى الوحدة القديمة أو الجديدة.
  • تحويل المستهلكين تدريجيًا إلى الوحدة الجديدة.


مثال: استبدال نمط الاسترجاع القديم (callback) بـ Promise/async/await.



// API القديم (قائم على الاسترجاع)function getUser(id, callback) {    // كود قديم    callback(null, { id: id, name: 'John' });}

// API الجديد (قائم على Promise)async function getUserAsync(id) { // تنفيذ جديد مع async/await const user = await database.findUser(id); return user;}

// واجهة الوكيلasync function getUserFacade(id) { if (USE_NEW_IMPLEMENTATION) { return getUserAsync(id); } else { return new Promise((resolve, reject) => { getUser(id, (err, user) => { if (err) reject(err); else resolve(user); }); }); }}


يمكن نقل العلامة USE_NEW_IMPLEMENTATION إلى التكوين أو مفتاح الميزة (feature toggle). هذا يسمح بتفعيل الكود الجديد لجزء من المستخدمين (إصدار تجريبي canary release) أو التراجع عن التغييرات دون نشر.



3. التقسيم إلى وحدات وعزل الآثار الجانبية



غالبًا ما يعاني الكود القديم من الآثار الجانبية المخفية: تقوم الدالة بتغيير حالة عامة، أو الكتابة إلى ملف، أو استدعاء API خارجي دون إرجاع نتيجة. إعادة هيكلة هذه الدوال تعني استخراج المنطق النقي.



التقنية: "استخراج الدالة" (Extract Function) متبوعًا بحقن التبعيات (Dependency Injection).



الكود الإشكالي:



                

المدونات

توصيات الكتب