تحسين طلبات السحب (Pull Requests) لمراجعات كود فعالة: دليل شامل للمطورين

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

مقدمة: فوضى السرد في الكود البرمجي

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

في عالم تطوير البرمجيات، غالباً ما نواجه تحدياً مشابهاً عند مراجعة طلبات السحب (Pull Requests). فعرض “الملفات المتغيرة” (Files changed) في منصات مثل GitHub يسرد التغييرات أبجدياً حسب مسار الملف. قد يكون هذا مقبولاً للفروع الصغيرة التي تحتوي على ميزات محدودة، لكن في كثير من الأحيان، تتضمن التغييرات المعقدة أجزاءً مترابطة تؤدي حتماً إلى فروقات كبيرة عبر ملفات متعددة. هذه التغييرات الضخمة يمكن أن تكون مربكة للمراجعين عند استخدام عرض الملفات المرتبة أبجدياً.

للتغلب على هذه المشكلة، يمكن للمراجعين عرض هذه التغييرات على هيئة أجزاء أصغر وأكثر عزلة، أي التزاماً تلو الآخر (commit-by-commit). فكل رسالة التزام (commit message) يمكن أن توضح الهدف من التغيير، وتسلسل الالتزامات يقدم سرداً منطقياً لسبب ضرورة هذه التغييرات في فرع الميزة هذا. كل هذا يساهم في جعل مهمة المراجع أسهل وأكثر متعة.

بناء تاريخ التزامات (Commit History) واضح ومنظم

إن الاستخدام الدقيق لأدوات مثل partial-staging (التجهيز الجزئي)، وamending (التعديل)، وrebasing (إعادة الأساس) هي جميعها أدوات ستساعدك على تحقيق تاريخ التزامات نظيف سيقدره مراجعوك. تجنب إنشاء التزامات ذات تركيز فضفاض. قد تكون قد نسيت الالتزام بتغييرات كان من المنطقي تجميعها منطقياً معاً، واستمررت في تحرير الملف. هذا أمر طبيعي ويحدث طوال الوقت. مجرد احتواء الملف على تغييرات لا يعني أنه يجب الالتزام بجميع هذه التغييرات. كما أنك لست مضطراً للتراجع عن التغييرات غير ذات الصلة. يمكنك استخدام التجهيز التفاعلي (interactive staging) لاختيار أي أجزاء من الملف يجب تجهيزها للالتزام وأيها يجب تركها لالتزام مستقبلي.

استخدم rebase بقوة لتجنب إنشاء التزامات يتم تعديل تغييراتها لاحقاً بشكل كبير أو حتى حذفها. قد يكون من المحبط للمراجع أن يقضي وقتاً في فهم ما تغير في التزام واحد، ثم يكتشف أنه أهدر وقته بشكل أساسي على كود ميت (dead code) بعد بضعة التزامات. قم بتعديل (amend) أو إصلاح (fixup) أو دمج (squash) هذه التغييرات قبل طلب المراجعة!

إذا بدا ذلك أمراً مزعجاً، وهو رد فعل منطقي نظراً لأن Git لا يُعرف بسهولة استخدامه، فإنني أوصي بشدة بالنظر في استخدام واجهة مستخدم رسومية لـ Git (Git GUI) التي يمكن أن تجعل الكثير من هذه العمليات غير مؤلمة.

طلب المراجعة: متى تتوقف عن إعادة الأساس (Rebasing)؟

بعد أن تطلب من زملائك مراجعة طلب السحب (Pull Request)، توقف عن إعادة أساس التزاماتك (rebasing your commits)! بدلاً من ذلك، ادفع التغييرات المطلوبة من قبل المراجعين في التزامات جديدة.

لماذا يجب التوقف عن إعادة الأساس بعد بدء المراجعة؟

قد تتساءل: “ألا يتعارض هذا مع هدف الحفاظ على تاريخ التزامات نظيف؟” في الواقع، الحفاظ على تاريخ التزامات نظيف لم يكن الهدف النهائي، بل وسيلة لجعل تغييراتك أسهل للفهم والمراجعة من قبل الآخرين. بمجرد أن تبدأ المراجعة، فإن تعديل التزاماتك يجعل مراجعة تغييراتك الجديدة أكثر صعوبة.

لنفترض أنك فتحت طلب سحب بهذه الالتزامات:
مثال على تسلسل التزامات في طلب سحب
(التزامات وهمية لأغراض العرض التوضيحي؛ لا ترقم رسائل الالتزام الخاصة بك فعلياً)

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

ماذا تغير منذ آخر مرة نظر فيها المراجع إلى طلب السحب؟

أي الالتزامات تم تعديلها وبالتالي تتطلب الانتباه، وأيها لم يتغير ويمكن تخطيه؟ الطريقة الوحيدة لمعرفة ذلك هي النظر إلى جميع الالتزامات التي تم دفعها قسراً، ومحاولة تذكر ما إذا كان ما تراه الآن مختلفاً عما كان موجوداً من قبل. إذا حاولت النقر على الملف الذي تم التعليق عليه لمعرفة ما إذا كان التعليق قد تم تناوله، أو للحصول على سياق إضافي للكود حول المنطقة التي تم فيها التعليق، فستُقابل بهذا المنظر المزعج:
رسالة خطأ عند محاولة عرض التغييرات القديمة بعد الدفع القسري
بالعودة إلى تشبيه قراءة الرواية – تخيل أنك وصلت إلى منتصفها، ثم تركتها ليوم أو يومين، وعندما تلتقطها مرة أخرى، يُقال لك إن أجزاء مهمة في الأجزاء التي قرأتها قد تغيرت، والطريقة الوحيدة لمعرفة ما تغير بالضبط هي قراءتها كلها مرة أخرى. هذا ليس ممتعاً على الإطلاق!

