لماذا يجب عليك إعادة هيكلة (Refactor) كودك البرمجي؟ دليل شامل لتحسين جودة الكود

دقائق القراءة: 8

مقدمة إلى عالم تطوير التطبيقات وجودة الكود

هل تبدو لك أي من العبارات التالية مألوفة؟ التفكير في تنسيق الكود، التخلص من عناصر <div> و <span> غير الضرورية، استخدام مكونات React الوظيفية، محاولة تجنب دوال السهم (arrow function) في عملية العرض (render)، والأهم من ذلك: لا تكرر نفسك! قبل أن نتعمق مباشرة في مفهوم إعادة الهيكلة (refactoring)، دعني أطرح عليك سؤالاً بسيطاً: ماذا يعني تطوير تطبيق برمجي؟

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

لا تنسَ الأخطاء البرمجية (Bugs)

نعم، الأخطاء تحدث. اعتماداً على عملية التطوير، تعقيد البرمجيات، المكدس التقني (technical stack)، والعديد من العوامل الأخرى، قد يختلف عدد الأخطاء. لا يمكن لأي عمل أن يتحمل مشكلات حرجة في بيئة الإنتاج (production). لتقليل المشاكل، يجب إيلاء اهتمام خاص لعملية ضمان الجودة (QA process). لكن نظرية ضمان الجودة تشير إلى أنه من المستحيل عادةً تغطية 100% من الاختبارات لتطبيقاتك والاستعداد لجميع السيناريوهات المحتملة. ومع ذلك، لتحقيق أفضل النتائج، تقضي الفرق الكثير من الوقت في اختبار البرمجيات وإصلاح المشكلات. هذا جزء ضروري من العملية يجب على كل عميل فهمه وتحديده كأولوية.

فهم الديون التقنية (Technical Debt) وتأثيرها

لكن لهذه العملة وجه آخر. كلما طالت عملية التطوير والاختبار، زادت الديون التقنية التي تتراكم عليك. إذاً، ما الذي تعنيه عبارة “الديون التقنية”؟

تشير الديون التقنية إلى جميع المشكلات المتعلقة بالجودة التي تواجهها في كودك البرمجي، وهي مشكلات ستتطلب إنفاق موارد إضافية في المستقبل. تتراكم عليك الديون التقنية لأسباب متنوعة، مثل:

  • ضغط العمل لإصدار ميزات جديدة بشكل أسرع.
  • عدم كفاية الاختبارات.
  • تغير المتطلبات بسرعة.
  • قلة خبرة المطورين.

