التزاماتنا البرمجية كانت قنابل موقوتة: كيف أنقذتنا ‘خطافات Git’ من جحيم ‘لقد كسر أحدهم البناء’؟

أذكرها وكأنها البارحة. كانت ليلة خميس، وأجواء المكتب بدأت تهدأ. الكل يجهز نفسه لعطلة نهاية الأسبوع، وأنا كنت أضع اللمسات الأخيرة على ميزة جديدة عملت عليها طويلاً. فجأة، علت أصوات الإشعارات من قناة “Slack” الخاصة بالفريق، تلك الإشعارات الحمراء التي لا تبشر بخير أبداً. “🔴 Build Failed on main”.

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

المشكلة ليست في المبرمج، بل في العملية

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

نحن نعتمد على أنفسنا لنتذكر قائمة طويلة من المهام قبل كل عملية git commit أو git push:

  • هل قمت بتنسيق الكود (Code Formatting)؟
  • هل قمت بتشغيل مدقق الشيفرة (Linting)؟
  • هل قمت بتشغيل كافة الاختبارات (Unit Tests)؟
  • هل رسالة الالتزام (Commit Message) تتبع المعايير المتفق عليها؟

هذه العملية اليدوية هي وصفة للكارثة. إنها مجرد مسألة وقت قبل أن ينسى أحدهم خطوة ما تحت ضغط العمل، وتنفجر “القنبلة الموقوتة” في نظام التكامل المستمر (CI/CD) لدينا.

الحل السحري: لنتعرف على خطافات Git (Git Hooks)

هنا يأتي دور البطل الصامت في عالم Git: “الخطافات” أو Git Hooks. ببساطة، خطافات Git هي عبارة عن سكربتات (scripts) برمجية يمكنك إعدادها لتعمل تلقائياً عند وقوع أحداث معينة في Git. فكر فيها كحارس أمن يقف على باب مستودعك البرمجي.

قبل أن تسمح لأي “التزام” (commit) بالدخول، يقوم هذا الحارس بفحصه والتأكد من مطابقته للقواعد. إذا لم يطابقها، يمنعه من الدخول ويرجعه لصاحبه ليقوم بتعديله.

أشهر أنواع الخطافات:

  • pre-commit: يعمل هذا الخطاف قبل إنشاء الالتزام مباشرةً. إنه المكان المثالي لتشغيل مهام سريعة مثل تنسيق الكود والتحقق من الأخطاء البسيطة (linting). إذا فشل السكربت، يتم إلغاء عملية الالتزام.
  • commit-msg: يعمل بعد كتابة رسالة الالتزام وقبل إنشائه فعلياً. رائع لفرض معايير محددة على رسائل الالتزام (مثل Conventional Commits).
  • pre-push: يعمل قبل دفع (push) التزاماتك إلى المستودع البعيد (remote repository). هذا هو المكان الأنسب لتشغيل مهام أطول وأكثر شمولاً، مثل مجموعة الاختبارات الكاملة (test suite).

تبدو فكرة رائعة، أليس كذلك؟ لكن هناك مشكلة صغيرة. ملفات الخطافات هذه تعيش داخل مجلد .git/hooks، وهذا المجلد لا يتم تتبعه بواسطة Git. هذا يعني أن كل مطور في الفريق يجب أن يقوم بإعداد هذه الخطافات يدوياً على جهازه، وإذا قمنا بتحديث أحدها، يجب على الجميع تحديثه يدوياً أيضاً. “شغلانة” ما بعدها “شغلانة” وغير عملية بالمرة.

“بس يا أبو عمر، هالشغلانة معقدة!” – نقدم لكم Husky 🐺

لهذا السبب بالذات، ظهرت أدوات لتسهيل هذه العملية. وأشهرها وأروعها برأيي هي أداة اسمها Husky. Husky هي أداة بسيطة تجعل إدارة خطافات Git أمراً سهلاً للغاية وتسمح بمشاركتها مع كل أعضاء الفريق عبر package.json.

Husky يقوم بتوصيل خطافات Git بأوامر npm scripts، مما يعني أن الإعدادات تصبح جزءاً من المشروع نفسه، ويحصل عليها كل مطور تلقائياً بمجرد تثبيت تبعيات المشروع.

لنطبق الأمر عملياً: إعداد Husky خطوة بخطوة

لنفترض أن لدينا مشروع JavaScript/TypeScript ونريد أن نتأكد من أن كل كود يتم الالتزام به منسق وخالٍ من أخطاء الـ linting.

الخطوة 1: تثبيت Husky

# باستخدام npm
npm install husky --save-dev

# أو باستخدام yarn
yarn add husky --dev

الخطوة 2: تفعيل خطافات Git في مشروعك

بعد التثبيت، قم بتشغيل هذا الأمر لمرة واحدة:

npx husky install

هذا الأمر سيقوم بإنشاء مجلد .husky في مشروعك. لجعل هذه العملية تلقائية لكل من ينضم للفريق، أضف السكربت التالي إلى ملف package.json:

# هذا يضمن أن `husky install` يعمل تلقائياً بعد كل `npm install`
npm set-script prepare "husky install"

الخطوة 3: إنشاء أول خطاف (pre-commit)

لنفترض أن لدينا سكربت اسمه npm run lint. نريد تشغيله قبل كل عملية commit. الأمر بسيط:

npx husky add .husky/pre-commit "npm run lint"

هذا الأمر سينشئ ملفاً جديداً باسم pre-commit داخل مجلد .husky يحتوي على الأمر الذي حددناه. الآن، في كل مرة يحاول أي مطور في الفريق عمل git commit، سيتم تشغيل npm run lint تلقائياً. إذا فشل الأمر، ستفشل عملية الـ commit. رائع!

نصيحة من أبو عمر: تشغيل الـ linter على المشروع بأكمله في كل مرة قد يكون بطيئاً، خاصة في المشاريع الكبيرة. هذا قد يزعج المطورين ويدفعهم لتجاوز الخطاف. فما الحل؟

شريك Husky المفضل: `lint-staged`

هنا يأتي دور أداة أخرى عبقرية تعمل بتناغم تام مع Husky، وهي lint-staged. فكرتها بسيطة: لماذا نشغل الأوامر على كل ملفات المشروع، بينما يمكننا تشغيلها فقط على الملفات التي تم تعديلها وإضافتها إلى منطقة التجهيز (staged files)؟

الخطوة 1: تثبيت `lint-staged`

npm install lint-staged --save-dev

الخطوة 2: إعداد `lint-staged`

أضف الإعدادات الخاصة به إلى ملف package.json:

// في ملف package.json
"lint-staged": {
  "*.{js,jsx,ts,tsx}": [
    "eslint --fix",
    "prettier --write"
  ],
  "*.{json,css,md}": "prettier --write"
}

هذا الإعداد يخبر lint-staged بالآتي: لأي ملف JavaScript أو TypeScript جاهز للالتزام، قم بتشغيل eslint --fix ثم prettier --write عليه. ولأي ملفات أخرى مثل JSON أو CSS، قم فقط بتنسيقها باستخدام Prettier.

الخطوة 3: تحديث خطاف `pre-commit`

الآن، بدلاً من تشغيل npm run lint، سنجعل الخطاف يشغل lint-staged:

npx husky set .husky/pre-commit "npx lint-staged"

والآن، أصبحت العملية آلية وذكية وسريعة جداً. قبل كل commit، سيتم فحص وتنسيق الملفات المعدلة فقط. يا سلام! انتهى زمن الالتزامات البرمجية الفوضوية.

سيناريوهات متقدمة ونصائح من الميدان

بمجرد أن تتقن الأساسيات، يمكنك فعل الكثير مع Husky.

فرض معايير على رسائل الالتزام (Commit Messages)

تاريخ Git النظيف هو كنز لا يقدر بثمن. باستخدام أداة مثل commitlint مع خطاف commit-msg، يمكنك إجبار الفريق على اتباع نمط معين في كتابة الرسائل (مثل Conventional Commits)، مما يسهل تتبع التغييرات وإنشاء سجلات التغيير (Changelogs) تلقائياً.

# تثبيت الأدوات
npm install @commitlint/cli @commitlint/config-conventional --save-dev