البديل الأفضل: دفع التزامات جديدة

بدلاً من ذلك، هذا ما ستبدو عليه التغييرات إذا قمت بدفع التزامات جديدة منفصلة بدلاً من التعديل القسري:
مثال على إضافة التزامات جديدة لتصحيح المراجعات
هل يمكنك تحديد ما هو جديد هنا؟

يجب عليك الاستمرار في إعادة الأساس (rebase) من فرع master، ولكن طالما أن الالتزامات التي تمت مراجعتها بالفعل لم تتغير بشكل جوهري، فلن يحتاج مراجعوك إلى مراجعتها كلها مرة أخرى. فكر في إضافة تعليق بعد عملية الدفع القسري (force-push) مع رابط إلى الالتزام الذي تمت مراجعته مؤخراً حتى يتمكن المراجعون من المتابعة من حيث توقفوا.

بعد الموافقة: دمج الكود بفعالية

بحلول الوقت الذي تتم فيه الموافقة على طلب السحب (PR) الخاص بك، من المحتمل أن يكون فرعك يحتوي الآن على بعض الالتزامات التي تبدو فوضوية بعض الشيء. أوصي هنا باستخدام الدمج السريع (squash-merging) وعدم القلق بشأن ذلك. لقد تم تحقيق الغرض من الوضوح.

ستحتوي رسائل الالتزام من طلبات السحب المدمجة بسرعة (squash-merged PRs) على روابط تعود إلى طلبات السحب حيث يمكن العثور على الالتزامات المدمجة مرة أخرى.

هل الدمج السريع (Squash Merging) لا يزال مثيراً للجدل؟

لست متأكداً ما إذا كان الدمج السريع لا يزال مثيراً للجدل في عام 2020، ولكن في حال كان كذلك – فإن مكتبة React تستخدمه! ​​♂️
مثال على استخدام React لعملية الدمج السريع (Squash Merging)
إذا قفز دان أبراموف (Dan Abramov) من جسر، فهل ستقفز أنت؟ (نعم. الإجابة الصحيحة هي “نعم”)

ومع ذلك، ربما تشعر بقوة أن الالتزامات في طلب السحب (PR) ذات معنى وأهمية كافية لتبرير دمجها مرة أخرى في فرع master كالتزامات منفصلة. إذا كان هذا هو الحال، فهذا هو الوقت الذي يمكنك فيه الانطلاق في عملية إعادة الأساس (rebasing) حتى يتم دمج جميع الفروقات في التزامات مثالية قبل الدمج دون دمج سريع (squashing).

خلاصة سريعة: أفضل الممارسات لتحسين طلبات السحب

  • يجب أن يروي كل التزام (commit) في طلب السحب (PR) قصة ما يغيره هذا الالتزام، ومن الناحية المثالية، ما الذي حفز هذا التغيير أيضاً.
  • قم بإعادة الأساس (rebase) بقوة قبل فتح طلب السحب/الدمج (pull/merge request) وطلب المراجعين.
  • بعد بدء المراجعة، توقف عن تعديل التزامات فرعك وادفع التزامات جديدة.
  • بعد الموافقة، قم بالدمج السريع (squash merge) (أو قم بدمج التزامات محددة ثم ادمجها).

ملاحظات هامة

نظراً لأن الجزء الثاني من سير العمل هذا يعتمد بشكل كبير على كيفية تعامل GitHub مع الالتزامات التي أعيد أساسها (rebased commits)، فقد يأتي يوم يتم فيه معالجة هذه المخاوف بواسطة المنصة وقد لا يعود هذا سير العمل مطلوباً. حتى ذلك الحين، يرجى النظر في تحسين التزاماتك لمراجعيك.

موارد إضافية

  • Stacked Git: هي أداة لإدارة تاريخ الالتزامات (commit histories) وجدت أنها أكثر سهولة من إعادة الأساس التفاعلي (interactive rebasing) عبر واجهة سطر الأوامر (CLI). قد يبدو الدليل التعليمي مخيفاً، ولكن قد يكون ذلك مشكلة تصميم بسبب وضع كل شيء (بما في ذلك تعليمات استخدام Emacs) في صفحة واحدة. في الواقع، من السهل جداً تعلم واستخدام جزء صغير منها في كل مرة.

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

في الختام، يُعد تحسين طلبات السحب (Pull Requests) ليس مجرد ممارسة تقنية، بل هو جزء أساسي من ثقافة التعاون الفعال في تطوير البرمجيات. إن تاريخ الالتزامات (commit history) النظيف والمنظم ليس فقط يسهل عملية مراجعة الكود، بل يعكس أيضاً احترافية المطور ويقلل من الأعباء المعرفية على فريق العمل. التمييز بين مرحلة ما قبل المراجعة، حيث يمكن استخدام rebase بحرية لترتيب الالتزامات، ومرحلة ما بعد بدء المراجعة، حيث يجب إضافة التغييرات كالتزامات جديدة، هو مفتاح لضمان سير عمل سلس وشفاف. كما أن تبني استراتيجيات الدمج مثل squash-merging بعد الموافقة يمكن أن يحافظ على نظافة الفرع الرئيسي (main branch) دون التضحية بوضوح تاريخ التطوير. هذه الممارسات لا تساهم فقط في قبول طلبات السحب بشكل أسرع، بل تعزز أيضاً جودة الكود واستقراره على المدى الطويل.

اترك تعليقاً

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