يجب توثيق الديون التقنية. إذا لم تترك ملاحظات “للقيام بها” (to-do's) في الكود، فمن المرجح أن تنسى المشكلة. وحتى لو كان لديك وقت لها في المستقبل، فلن تتذكر إصلاحها. توثيق هذه الديون يضمن عدم إغفالها ويسهل معالجتها لاحقاً.

أهمية إعادة هيكلة الكود (Refactoring)

عادةً ما تحتاج إلى قضاء بعض الوقت في إعادة هيكلة الكود الموجود لحل مشكلات جودة الكود وبالتالي تقليل الديون التقنية. ولكن ما هي إعادة الهيكلة (refactoring

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

  • هل سنحصل على أي ميزات جديدة؟ لا.
  • هل سنصلح بعض الأخطاء على الأقل؟ لا أيضاً.
  • ماذا سنحصل إذن؟ …

العمل على معالجة الديون التقنية يساعد على تجنب الأخطاء. ولإضافة إصلاحات أو تغييرات إلى المشروع، نحتاج دائماً إلى قراءة الكود القديم. لذلك، فإن إعادة الهيكلة والحفاظ على جودة كود جيدة سيساعدنا في الحفاظ على وتيرة تطوير جيدة.

في بعض الأحيان قد لا يحتاجها العمل. على سبيل المثال، إذا كنت تعمل على نموذج أولي (prototype) أو إثبات مفهوم (Proof of Concept)، أو إذا كانت هناك أولويات عمل لا يمكن تعديلها، يمكنك الاستغناء عن إعادة الهيكلة. ولكن في معظم الحالات، فإن التخلي عن إعادة الهيكلة ليس أمراً حكيماً. قد تقضي قدراً هائلاً من الوقت في إعادة الهيكلة إذا كان مطورو فريقك يسعون إلى الكمال، ولكن هذا أيضاً ليس منطقياً. لذلك، تحتاج إلى تحقيق التوازن. يجب ألا تقضي وقتاً في إعادة الهيكلة أكثر مما ستوفره في المستقبل.

كيف تبدأ إعادة هيكلة كود React الخاص بك؟

تعتبر إعادة هيكلة الكود ممارسة أساسية لتحسين قابلية الصيانة والأداء. إليك بعض النصائح العملية للبدء في إعادة هيكلة كود React:

1. فكر في تنسيق الكود (Code Formatting)

يضيف بعض الأشخاص فواصل زائدة (trailing commas)، والبعض الآخر لا يفعل. يستخدم البعض علامات اقتباس فردية (single quotes)، بينما يستخدم آخرون علامات اقتباس مزدوجة (double quotes) للسلاسل النصية (string). إذا كنت تعمل ضمن فريق، فإن الحفاظ على نمط كود موحد يمكن أن يكون صعباً حقاً. ويمكن أن يؤدي عدم الاتساق في نمط الكود إلى جعل كودك يبدو فوضوياً وصعب القراءة. لذا، إذا لم تفكر في استخدام أدوات تنسيق الكود من قبل، فقد حان الوقت لذلك.

أحد أشهر أدوات إعادة هيكلة React وأسهلها استخداماً هو Prettier. يمكنك ببساطة إضافته إلى المشروع وسيتولى أمر التنسيق نيابة عنك. يحتوي Prettier على بعض إعدادات النمط الافتراضية، ولكن يمكنك تغييرها وفقاً لتفضيلاتك عن طريق إضافة ملف .prettierrc بقواعد التنسيق الخاصة بك. قد يبدو الإعداد الجيد لملف .prettierrc كما يلي:

{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "none"
}

يمكنك أيضاً إعادة تنسيق الكود تلقائياً قبل الالتزام بالتغييرات (committing) باستخدام خطافات ما قبل الالتزام (pre-commit hooks).

2. تخلص من عناصر <div> و <span> غير الضرورية

عندما تم إصدار React 16.2 في نوفمبر 2017، تنهد الكثير من مطوري React بالارتياح. قبل ذلك، لكي يعرض المكون قائمة من العناصر الفرعية (children)، كان من الضروري تغليف هذه العناصر في عنصر إضافي، مثل <div> أو <span>. ولكن مع React 16.2، حصلنا على دعم محسّن لعرض العناصر الفرعية للمكونات. يمكن للمطورين الآن استخدام ما يسمى الأجزاء (fragments).

تبدو هذه الأجزاء وكأنها وسوم JSX فارغة (<> … </>). بمساعدة الأجزاء، يمكنك تمرير قائمة من العناصر الفرعية إلى المكون دون إضافة عقد إضافية إلى نموذج كائن المستند (DOM)، مما يحافظ على نظافة الهيكل.

3. فكر جيداً في الأسماء

لا تكن كسولاً عند التفكير في أسماء المكونات والمتغيرات. يجب أن يكون كل اسم واضحاً بذاته (self-explanatory). هل سبق لك أن رأيت مقتطفات كود مثل هذا؟

const modifyData = data.map(x => [x.a, x.b]);

ماذا يفعل هذا الكود؟ إذا لم تتمكن من فهم الغرض من المتغير من اسمه، فقد حان الوقت لإعادة تسميته! سيساعدك هذا أنت وفريقك على فهم المنطق بسهولة أكبر. كما سيلغي الوقت المستغرق في إجراء تغييرات على المكونات الموجودة في المستقبل.

4. لا تكرر نفسك (DRY Principle)

تم صياغة مبدأ DRY (Don't Repeat Yourself) لأول مرة في كتاب The Pragmatic Programmer. ينص على أن “كل قطعة من المعرفة يجب أن يكون لها تمثيل واحد، لا لبس فيه، وموثوق به داخل النظام”. بعبارة أخرى، تحتاج إلى وضع كتل الكود المتكررة في مكونات منفصلة قابلة لإعادة الاستخدام.

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

للحفاظ على مكوناتك DRY وصغيرة، يمكنك اتباع قاعدتين بسيطتين:

  • إذا كنت تستخدم كتلة كود أكثر من مرتين، فقد حان الوقت لاستخراجها.
  • إذا تجاوزت عدداً محدداً مسبقاً من الأسطر في مكون (على سبيل المثال 100 سطر)، فمن المحتمل أن يكون هناك منطق يمكن استخراجه. قسمه إلى مكونات أصغر حسب الوظيفة.

5. استخدم المكونات الوظيفية (Functional Components) بدلاً من المكونات الفئوية (Class Components)

مع تقديم Hooks في React 16.8، حصلنا على إمكانية الوصول إلى ميزات فئات React في المكونات الوظيفية. تحل Hooks مجموعة من المشكلات التي واجهها المطورون بشكل متكرر على مدار السنوات القليلة الماضية. على سبيل المثال، يتيح لنا خطاف useEffect، كما تقترح وثائق React، تجميع منطق المكون في دوال صغيرة بناءً على الأجزاء ذات الصلة (بدلاً من تجميع المنطق بناءً على طرق دورة الحياة). هذا يساعدنا على إعادة هيكلة منطقنا بشكل أفضل. بشكل عام، فإن إعادة هيكلة مكونات React بمساعدة hooks تجعل الكود أنظف وتقلل من كمية الكود الذي تحتاج إلى كتابته.

إليك مثال أساسي جداً: جلب البيانات بعد تحميل المكون وإعادة جلبها بناءً على الخصائص (props) المحدثة. في مكون فئوي (class component)، كنا سنكتب شيئاً كهذا:

class BookList extends React.Component {
componentDidMount() {
this.props.fetchBooks(this.props.bookGenre);
}

componentDidUpdate(prevProps) {
if (prevProps.bookGenre !== this.props.bookGenre) {
this.props.fetchBooks(this.props.bookGenre);
}
}
// ...
}

مع React hooks سيبدو الأمر هكذا:

const BookList = ({ bookGenre, fetchBooks }) => {
useEffect(() => {
fetchBooks(bookGenre);
}, [bookGenre]);
// ...
};

تم الآن تجميع منطق جلب الكتب في مكان واحد. سيتم تشغيل خطاف useEffect بعد التحميل في كل مرة تتغير فيها الخاصية bookGenre الموجودة بين الأقواس المربعة. أنظف بكثير، أليس كذلك؟

أيضاً، يمكنك استخراج المنطق ذي الحالة المماثلة (stateful logic) وإعادة استخدامه في مكونات مختلفة عن طريق إنشاء خطافات مخصصة (custom hooks) خاصة بك. يمكنك قراءة المزيد عن الخطافات المخصصة في وثائق React الرسمية.

6. حاول تجنب دوال السهم (Arrow Functions) في العرض (Render)

هل سبق لك أن رأيت كوداً كهذا؟

render() {
return (
<div>
<button onClick={() => this.setState({ flag: true })} />
...
</div>
);
}

بالتأكيد رأيت. ما المشكلة في ذلك؟ في كل مرة يتم فيها عرض المكون، يتم إنشاء نسخة جديدة من هذه الدالة. هذا ليس أمراً جللاً إذا تم عرض المكون مرة أو مرتين. ولكن في حالات أخرى، يمكن أن يؤثر ذلك حقاً على الأداء. لذا، إذا كنت تهتم بالأداء، قم بتعريف الدالة قبل استخدامها في render:

changeFlag = () => this.setState({ flag: true });

render() {
return (
<div>
<button onClick={this.changeFlag} />
...
</div>
);
}

7. اجعل حزمة الكود (Bundle) أصغر

إذا كنت تستخدم مكتبة خارجية، فلا يجب عليك تحميل المكتبة بأكملها إذا لم يكن ذلك ضرورياً. في بعض الأحيان قد تصادف استيراداً (import) يستخدم طريقة واحدة فقط من المكتبة، مثل:

import lodash from 'lodash';
...
const certainProps = lodash.pick(userObject, ['name', 'email']);
...

بدلاً من ذلك، من الأفضل استخدام ما يلي:

import pick from 'lodash/pick';
...
const certainProps = pick(userObject, ['name', 'email']);
...

الآن لا تقوم بتحميل المكتبة بأكملها، بل فقط الطريقة التي تحتاجها، مما يقلل من حجم الحزمة النهائية للتطبيق ويحسن من سرعة التحميل.

الخلاصة التقنية

تُعد إعادة هيكلة الكود (Refactoring) ركيزة أساسية في بناء وصيانة الأنظمة البرمجية عالية الجودة، وليست مجرد رفاهية أو مهمة ثانوية. إنها استثمار في مستقبل المشروع يقلل من الديون التقنية، يحسن من قابلية القراءة والصيانة، ويعزز من كفاءة المطورين على المدى الطويل. من خلال تبني ممارسات مثل التنسيق الموحد، استخدام مكونات React الوظيفية، والالتزام بمبدأ DRY، يمكن للفرق البرمجية إنشاء كود أكثر قوة ومرونة، قادر على التكيف مع المتطلبات المتغيرة وتقليل الأخطاء التشغيلية. يجب أن تكون إعادة الهيكلة جزءاً لا يتجزأ من دورة حياة التطوير، مع تحقيق التوازن بين تحسين الجودة وتقديم الميزات الجديدة، لضمان استمرارية نجاح المشروع.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *