أذكرها وكأنها البارحة، ليلة خميس والساعة تجاوزت منتصف الليل. كنا في خضم إطلاق ميزة جديدة ومهمة، وفجأة، بدأت التنبيهات تنهال على هواتفنا كالمطر: خطأ فادح في بيئة الإنتاج! توقف كل شيء، والضغط وصل عنان السماء. كان علينا تحديد سبب المشكلة وإصلاحها فورًا.
كنا نعرف أن المشكلة ظهرت بعد آخر عملية دمج (merge) للكود. وبكل ثقة (ساذجة وقتها)، فتحت الطرفية وكتبت الأمر السحري: git log --oneline. وهنا بدأت الكارثة الحقيقية. ما ظهر أمامي كان أشبه بنص مشفر من روايات دان براون، سلسلة لا نهائية من رسائل الـ commit التي لا تقول شيئًا:
a1b2c3d (HEAD -> main) Merge pull request #123 from dev/feature-x
f4g5h6i Update files
e7d8c9b Fix
a0b1c2d More updates
c3d4e5f Stuff
...
“Fix”؟ أي إصلاح؟ “Update files”؟ أي ملفات ولماذا؟ “Stuff”؟ بالله عليك يا زميلي، ما هذا الـ “Stuff” الذي كلفنا ليلتنا؟! كنا كمن يبحث عن إبرة في كومة قش عملاقة. بعد ساعات من البحث اليدوي المؤلم والمقارنة بين الـ commits، وجدنا التغيير المسبب للمشكلة. لكن السؤال الذي بقي يتردد في ذهني تلك الليلة: “هل هذا هو أفضل ما يمكننا فعله؟ هل محكوم علينا أن نعيش في جحيم الـ git log هذا إلى الأبد؟”.
من رحم تلك المعاناة، بدأت رحلة البحث عن حل، رحلة انتهت بنا إلى تبني فلسفة غيرت كل شيء. وهذه هي قصتنا مع “Conventional Commits”.
ما هو الجحيم الذي أتحدث عنه؟ (مشكلة الـ Commits العشوائية)
قبل أن ننتقل للحل، دعونا نعترف بالمشكلة. المشكلة التي وصفتها في قصتي ليست حالة نادرة، بل هي الوضع الافتراضي في كثير من فرق العمل. حين لا توجد قواعد واضحة لكتابة رسائل الـ commit، يكتب كل مطور ما يخطر بباله في تلك اللحظة. والنتيجة؟
- سجل تغييرات عديم الفائدة: يصبح
git logمجرد ضوضاء، من المستحيل فهم تسلسل التغييرات أو تاريخ تطور المشروع منه. - صعوبة تتبع الأخطاء (Bugs): كما حدث معنا، يصبح تحديد الـ commit الذي أدخل خطأً معينًا مهمة شبه مستحيلة.
- استحالة إنشاء سجلات التغيير (Changelogs) تلقائيًا: كيف يمكنك أن تخبر المستخدمين بما هو جديد في الإصدار 2.1.0 إذا كان سجل الـ commits الخاص بك عبارة عن “updates” و “fixes”؟
- مراجعات الكود (Code Reviews) أصعب: عندما تراجع طلب سحب (Pull Request) يحتوي على 10 commits برسائل غامضة، يصعب عليك فهم القصة التي يحاول المطور سردها من خلال تغييراته.
من الآخر، الشغل بكون “ملخبط” وغير احترافي. وهذا لا يؤثر فقط على الإنتاجية، بل على معنويات الفريق أيضًا.
الضوء في آخر النفق: معيار ‘Conventional Commits’
وسط هذا الظلام، وجدنا ضالتنا في معيار بسيط لكنه عبقري يسمى Conventional Commits. القصة وما فيها، هو عبارة عن اتفاقية أو مجموعة قواعد خفيفة لكيفية تنسيق رسائل الـ commit الخاصة بك. إنه ليس أداة جديدة أو برنامج معقد، بل هو مجرد “وصفة” لكتابة الرسائل.
الفكرة الأساسية هي إعطاء هيكل واضح لرسائل الـ commit، مما يسمح للبشر والآلات بفهمها بسهولة. الهيكل العام يبدو هكذا:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
قد يبدو معقدًا للوهلة الأولى، لكنه في الحقيقة بسيط جدًا. دعونا نفصّله جزءًا جزءًا.
تشريح رسالة الـ Commit المثالية
1. النوع (Type) – إجباري
هذا هو أهم جزء. يخبرنا عن “نوع” التغيير الذي قمت به. أشهر الأنواع هي:
feat: لإضافة ميزة جديدة (feature). هذا النوع هو الذي يظهر غالبًا في سجل التغييرات للمستخدمين.fix: لإصلاح خطأ (bug). هذا أيضًا يظهر في سجل التغييرات.docs: لتغييرات تتعلق بالتوثيق فقط (documentation).style: تغييرات لا تؤثر على معنى الكود (مسافات بيضاء، تنسيق، فواصل منقوطة ناقصة، إلخ).refactor: إعادة صياغة للكود لا تصلح خطأً ولا تضيف ميزة جديدة.perf: تغيير في الكود يحسن الأداء (performance).test: إضافة اختبارات ناقصة أو تصحيح اختبارات موجودة.chore: تغييرات في عملية البناء أو الأدوات المساعدة والمكتبات مثل إدارة الحزم (e.g. updating dependencies).ci: تغييرات في ملفات وإعدادات التكامل المستمر (CI configurations).
2. النطاق (Scope) – اختياري
هذا الجزء يحدد القسم من قاعدة الكود الذي تأثر بالتغيير. على سبيل المثال: auth, api, ui, database. يساعد هذا في توفير سياق إضافي بسرعة.
مثال: feat(auth): add social login with Google
3. الوصف (Description) – إجباري
وصف قصير وواضح للتغيير. يجب أن يبدأ بحرف صغير ولا ينتهي بنقطة.
4. المتن (Body) – اختياري
إذا كان التغيير معقدًا، يمكنك إضافة شرح أطول هنا. في هذا الجزء، تشرح “لماذا” قمت بهذا التغيير، وليس فقط “ماذا” غيرت. ما هي المشكلة التي يحلها؟ ما هو السياق؟
5. التذييل (Footer) – اختياري
يستخدم لمعلومات إضافية. أهم استخدامين له هما:
- التغييرات الكاسرة (Breaking Changes): إذا كان الـ commit الخاص بك يكسر التوافق مع الإصدارات السابقة، يجب أن تذكر ذلك بوضوح هنا، بدءًا بالكلمة
BREAKING CHANGE:. هذا أمر بالغ الأهمية! - ربط المشاكل (Referencing Issues): يمكنك إغلاق “issue” على GitHub أو GitLab تلقائيًا باستخدام كلمات مثل
Closes #123.
مثال على رسالة Commit متكاملة
feat(api): allow users to change their email address
Previously, users could only set their email at signup. This change
introduces a new endpoint /api/v2/user/email to allow authenticated
users to update their email address.
A confirmation email will be sent to the new address before the
change takes effect.
Closes #45, #48
BREAKING CHANGE: The user profile endpoint /api/v1/profile no longer
returns the 'email' field. It must be fetched from the new dedicated
/api/v2/user/email endpoint.
لاحظ الفرق الشاسع بين هذا وبين “Update files”!
من النظرية إلى التطبيق: كيف بدأنا الرحلة؟
يا جماعة، المعرفة وحدها لا تكفي. التحدي الحقيقي كان في تطبيق هذا المعيار في فريقنا. مررنا بثلاث مراحل أساسية:
الخطوة الأولى: الإقناع والاتفاق
عقدت اجتماعًا قصيرًا مع الفريق. لم أبدأ بإلقاء الأوامر، بل عرضت عليهم قصة ليلة “الجحيم” التي مررنا بها. عرضت عليهم سجل git log الفوضوي، ثم عرضت عليهم كيف يمكن أن يبدو مع Conventional Commits. ركزت على الفوائد التي ستعود علينا جميعًا: سجل أوضح، تتبع أسهل، ومراجعات كود أسرع. عندما يرى الناس أن الحل سيوفر عليهم وقتًا وجهدًا، يصبحون أكثر تقبلاً له.
الخطوة الثانية: الأدوات المساعدة (وهي سر النجاح)
الاعتماد على الانضباط البشري وحده غالبًا ما يفشل. لذلك، استعنّا ببعض الأدوات لجعل العملية تلقائية وسهلة:
- Commitizen: أداة رائعة تساعدك على كتابة رسائل commit متوافقة مع المعيار خطوة بخطوة. بدلًا من كتابة
git commit، تكتبgit cz، فتقوم الأداة بسؤالك عن النوع، النطاق، الوصف، إلخ. هذا يزيل عبء التذكر عن المطور. - commitlint: هذه الأداة هي “شرطي المرور”. تقوم بفحص رسالة الـ commit قبل قبولها للتأكد من أنها تتبع القواعد. إذا لم تكن متوافقة، يتم رفض الـ commit مع رسالة توضح الخطأ.
- husky: أداة تسمح بتشغيل سكربتات عند أحداث معينة في Git (مثل قبل الـ commit أو قبل الـ push). استخدمناها لتشغيل
commitlintتلقائيًا على كل commit.
هذا الثلاثي (Commitizen, commitlint, husky) كان له أثر السحر. لقد حولوا القاعدة من “شيء يجب أن نتذكره” إلى “جزء آلي من سير العمل”.
الخطوة الثالثة: جني الثمار
لم يمض وقت طويل حتى بدأنا نرى النتائج. أصبح سجل git log الخاص بنا يبدو هكذا:
d2a9f1e (HEAD -> main) fix(auth): correct redirect URL after password reset
c8b3e5a feat(ui): add loading spinner to data tables
a1b7c4d docs(readme): update setup instructions
f3e2d1c refactor(api): simplify user query logic
...
يا له من منظر مريح للعين! أصبحنا قادرين على:
- إنشاء سجلات التغيير (Changelogs) بضغطة زر: باستخدام أدوات مثل
standard-version، أصبحنا نصدر إصدارات جديدة مع سجل تغييرات مفصل وجميل يتم إنشاؤه تلقائيًا من رسائل الـ commits. - فهم تاريخ المشروع بسرعة: أي مطور جديد ينضم للفريق يمكنه الآن فتح
git logوفهم ما حدث في المشروع خلال الأشهر الماضية. - تحديد الإصدارات الدلالية (Semantic Versioning) تلقائيًا: الأداة تعرف أنه إذا كان هناك
feat، فيجب زيادة الرقم الثاني من الإصدار (e.g., 1.2.0). وإذا كان هناكBREAKING CHANGE، فيجب زيادة الرقم الأول (e.g., 2.0.0).
نصائح من خبرة أبو عمر
- ابدأ ببساطة: لا تفرض كل أنواع الـ commits من اليوم الأول. ابدأ بالأساسيات:
feat,fix,chore. ثم أضف المزيد حسب حاجة فريقك. - الأتمتة هي صديقك: لا تترك الأمر للانضباط الشخصي. استخدم أدوات مثل
commitlintوhusky. اجعل من المستحيل ارتكاب الخطأ. - كن مرنًا: الهدف هو الوضوح والتواصل، وليس الالتزام الأعمى بالقواعد. إذا واجهت حالة لا تناسبها أي من القواعد، استخدم أفضل حكم لديك. الهدف هو شغل نظيف، وليس تعقيد الأمور.
- استخدم متن الرسالة (Body): لا تبخل في كتابة شرح مفصل في متن الرسالة للـ commits المهمة. اشرح الـ “لماذا” خلف التغيير. سَيشكرك زملاؤك (وأنت المستقبلي) على ذلك.
الخلاصة: من الفوضى إلى النظام organizado
رحلتنا مع Conventional Commits لم تكن مجرد تغيير تقني، بل كانت تحولًا ثقافيًا. انتقلنا من فريق يعمل في جزر منعزلة، يترك وراءه سجلات غامضة، إلى فريق يتواصل بفعالية من خلال الكود نفسه. أصبح تاريخ المشروع ملكية جماعية واضحة ومفهومة للجميع.
إذا كنت أنت وفريقك لا تزالون تعانون من جحيم git log، فأتمنى أن تكون قصتنا هذه دافعًا لكم للتغيير. قد يتطلب الأمر بعض الجهد في البداية، لكن العائد على المدى الطويل لا يقدر بثمن. لن تحصل فقط على سجل تغييرات أنظف، بل ستبني ثقافة من الوضوح والاحترافية والعمل الجماعي. صدقني، “أنت المستقبلي” سَيشكرك على هذا القرار. 😉