# إنشاء ملف الإعدادات
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

# إضافة الخطاف
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

تشغيل الاختبارات قبل الدفع (pre-push)

لتجنب كسر “البناء” على الفرع الرئيسي، يمكنك إضافة خطاف pre-push لتشغيل الاختبارات الأساسية.

npx husky add .husky/pre-push "npm test"

نصيحة عملية جداً من أبو عمر: كن حذراً مع خطاف pre-push. إذا كانت مجموعة الاختبارات لديك تستغرق 5 أو 10 دقائق، فسيشعر المطورون بالإحباط وسيبحثون عن طرق لتجاوزها (مثل استخدام git push --no-verify). القاعدة الذهبية هي: اجعل الخطافات المحلية سريعة. استخدم pre-push للاختبارات السريعة والحرجة فقط، واترك الاختبارات الشاملة والطويلة لخادم الـ CI/CD. التوازن هو مفتاح النجاح.

الخلاصة: من قنابل موقوتة إلى التزامات واثقة 😌

رحلتنا بدأت من الفوضى والإحباط بسبب أخطاء كان من الممكن تفاديها بسهولة. المشكلة لم تكن في قدراتنا كمبرمجين، بل في اعتمادنا على عمليات يدوية هشة. من خلال تبني الأتمتة في أبسط صورها عبر خطافات Git، وبمساعدة أدوات مثل Husky و lint-staged، حولنا عملية الالتزام البرمجي من حقل ألغام إلى مسار آمن وموثوق.

النتائج كانت مذهلة:

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

يا جماعة، استثمار بضع دقائق في إعداد هذه الأدوات سيوفر عليكم ساعات، بل أياماً، من وجع الراس لاحقاً. أتمتة هذه العمليات ليست رفاهية، بل هي من أساسيات الهندسة البرمجية الحديثة التي تفرق بين فريق هاوٍ وفريق محترف. ابدأوا اليوم، وسترون الفرق بأنفسكم. يلا، شدّوا حيلكم! 💪

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

التوظيف وبناء الهوية التقنية

كانت إجاباتي في المقابلات التقنية كارثية: كيف أنقذني إطار STAR من جحيم ‘حدثني عن موقف صعب واجهته؟’

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

25 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كان كل طلب يضرب قاعدة البيانات: كيف أنقذنا النظام بـ ‘التخزين المؤقت الموزع’ (Distributed Caching)؟

أشارككم قصة حقيقية عن كيفية انهيار نظام تحت ضغط الطلبات، وكيف كان "التخزين المؤقت الموزع" باستخدام Redis هو طوق النجاة. سنتعمق في المفهوم، ونرى أمثلة...

25 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

من الإنذار الكاذب إلى الكشف الذكي: كيف أنقذنا نماذج الاحتيال المالي من بحر التنبيهات الخاطئة؟

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

25 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت بنيتنا التحتية قصراً من رمال: كيف أنقذنا Terraform من جحيم “مين غيّر هالإعداد؟”

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

25 مايو، 2026 قراءة المزيد
اختبارات الاداء والجودة

كانت تغطية اختباراتنا 100% ثقة زائفة: كيف أنقذنا ‘الاختبار الطفري’ (Mutation Testing) من جحيم ‘الاختبارات التي لا تكتشف شيئًا’؟

أشارككم قصة حقيقية من الميدان، حين كنا نظن أن تغطية اختباراتنا بنسبة 100% هي درعنا الحصين، لنكتشف أنها كانت وهمًا كبيرًا. هذه المقالة تشرح كيف...

25 مايو، 2026 قراءة المزيد
نصائح برمجية

كانت أرقامي السحرية طلاسم لا تُقرأ: كيف أنقذتنا ‘الثوابت المسماة’ من جحيم ‘ماذا يعني هذا الرقم؟’

في عالم البرمجة، الأرقام العشوائية في الكود أو "الأرقام السحرية" هي وصفة لكارثة مستقبلية. في هذه المقالة، أشارككم قصة من واقع تجربتي وكيف أن استخدام...

25 مايو، 2026 قراءة المزيد
البودكاست