الحد الأدنى من قواعد Git لتفادي الكوارث
ست قواعد لفريق من مطوّرَين إلى ثمانية، والإعدادات التي تجعل المنصة تفرضها، وما تفعله إذا وصلت كلمة مرور إلى المستودع.
مساء الخميس، بعد السادسة بقليل. يدفع أحد المطوّرين إصلاحًا أخيرًا إلى الفرع main مباشرة قبل عطلة نهاية الأسبوع، ويرفعه إلى الخادم. يبدأ موقع العميل بإظهار الأخطاء. المطوّر في طريقه إلى البيت، ولا أحد غيره يعرف ما الذي تغيّر.
الفرق الصغيرة، من مطوّرَين إلى ثمانية، تستغني في الغالب عن أي قواعد للعمل، لأن القواعد تبدو شأنًا يخص شركات فيها خمسون مهندسًا. أسلوب العمل المعروض هنا ست قواعد تكفي في الغالب فريقًا بهذا الحجم، ومعظمها تفرضه منصة استضافة Git نفسها بعد تفعيل إعداداتها.
أربع كوارث يمنعها هذا الأسلوب
- عمل يُمحى. يعدّل اثنان الملفات نفسها، ثم يدفع أحدهما دفعًا قسريًا (force push) أو ينسخ مجلدًا فوق ما على الخادم، فيضيع عمل يوم كامل لزميله.
- الفرع main معطّل مساء الخميس. يدخله تعديل لم يُختبر، فيرثه أول من يسحب الشيفرة بعده، أو أول إصدار يُشغَّل على الخادم.
- كلمة مرور أُودعت في المستودع. كلمة مرور قاعدة البيانات أو مفتاح API في ملف إعدادات، وقد صارت في السجل وفي كل نسخة من المستودع.
- إصدار لا يستطيع أحد إعادة بنائه. النسخة العاملة على خادم الإنتاج بُنيت على حاسوب أحد المطوّرين من تعديلات لم تُودَع قط، فلا يعرف أحد أي شيفرة تعمل الآن.
أسلوب العمل في ست قواعد
- الفرع main صالح للتشغيل دائمًا. كل ما على
mainيمكن أن يذهب إلى خادم الإنتاج الآن. والعمل غير المكتمل مكانه خارجه. - العمل يجري على فروع قصيرة العمر. فرع لكل مهمة، يُنشأ من
mainويُدمج فيه خلال يوم أو يومين ثم يُحذف. - كل تعديل يصل إلى main عبر طلب دمج. لا دفع مباشرًا، ولا استثناء لقائد الفريق.
- يراجعه شخص آخر. لا أحد يوافق على تعديله هو. وفي فريق من اثنين يراجع كلٌّ منكما عمل الآخر.
- فحوص آلية تعمل على كل طلب دمج. ابدأ بما عندك: البناء، وأداة فحص الأسلوب (linter)، وبضعة اختبارات. حتى الفحص الذي يكتفي بالتأكد من أن المشروع ما زال يُبنى يكشف نوعًا كاملًا من أخطاء مساء الخميس.
- الفرع main محمي. ترفض المنصة كل ما يخالف القواعد من 3 إلى 5، فلا يتوقف الالتزام على ذاكرة أحد.
كل قاعدة والإعداد الذي يفرضها
تختلف الأسماء من منصة إلى أخرى، لكن هذه الإعدادات موجودة في معظم المنصات. فعّلها مرة واحدة على الفرع main.
| القاعدة | الإعداد الذي يفرضها |
|---|---|
| لا يصل تعديل إلى main إلا عبر طلب دمج | حماية الفرع main: منع الدفع المباشر، حتى على مديري المستودع |
| يراجع التعديل شخص آخر | موافقة واحدة مطلوبة على الأقل، ولا تُحتسب موافقة صاحب التعديل |
| تنجح الفحوص قبل الدمج | فحوص حالة (status checks) إلزامية: البناء، وفحص الأسلوب، والاختبارات |
| لا تُعاد كتابة سجل main | منع الدفع القسري ومنع حذف الفرع |
| الفروع قصيرة العمر | حذف الفرع تلقائيًا بعد دمجه |
| الإصدارات تخرج من الوسوم | المسار الآلي (pipeline) لا يشغّل إصدارًا إلا عند وسم (tag) إصدار، ولا ينشئ هذا الوسم إلا المسؤولون عن المستودع |
| لا أسرار في المستودع | ملف .gitignore للملفات المحلية، وفحص يكشف الأسرار عند كل دفع إن وفّرته المنصة |
| الصلاحيات لكل شخص باسمه | حسابات ومفاتيح فردية، ولا حساب مشترك، ومراجعة قائمة الأعضاء عند مغادرة أي شخص |
أبقِ طلبات الدمج صغيرة
المراجِع الذي يستلم تعديلًا تسعه شاشة واحدة يقرأ كل سطر فيه. والذي يستلم تعديلًا يمسّ ثلاثين ملفًا يمرّ عليه سريعًا، فلا يجد شيئًا، ويوافق.
وطلبات الدمج الصغيرة تُدمج أسرع، ويقلّ تعارضها مع عمل الآخرين، ويسهل التراجع عنها. ولإبقائها صغيرة اجعل لكل طلب غرضًا واحدًا، وافصل ترتيب الشيفرة عن تغيير سلوكها، وادمج الميزات غير المكتملة خلف مفتاح يبقيها معطّلة بدل أن تترك فرعًا مفتوحًا ثلاثة أسابيع. واكتب في الوصف ما الذي تغيّر، ولماذا، وكيف اختبرته.
والشيفرة التي لم يكتبها إنسان تسلك المسار نفسه. إذا كان فريقك يستخدم مساعد ذكاء اصطناعي داخل المحرّر، فمقترحاته تمرّ بطلب دمج وبمراجعة بشرية مثل أي تعديل آخر.
شغّل الإصدار من المسار الآلي، ومن إيداع موسوم
تشغيل الإصدار على الخادم (deploy) من حاسوب محمول يرسل معه كل ما على ذلك الحاسوب: تعديلات لم تُودَع، ونسخة مختلفة من إحدى المكتبات، وملف إعدادات محلي. انقل هذه الخطوة إلى المسار الآلي، واجعل ما يطلقها وسمًا على الفرع main:
على main بعد تحديثه: git tag v1.8.0 ثم git push origin v1.8.0
يبني المسار الآلي ذلك الإيداع بعينه ويشغّله. ولأن الوسم يمكن أن يشير إلى أي إيداع، اجعل المسار الآلي يرفض الوسم الذي ليس على main. عندها يصير لسؤال «ما الذي يعمل على خادم الإنتاج؟» جواب من كلمة واحدة، ويصير الرجوع إلى شيفرة الإصدار السابق تشغيلًا لوسمه. وبعد أن يستقر المسار الآلي احذف بيانات الدخول إلى الخادم من حواسيب المطوّرين، ليُغلق الطريق القديم.
الأسرار والصلاحيات
كلمات المرور ومفاتيح API والمفاتيح الخاصة لا تدخل المستودع. احفظها في خزنة الأسرار التابعة للمسار الآلي أو في متغيرات البيئة على الخادم، وأدرج الملفات المحلية مثل .env في ملف .gitignore، وأودِع ملفًا نموذجيًا فيه الأسماء بلا قيم.
وإذا أُودع سرٌّ مع ذلك فالترتيب مهم:
- غيّره أولًا. بدّل كلمة المرور أو أبطل المفتاح لدى الخدمة التي يخصّها، وحدّث خزنة الأسرار. وعُدَّه مكشوفًا منذ لحظة دفعه، وراجع سجلات تلك الخدمة بحثًا عن استعمال لا تعرفه.
- ثم نظّف. احذفه من الشيفرة. أما محوه من السجل فاختياري: يحتاج إلى دفع قسري منسَّق وإلى أن يستنسخ الجميع المستودع من جديد.
حذف الإيداع وحده لا يكفي. السر باقٍ في كل نسخة استُنسخت أو سُحبت في تلك الأثناء وفي النسخ الاحتياطية للمستودع، ولا سبيل إلى معرفة من قرأه.
والصلاحيات على المبدأ نفسه: حساب ومفتاح لكل شخص، ولا حساب مشترك، وتُسحب الصلاحية يوم يغادر صاحبها، ومعها رموز الوصول والمفاتيح التي أنشأها. والحساب المؤسسي الذي يضم المستودعات يجب أن يكون ملكًا للشركة لا حسابًا شخصيًا لأحد المطوّرين، للأسباب المشروحة في مقال أين تعيش شيفرتك قرارٌ تجاري لا تقني.
الإصلاح العاجل يسلك المسار نفسه، بسرعة أكبر
حين يتوقف خادم الإنتاج يغريك الموقف بتعديل الملف على الخادم مباشرة أو بالدفع إلى main. اسلك المسار نفسه: فرع من main، وأصغر تعديل يعالج المشكلة، وطلب دمج، ومراجِع تتصل به هاتفيًا لينظر فيه الآن، ثم الفحوص، فالوسم، فالتشغيل. مصدر السرعة هنا صغر التعديل وتقديم مراجعته على غيرها، وتبقى كل خطوة مسجَّلة. وإذا كانت الفحوص أبطأ من أن تحتملها حالة طارئة فذلك سبب لتسريعها في الأيام كلها.
ما يُترك لوقت لاحق
نماذج التفريع المعقدة التي تفصل بين فروع للتطوير وللإصدار وللإصلاح العاجل، وفروع الإصدار الطويلة العمر، تعالج مشكلات قلّما يواجهها فريق بهذا الحجم. فهي تزيد عمليات الدمج، ومعها المواضع التي يضيع فيها العمل. وتصير في محلها حين تتولى صيانة عدة إصدارات في وقت واحد، كمنتج مثبَّت عند عملاء مختلفين بإصدارات مختلفة.
للبدء، فعّل الصفوف الثلاثة الأولى من الجدول على المنصة التي تستخدمها الآن. وفي قواعد المراجعة والفحوص، توفّر PearlGit، وهي خدمة استضافة Git للفرق من PearlFibers، طلبات دمج مع نقاش على سطور الشيفرة، ومراجعين إلزاميين يُحدَّدون لكل فرع، وفحوص حالة تمنع الدمج إلى أن تنجح، ومسارات آلية تعمل عند كل دفع وكل طلب دمج وتعرض نتائجها على الطلب نفسه. وواجهتها متاحة بالعربية والإنجليزية. التفاصيل في صفحة PearlGit.