معظم محتوى "دروس CI/CD" يقفز مباشرة إلى خط أنابيب منجز ومتقن — بناءات مصفوفية، بيئات متعددة، نشر تدريجي (canary) — دون أبداً شرح المنطق الذي يوصلك إلى هناك. هذا معكوس لتعلّمه فعلياً. هذه النسخة التي أتمنى لو حظيت بها: البدء من لا شيء، وإضافة قدرة واحدة بالضبط في كل مرة، وشرح سبب وجود كل خطوة قبل إضافة التالية.
ما هو CI/CD فعلياً تحت الاختصار
التكامل المستمر (Continuous Integration) يعني أن كل تغيير في الكود يُبنى ويُختبر تلقائياً لحظة اقتراحه، بحيث تُكتشف مشاكل التكامل خلال دقائق، لا بعد أيام حين يتصادم تغيير شخص آخر مع تغييرك. التسليم المستمر (Continuous Delivery) يعني أن كل تغيير يجتاز تلك الفحوصات يُحزَّم تلقائياً إلى شيء قابل للنشر — قطعة بناء، صورة حاوية — جاهزة للشحن في أي وقت. النشر المستمر (Continuous Deployment) يذهب خطوة أبعد وينشر تلك القطعة فعلياً تلقائياً، دون إنسان ينقر "موافقة". معظم الفرق تفعل CI والتسليم المستمر؛ النشر المستمر الحقيقي (بلا بوابة بشرية إطلاقاً) أقل شيوعاً وليس دائماً الهدف — وهذا جيد.
المرحلة 0 — قبل وجود أي خط أنابيب
خط الأنابيب يؤتمت ما تفعله بالفعل يدوياً. إذا لم يكن "اختبار ونشر" بالفعل عملية يدوية واضحة ومفهومة جيداً وقابلة للتكرار على جهازك، فإن أتمتته تؤتمت الالتباس فقط. اضبط النسخة اليدوية أولاً: اعرف بالضبط أي أوامر تُشغّل الاختبارات، وما الذي ينتجه بناء ناجح، وما الذي ينطوي عليه النشر فعلياً.
المرحلة 1 — التكامل المستمر: خط الأنابيب الأدنى القابل للتطبيق
يجب أن يفعل خط الأنابيب الأول شيئاً واحداً بالضبط وبشكل جيد: يعمل عند كل دفعة، يثبّت التبعيات، يُشغّل الفحص اللغوي (linting)، يُشغّل الاختبارات، ويُبلّغ نجاح/فشل على طلب السحب. هذا كل شيء. لا نشر بعد. هذا وحده يلتقط حصة كبيرة من الأخطاء القابلة للمنع — بناءات معطلة، اختبارات فاشلة، انتهاكات أسلوب — قبل أن تصل لانتباه مراجع بشري، وهذا هو الهدف الفعلي: أتمتة ما يستطيع جهاز فحصه أسرع وأكثر موثوقية من شخص يعيد قراءة اختلاف (diff).
# .github/workflows/ci.yml — بسيط عن قصد
name: CI
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npm run lint
- run: npm test
قاوم الرغبة في إضافة المزيد إلى هذا فوراً. خط أنابيب يثق فيه الفريق لأنه بسيط ودائماً صحيح أكثر قيمة بكثير من واحد متقن يبدأ الناس بتجاهله لأنه غير مستقر.
المرحلة 2 — قطع البناء
بمجرد أن تصبح الاختبارات خضراء بموثوقية، أضف خطوة بناء تنتج الشيء الفعلي الذي ستنشره — صورة حاوية، ملف تنفيذي مُترجَم، حزمة ثابتة — وخزّنه في مكان قابل للعنونة (سجل حاويات، مخزن قطع). هذه الحدود الفعلية بين CI وCD: لديك الآن شيء محدد ومُرقَّم وغير قابل للتغيير إما يُنشر أو لا، بدلاً من "أياً كان الموجود حالياً على نسخة خادم النشر من main".
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t myapp:${{ github.sha }} .
- run: docker push myapp:${{ github.sha }}
سمِّ بمعرّف الالتزام (commit SHA)، لا فقط latest — القدرة على تحديد أي بناء بالضبط يعمل في بيئة معينة، والتراجع لواحد سابق محدد بالعلامة، تستحق الخطوة الإضافية الصغيرة منذ اليوم الأول.
المرحلة 3 — النشر على staging تلقائياً
كل بناء يجتاز CI يُنشر على بيئة staging دون خطوة بشرية. هنا يظهر الكثير من القيمة الحقيقية لـ CI/CD في العمل اليومي: "هل يعمل هذا فعلياً" يصبح سؤالاً تجيب عليه بالنظر إلى staging، لا بوصف ما تعتقد أنه سيحدث. يجب أن يحاكي staging الإنتاج بقرب كافٍ بحيث تكون "عملت في staging" إشارة ذات معنى، لا شكلية.
المرحلة 4 — النشر على الإنتاج، مع بوابة بشرية
أضف خطوة موافقة يدوية قبل النشر على الإنتاج — ليس لأن الأتمتة غير موثوقة، بل لأن نقطة تحقق بشرية متعمدة قبل إجراء يؤثر على الإنتاج افتراضٌ معقول حتى تبني ثقة كافية (وشبكات أمان آلية كافية — فحوصات صحة، تراجع تلقائي) لإزالتها بمسؤولية. معظم إعدادات GitHub Actions/GitLab CI تدعم هذا كـ"قاعدة حماية بيئة" أصلية، لا سكربت مخصص.
المرحلة 5 — اجعل التراجع مملاً، لا بطولياً
خط أنابيب بلا مسار تراجع سريع ومُختبَر جيداً هو خط أنابيب يحوّل كل نشر سيء إلى حادثة. يجب أن يكون التراجع: إعادة نشر آخر قطعة بناء معروفة الصحة عبر علامتها، لا "اكتشف ما تغيّر وتراجع يدوياً تحت الضغط". اختبر مسار التراجع نفسه قبل أن تحتاجه فعلياً — إجراء تراجع لم يُشغّله أحد فعلياً ليس إجراء تراجع، إنه أمل.
المرحلة 6 — وازِ ما هو مستقل فعلياً
بمجرد أن يصبح خط الأنابيب موثوقاً، تصبح السرعة تستحق التحسين. شغّل المهام المستقلة بالتوازي (الفحص اللغوي واختبارات الوحدة لا يحتاجان انتظار بعضهما)، خزّن التبعيات مؤقتاً بين التشغيلات، وقسّم مجموعات الاختبار البطيئة عبر عدة مُشغّلات إن كانت مستقلة فعلياً. افعل هذا بعد أن تصبح الصحة والموثوقية راسختين، لا قبل ذلك — خط أنابيب سريع يعطي إجابات خاطئة أسوأ من واحد بطيء لكنه موثوق.
المرحلة 7 — التسليم التدريجي، بمجرد أن تحتاجه فعلياً
النشر التدريجي (canary) (انشر لنسبة صغيرة من حركة المرور أولاً، راقب المقاييس، ثم استمر أو تراجع) والنشر الأزرق-الأخضر (شغّل النسخة الجديدة جنباً إلى جنب مع القديمة، بدّل حركة المرور، أبقِ النسخة القديمة جاهزة كتراجع فوري) قيّمان فعلياً عند مستوى معين من النطاق وتحمّل المخاطر — وتعقيد غير ضروري فعلياً تحته. أضف هذه حين يكون وصول نشر سيء لـ100% من المستخدمين فوراً خطراً تحاول تقليله بنشاط، لا لأنها تظهر في كل مقال "CI/CD متقدم".
الأسرار: الخطأ الذي يظهر في كل تشريح ما بعد الحادثة "تم اختراقنا"
لا تضع بيانات اعتماد في ملفات YAML لخط الأنابيب أبداً، حتى "مؤقتاً". استخدم إدارة أسرار منصة CI الخاصة بك (أسرار GitHub Actions، متغيرات GitLab CI/CD، أو مدير أسرار مخصص لأي شيء أكثر حساسية) وأشر إليها بالاسم. دقّق من يملك وصولاً لأسرار الإنتاج تحديداً — خط أنابيب يستطيع النشر على الإنتاج يحتاج نطاق أسرار أضيق من واحد يُشغّل الاختبارات فقط. هذا يتصل مباشرة بممارسات الأمان في قائمة تحصين Windows Server وLinux لمهندسي الباك إند وDevOps — انضباط الأسرار نفس المبدأ مُطبَّقاً على خط تسليمك بدلاً من خوادمك.
علامات تحتاج فيها خط أنابيبك للانتباه (قائمة تحقق عملية)
- اختبارات متقلبة يُعاد تشغيلها حتى تنجح — انهيار ثقة بطيء الحركة؛ خط أنابيب يتعلم الناس تجاهله أسوأ من عدم وجود خط أنابيب.
- نشر يعرفه شخص واحد فقط يدوياً "احتياطاً" — إذا لم يكن في خط الأنابيب، فليس مؤتمتاً فعلياً، إنه دليل تشغيل بخطوات إضافية.
- لا مسار تراجع مُختبَر — انظر المرحلة 5.
- أسرار ظاهرة في السجلات أو ملفات الإعداد — أصلح فوراً، لا في السباق القادم.
- خط أنابيب يستغرق وقتاً طويلاً لدرجة توقف الناس عن انتظاره — السرعة مهمة لأن خط أنابيب لا يراقبه أحد يتوقف عن التقاط أي شيء.
الهدف الفعلي
خط أنابيب CI/CD جيد لا يُقاس بعدد مراحله أو أدواته — يُقاس بما إذا كان الفريق يثق فيه بما يكفي للشحن بثقة وتكرار، وما إذا كان النشر السيء حدثاً صغيراً يُعكَس بسرعة بدلاً من حادثة. كل ما سبق في خدمة تلك النتيجة الواحدة؛ أضف تعقيداً فقط حين تكون المرحلة الحالية راسخة والتالية تحل مشكلة تملكها فعلياً.
أسئلة شائعة
ما الفرق بين التسليم المستمر والنشر المستمر فعلياً؟ التسليم المستمر يعني أن كل تغيير يجتاز CI يُحزَّم تلقائياً إلى قطعة قابلة للنشر — لكن إنساناً لا يزال يقرر متى تذهب فعلياً للإنتاج (بوابة الموافقة في المرحلة 4). النشر المستمر يزيل تلك البوابة تماماً: كل تغيير ناجح يُنشر تلقائياً، بلا إنسان في الحلقة. النشر المستمر هو الحالة النهائية "الأكثر تقدماً"، لكنه ليس أفضل بالضرورة — إنه مناسب بمجرد أن تصبح تغطية اختباراتك ومراقبتك وأتمتة تراجعك موثوقة بما يكفي.
هل أحتاج خطوط أنابيب منفصلة لبيئات مختلفة (staging، إنتاج)؟ ليس بالضرورة تعريفات خط أنابيب منفصلة — معظم الفرق تستخدم خط أنابيب واحد بمراحل/مهام خاصة بالبيئة (ابنِ مرة واحدة، انشر نفس القطعة على staging ثم الإنتاج)، وهذا فعلياً النمط الأكثر أماناً: تريد معرفة أن القطعة المحددة المُختبَرة في staging هي نفسها الواصلة للإنتاج.
كيف أقنع فريقاً ينشر يدوياً بتبني CI/CD؟ ابدأ بالمرحلة 1 فقط — اختبار آلي على كل طلب سحب، بلا أتمتة نشر بعد — ودع الفريق يشعر بالفائدة قبل اقتراح أتمتة النشر. محاولة بيع خط الأنابيب الكامل دفعة واحدة، لفريق بلا خبرة CI/CD، محادثة أصعب بكثير من إثبات القيمة تدريجياً.
ماذا أفعل حين يكون خط الأنابيب متقلباً ويبدأ الناس بتجاهل فشله؟ عامل هذا كطارئ، لا إزعاجاً في الخلفية — خط أنابيب يتعلم الناس النقر متجاوزين فشله توقف بالفعل عن أداء وظيفته. جد وأصلح مصدر التقلب الفعلي (عادة اختبار بحالة سباق، أو مورد مشترك بين تشغيلات الاختبار) بدلاً من إضافة إعادة محاولات لإخفائه.