يا الله شو بتذكر هداك اليوم… كان يوم خميس، وكنا على وشك إطلاق ميزة جديدة ومهمة في المشروع. واحد من الشباب الطيبة في الفريق، شب شاطر واسمه “خالد”، فتح طلب سحب (Pull Request) فيه الشغل كله. أنا كمسؤول عن الفريق، فتحت الكود لأعمل مراجعة، وتوقعت أناقش معه منطق العمل، أو أداء جزئية معينة، أو حتى بنية الكود المعمارية.
لكن اللي صار إشي ثاني خالص. أول ما فتحت الكود، عيني وقعت على أسطر Python مكتوبة بعلامات تنصيص مفردة (‘)، وفريقنا متفق (نظرياً) على استخدام المزدوجة (“). وتحت شوي، لقيت مسافة زايدة في آخر سطر (trailing whitespace). وقبل ما أكتب أي تعليق، لقيت زميل ثاني سبَقني وكتب: “يا خالد، لو سمحت استخدم double quotes حسب الـ style guide”. وبعدها بخمس دقايق، دخل زميل ثالث وعلّق على المسافة الزايدة.
خلال نص ساعة، الـ Pull Request تحوّل لساحة جدال عقيم. خالد يدافع عن حاله إنه محرر الكود (IDE) تبعه هيك إعداداته، والشباب الثانيين مصرّين على تطبيق “المعايير”. وولّعت بين الشباب على إشي تافه ما بستاهل. ضاع وقت ثمين، وتوترت الأجواء، والأهم من كل هاد، إنه ولا واحد فينا ناقش منطق الكود نفسه! وقتها صفنت وقلت لحالي: “يا أبو عمر، مش منطق هالحكي! لازم نلاقي حل جذري يخلّصنا من هالقصص هاي للأبد”.
الكيل طفح: عندما قررنا أن الجدال يجب أن يتوقف
كانت هذه الحادثة هي القشة التي قصمت ظهر البعير. المشكلة لم تكن في خالد أو في أي شخص آخر، بل كانت في عمليتنا نفسها. الاعتماد على الانضباط البشري في أمور روتينية ومملة مثل تنسيق الكود هو وصفة للفشل ولإضاعة الوقت. البشر ينسون، يخطئون، ولكل منهم تفضيلاته وأدواته. النقاش حول ما إذا كانت الفاصلة يجب أن تكون في نهاية السطر أم لا، هو نقاش يجب ألا يخوضه المبرمجون في عام 2024.
الحل واضح: الأتمتة. يجب أن نوكل هذه المهمة للآلات، ونحرر عقولنا للتركيز على ما يهم حقًا: حل المشكلات وبناء برمجيات ذات قيمة. قررنا أن أي كود يصل إلى مرحلة طلب السحب (Pull Request) يجب أن يكون منسقًا وخاليًا من الأخطاء الأسلوبية الشائعة بشكل تلقائي. وهنا بدأت رحلة بحثنا عن الأداة المثالية، وهي الرحلة التي انتهت بنا إلى بطل قصتنا: إطار pre-commit.
الحل السحري: تعرف على إطار pre-commit
ببساطة، pre-commit هو إطار عمل لإدارة وتشغيل “خطافات Git” أو ما يعرف بـ (Git Hooks). والـ Git Hook هو مجرد سكربت صغير يتم تشغيله تلقائيًا عند نقاط معينة في دورة حياة Git، مثل قبل عملية الـ commit (وهذا هو الـ pre-commit hook) أو قبل عملية الـ push.
قد يقول قائل: “ما أنا بقدر أكتب سكربتاتي الخاصة وأحطها في مجلد .git/hooks يدويًا”. صحيح، ولكن pre-commit يقدم مزايا جبارة تجعل الطريقة اليدوية شيئًا من الماضي:
- مركزي وقابل للمشاركة: يتم تعريف كل الإعدادات في ملف واحد اسمه
.pre-commit-config.yamlتضعه في مشروعك. هذا يعني أن كل أعضاء الفريق سيستخدمون نفس الأدوات والقواعد بمجرد سحبهم للمشروع. - إدارة تلقائية للأدوات: هل تريد استخدام أداة مثل
blackلتنسيق كود بايثون؟ لا حاجة لتثبيتها يدويًا عند كل زميل.pre-commitسيقوم بتنزيلها وإدارتها في بيئة معزولة خاصة به. - متعدد اللغات: يدعم عددًا هائلاً من الأدوات لمختلف اللغات والتقنيات (Python, JavaScript, Terraform, YAML, Dockerfiles, والمزيد).
- سهولة الاستخدام: بمجرد إعداده، يصبح استخدامه شفافًا تمامًا للمطور.
خطوات عملية: كيف طبقنا pre-commit في مشروعنا؟
دعونا ننتقل من التنظير إلى التطبيق. سأريكم بالضبط كيف قمنا بإعداد pre-commit في أحد مشاريع بايثون لدينا. العملية بسيطة ومباشرة.
الخطوة الأولى: التثبيت
أولاً، يجب تثبيت الأداة نفسها. نحن نستخدم بايثون، فالأمر بسيط باستخدام pip:
pip install pre-commit
هذا الأمر يتم تشغيله مرة واحدة على جهاز كل مطور.
الخطوة الثانية: إنشاء ملف الإعدادات
في جذر المشروع، أنشئ ملفًا جديدًا باسم .pre-commit-config.yaml. هذا هو قلب النظام. سنبدأ ببعض الخطافات العامة والمفيدة جدًا لأي مشروع:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 # استخدم دائمًا أحدث إصدار مستقر
hooks:
- id: trailing-whitespace # يزيل المسافات الزائدة في نهاية الأسطر
- id: end-of-file-fixer # يتأكد من وجود سطر فارغ في نهاية كل ملف
- id: check-yaml # يتأكد من صحة بناء ملفات YAML
- id: check-added-large-files # يمنع إضافة ملفات كبيرة الحجم عن طريق الخطأ
نصيحة من أبو عمر: دائمًا حدد رقم الإصدار (
rev) لكل مستودع. هذا يضمن أن جميع أعضاء الفريق يستخدمون نفس إصدار الأداة بالضبط، مما يمنع حدوث مفاجآت غير متوقعة.
الخطوة الثالثة: إضافة أدوات التنسيق والتدقيق (Linters & Formatters)
الآن نضيف الأدوات المتخصصة. في عالم بايثون، هناك أداتان لا أستغني عنهما أبدًا:
- Black: أداة تنسيق كود “عنيدة” (The Uncompromising Code Formatter). جمالها يكمن في أنها لا تترك لك أي خيار. هي تفرض أسلوبًا واحدًا فقط، وبذلك تقتل كل الجدالات الأسلوبية.
- Ruff: أداة تدقيق (linter) سريعة جدًا جدًا، مكتوبة بلغة Rust. تبحث عن الأخطاء المحتملة، الكود غير المستخدم، والممارسات السيئة.
لنضفهما إلى ملف الإعدادات:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 24.3.0
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.6
hooks:
- id: ruff
args: [--fix] # نجعل ruff يصلح الأخطاء البسيطة تلقائيًا
- id: ruff-format
الخطوة الرابعة: تثبيت الـ Hooks في مستودع Git المحلي
بعد إعداد الملف، كل ما على المطور فعله هو تشغيل هذا الأمر مرة واحدة فقط بعد استنساخ المشروع (clone):
pre-commit install
هذا الأمر يقوم بقراءة ملف .pre-commit-config.yaml وبتثبيت الخطافات (hooks) في مجلد .git/hooks الخاص بالمستودع المحلي. انتهى! الآن النظام جاهز للعمل.
الحياة بعد pre-commit: من جحيم الجدال إلى نعيم الإنتاجية
الآن، تخيل معي السيناريو الجديد. زميلنا خالد انتهى من كتابة الكود الخاص به. يفتح الطرفية (terminal) ويكتب:
git commit -m "feat: add user authentication"
قبل أن تتم عملية الـ commit، يبدأ pre-commit بالعمل تلقائيًا. يمر على كل الملفات التي تم تعديلها ويشغل الأدوات التي حددناها:
trailing-whitespaceيجد مسافة زائدة ويحذفها.blackيجد أن خالد استخدم علامات تنصيص مفردة، فيقوم بإعادة تنسيق الملف بالكامل ليستخدم علامات مزدوجة.ruffيكتشف متغيرًا لم يتم استخدامه، فيعرض رسالة خطأ.
لأن بعض الأدوات قامت بتعديل الملفات (مثل black)، ستفشل عملية الـ commit الأولى، وستظهر رسالة تخبر خالد أن الملفات قد تم تعديلها. كل ما عليه فعله هو مراجعة التعديلات وإضافتها مرة أخرى (git add .) ثم إعادة محاولة الـ commit. هذه المرة، ستمر العملية بنجاح لأن الكود أصبح نظيفًا ومنسقًا.
النتيجة؟ طلب السحب (Pull Request) الذي يصلني الآن هو طلب سحب نظيف 100% من الناحية الأسلوبية. مراجعات الكود تحولت من جدالات حول الفواصل والمسافات إلى نقاشات مثمرة حول بنية الكود، والمنطق، والأداء. صارت مراجعة الكود ممتعة ومفيدة. حتى أني سمعت أحد الشباب يقول لي: “أبو عمر، والله ريّحتنا. صرت أفتح الـ PR وأنا مرتاح البال، عارف إنه الأساسيات كلها تمام”.
نصائح من خبرة أبو عمر
بعد استخدام هذه الأداة في عدة مشاريع ومع فرق مختلفة، جمعت لكم بعض النصائح العملية:
- ابدأ بسيطًا: لا تضف 20 خطافًا من اليوم الأول. ابدأ بالأدوات الأساسية (مثل
pre-commit-hooksوblack) ثم أضف المزيد تدريجيًا كلما احتاج الفريق لذلك. - الاتفاق هو المفتاح: قبل فرض أداة تنسيق، يجب أن يتفق الفريق عليها. الهدف هو الاتساق، وليس فرض أسلوب شخص واحد. أدوات مثل
blackأوprettier(لعالم JavaScript) ممتازة لأنها “ديكتاتورية” ولا تترك مجالاً للنقاش. - لا تنسَ الـ CI/CD: أضف خطوة في مسار التكامل المستمر (CI pipeline) الخاص بك لتشغيل
pre-commit. هذا يضمن أنه حتى لو قام أحدهم بتجاوز الخطافات محليًا (باستخدامgit commit --no-verify)، سيتم اكتشاف المشكلة على الخادم. يمكنك استخدام الأمر:pre-commit run --all-files. - تخصيص الأدوات: معظم أدوات التدقيق (Linters) تسمح لك بتخصيص قواعدها. على سبيل المثال، يمكنك إنشاء قسم
[tool.ruff]في ملفpyproject.tomlلتجاهل قواعد معينة أو لتحديد طول السطر الأقصى. - لكل لغة دوائها: تذكر أن
pre-commitليس حكرًا على بايثون. استكشف قائمة الخطافات المتاحة، ستجد كنوزًا لكل لغة وتقنية تستخدمها.
الخلاصة: استثمر في الأتمتة، واربح وقتك 🏆
في النهاية، أتمتة جودة وتنسيق الكود ليست رفاهية، بل هي ضرورة حتمية لأي فريق برمجي يطمح للإنتاجية والاحترافية. إنها استثمار صغير في الإعداد الأولي، ولكنه يوفر ساعات لا تحصى من الوقت والجهد والنقاشات العقيمة على المدى الطويل.
إطار pre-commit كان بمثابة المنقذ لفريقنا، حيث حوّل عملية مراجعة الكود من مهمة محبطة إلى فرصة للتعلم والتركيز على ما هو مهم حقًا. جربوها يا جماعة، وادعولي. صدقوني، راح تتذكروا أيّام الجدالات التافهة وتضحكوا عليها. 😉