إدارة المخرجات وهندسة الإصدارات

التصحيحات العاجلة والنقل الخلفي للإصلاحات

18 دقيقة الدرس 9 من 28

التصحيحات العاجلة والنقل الخلفي للإصلاحات

الإنتاج معطوب. ثغرة أمنية حرجة يجري استغلالها. العملاء يخسرون المال مع كل دقيقة تمر. هذه هي اللحظة التي يُثبت فيها نظام الإصدار نضجه — أو ينهار تحت الضغط. التصحيحات العاجلة (Hotfixes) والنقل الخلفي للإصلاحات (Backports) هي الأنظمة التي تُمكّنك من تصحيح الإصدارات المنشورة بأمان، دون تعطيل مسار التطوير الاعتيادي، ودون الوقوع في فوضى "ادفعه بسرعة فقط."

تُميّز هندسة الشركات الكبرى بوضوح بين عمليتين: التصحيح العاجل (رقعة طارئة تُطبَّق مباشرة على إصدار منشور) والنقل الخلفي (نقل إصلاح هبط بالفعل على main إلى فرع دعم أقدم). يتشاركان الآليات لكنهما يختلفان في الإلحاح، والحوكمة، وملف المخاطر.

فروع الدعم: الأساس

لا يمكنك تصحيح إصدار منشور إلا إذا كان لذلك الإصدار فرع حيّ. هذا هو السبب في أن كل قطار إصدار يقطع فرعاً مسمّى — release/1.5 أو release/2024.10 — ويبقيه حياً طوال فترة دعم الإصدار. الفرع هو سطح التصحيح. احذفه وتفقد القدرة على إصدار تصحيح عاجل دون إعادة بناء التاريخ.

ضع سياسة نافذة دعم واضحة قبل أن تشحن أي شيء: Chrome يدعم الإصدار الحالي فقط؛ Kubernetes يدعم الإصدارات الثلاثة الأحدث (N, N-1, N-2)؛ Ubuntu LTS يدعم إصدارين لخمس سنوات لكل منهما. مهما اخترت، انشره وأتمت دورة حياة الفرع وطبّقه بصرامة — أسوأ نتيجة هي أن يفترض المهندسون أن فرعاً مدعوم بينما تُرك في صمت.

Hotfix and Backport Flow main fix commit release/1.4 v1.4.0 cherry-pick (backport) v1.4.1 release/1.3 v1.3.0 v1.3.1 hotfix commit merge back to main Hotfix: patch on release branch, merge back. Backport: cherry-pick from main.
تدفق التصحيح العاجل مقابل النقل الخلفي: التصحيح العاجل ينشأ على فرع الإصدار ويُدمج في main؛ النقل الخلفي يختار إصلاحاً من main إلى فرع الدعم.

سير عمل التصحيح العاجل

يُكتب التصحيح العاجل حين يُكتشف الخلل لأول مرة في الإنتاج ولا يوجد بعد إصلاح مقابل له على main — أو حين لا يمكن نقل الإصلاح الموجود على main بأمان لأن الفرع قد تباعد كثيراً. سير العمل المعياري:

  1. التفرع من وسم الإصدار، وليس من main. هذا جوهري: قد يحتوي main على أسابيع من الميزات غير المنشورة التي يجب ألا تذهب إلى الإنتاج.
  2. تطبيق أدنى إصلاح ممكن. التصحيحات العاجلة ليست وقت إعادة الهيكلة أو ترقية التبعيات أو التغييرات الإضافية. الزم التزاماً واحداً بغرض واحد.
  3. تشغيل خط إصدار متسارع مع كامل الاختبارات. يجب أن تنجح جميع الاختبارات — لا تتخطَّ شيئاً. إذا كانت مجموعة اختباراتك تستغرق 45 دقيقة والعملاء في انقطاع، فهذه مشكلة أداء مجموعة الاختبارات تُحلّ بشكل منفصل، وليست مبرراً لتجاهل الاختبارات.
  4. وسم بترقية إصدار الترقيع (v1.4.0v1.4.1) والإطلاق.
  5. دمج الإصلاح في main فوراً بعد الإطلاق. هذه الخطوة هي الأكثر تجاهلاً، وتجاهلها يجعل الخلل ذاته يظهر مجدداً في الإصدار التالي.
# سير عمل التصحيح العاجل — ابدأ من وسم الإصدار وليس من main git fetch --tags git checkout v1.4.0 git checkout -b hotfix/CVE-2025-1234 # طبّق الإصلاح الأدنى ثم أنشئ التزاماً بنطاق واضح git add src/auth/token.go git commit -m "fix(auth): reject tokens with empty sub claim (CVE-2025-1234)" # شغّل مجموعة الاختبارات الكاملة — لا تتخطَّ أياً منها make test-all # ضع وسماً على إطلاق التصحيح (وسم موسوم مع ملاحظات الإصدار) git tag -a v1.4.1 -m "chore(release): v1.4.1 - security patch CVE-2025-1234" git push origin hotfix/CVE-2025-1234 git push origin v1.4.1 # بعد أن يُصدر CI القطعة الأثرية، ادمج الإصلاح في main git checkout main git cherry-pick <hotfix-commit-sha> git push origin main
فخ الدمج المعكوس: تخطّي الدمج في main هو الخطأ الأكثر شيوعاً في التصحيحات العاجلة. يُشحن الإصلاح، تُغلق الحادثة، يعود الجميع إلى البيت — وبعد ثلاثة أشهر يُشحن الخلل ذاته مجدداً في الإصدار التالي لأن الإصلاح عاش فقط على فرع الإصدار. أتمت خطوة CI بعد الإطلاق تفتح طلب سحب من فرع التصحيح إلى main حتى لا ينساه الفريق.

النقل الخلفي: نقل الإصلاحات إلى فروع أقدم

يختلف النقل الخلفي عن التصحيح العاجل في نقطة واحدة مهمة: الإصلاح موجود بالفعل على main. مهمتك هي نقله — بأنظف ما يمكن — إلى فرع إصدار أقدم. الأداة هي git cherry-pick.

يقوم cherry-pick بنسخ فرق (diff) التزام محدد وإعادة تطبيقه. ينشئ التزاماً جديداً بـSHA جديد على الفرع الهدف — التاريخ منفصل عن main. هذا مقصود: فرع الإصدار لا يجب أن يرث التزامات غير ذات صلة من main.

# نقل إصلاح خلفياً من main إلى release/1.3 # ابحث عن SHA الالتزام في main git log main --oneline --grep="fix(sql): escape user input" # مثال على الإخراج: a7f3c21 fix(sql): escape user input in search handler # انتقل إلى فرع الدعم git checkout release/1.3 git pull origin release/1.3 # اختر الالتزام بـcherry-pick git cherry-pick a7f3c21 # في حال وجود تعارضات (شائعة حين تباعد الفرعان)، حلّها يدوياً # git cherry-pick --continue (بعد الحل) # git cherry-pick --abort (للإلغاء) # ادفع وافتح طلب سحب للمراجعة — حتى التصحيحات العاجلة تحتاج أعيناً git push origin release/1.3 # ضع وسماً على إصدار الترقيع git tag -a v1.3.1 -m "chore(release): v1.3.1 - backport SQL escape fix" git push origin v1.3.1
استراتيجية تقليل التعارضات: اختر دائماً أصغر وحدة ممكنة — افضّل الالتزامات الذرية المضغوطة على اختيار التزام دمج. إذا كان الإصلاح الأصلي على main إعادة هيكلة تمتد على 40 ملفاً، اطلب من المؤلف استخراج الإصلاح الصافي في التزام منفصل قبل النقل الخلفي. الإصلاح النظيف المعزول يُطبَّق بلا تعارضات؛ نقل إعادة الهيكلة خلفياً يستغرق أياماً من التصحيح.

أتمتة النقل الخلفي باستخدام الوسوم

على نطاق واسع — فروع دعم متعددة وعشرات المهندسين — النقل الخلفي اليدوي عرضة للخطأ وبطيء. النهج الصناعي المعياري هو الأتمتة المدفوعة بالوسوم: يُعلّم مؤلف طلب السحب إصلاحه بـbackport/1.3 وbackport/1.4؛ يفتح بوت طلبات cherry-pick لتلك الفروع تلقائياً بعد الدمج.

GitHub Actions مع zeebe-io/backport-action أو أمر /cherrypick في Prow (تستخدمه Kubernetes) تطبيقات شائعة. النمط متطابق: الوسم يُشغّل الأتمتة، الأتمتة تفتح طلب سحب، إنسان يراجعه ويدمجه. خطوة المراجعة البشرية غير اختيارية — يمكن أن تتعارض عمليات cherry-pick الآلية، ودمج بوت لنقل خلفي معطوب قد يُسقط نظام إنتاج.

# .github/workflows/backport.yml # يُشغَّل حين يُغلق طلب سحب (مدموج) ويحمل وسم نقل خلفي name: Backport on: pull_request: types: [closed] jobs: backport: if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Run backport uses: zeebe-io/backport-action@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} # صيغة الوسم: backport/release-1.4 # يفتح البوت طلب سحب بعنوان "[Backport release/1.4] عنوان طلب السحب الأصلي" pull_request_description: | Automated backport of #${{ github.event.pull_request.number }}. Review carefully — cherry-pick conflicts may have been resolved automatically.

الحوكمة: من يوافق على التصحيح العاجل؟

يجب أن تكون فروع الإصدار فروعاً محمية بضوابط دمج أكثر صرامة من main. التصحيح العاجل الذي يتجاوز مراجعة الكود ليس تصحيحاً عاجلاً — بل تغيير إنتاجي غير مصرّح به، وهكذا تُدخل رقع الأمان ثغرات جديدة. تبدو الحوكمة الجاهزة للإنتاج كالتالي:

  • مراجعون مطلوبون: اثنان على الأقل من المهندسين، بينهم مدير الإصدار المناوب. يجب أن يكون أحد الموافقين قائد الفريق أو مهندساً أول.
  • كل فحوصات CI يجب أن تنجح: اختبارات الوحدة والتكامل وفحوصات الثغرات. لا استثناءات حتى في الثالثة فجراً.
  • توقيع الالتزامات مطلوب: استخدم git commit -S بمفتاح GPG أو SSH. الوسم الموقّع على إصدار التصحيح يُثبت بناء القطعة الأثرية من التزام مراجَع.
  • ملاحظات الإصدار مطلوبة: كل إصدار ترقيع يحتاج مدخلاً في CHANGELOG يُكتب قبل دفع الوسم. يجب أن يعرف فريق المناوبة تحديداً ما الذي تغيّر قبل النشر.
إجراء "كسر الزجاج": حتى مع حماية الفروع الصارمة، كل فريق يحتاج مساراً موثقاً ومراجعاً للتجاوز في حالات الطوارئ الصفرية الحقيقية — حين تكون الاختبارات غير مستقرة وكل ثانية تكلّف المال. حدّد هذا المسار مسبقاً: من يوافق على نشر كسر الزجاج، وما الضوابط التعويضية (قائمة اختبار دخاني يدوي، خطة استرجاع مكتوبة قبل النشر، رقم تذكرة الحادثة مرفق)، وكيف ستراجع مراجعة ما بعد الوفاة القرار كاملاً. كسر الزجاج بدون هذا الإجراء هو إهمال مع خطوات إضافية.

تصميم خط إصدار التصحيح العاجل

خط الإصدار المعياري لديك محسّن للإنتاجية. خط التصحيح العاجل يجب أن يُحسَّن من أجل السرعة دون التضحية بالسلامة. الاختلافات العملية:

  • التوازي: شغّل اختبارات الوحدة والتكامل وفحوصات الأمان بالتوازي بدلاً من التسلسل. مجموعة تسلسلية مدتها 45 دقيقة تصبح 15 دقيقة بالتشغيل المتوازي دون تراجع في الجودة.
  • عدّاءون مخصصون: تعمل وظائف التصحيح العاجل على عدّاءي CI محجوزين مسبقاً — لا مُصطفَّة خلف 200 بناء لطلبات سحب عادية. احجز الطاقة على مستوى البنية التحتية.
  • تخطّي بوابات غير السلامة: من المقبول تخطّي معايير الأداء وفحوصات دلتا تغطية الكود والتنسيق. ليس من المقبول أبداً تخطّي اختبارات الصحة أو فحوصات الأمان.
  • نشر مع أولوية الاسترجاع: قبل نشر التصحيح العاجل، تأكد من تجهيز قطعتك الأثرية للاسترجاع (الإصدار السابق) مسبقاً في كل منطقة. انشر التصحيح فقط حين يمكنك الاسترجاع في أقل من دقيقتين إذا ساءت الأمور.

إتقان التصحيحات العاجلة والنقل الخلفي هو ما يُميّز الفرق التي تتعافى بأناقة من الحوادث عن تلك التي تُضاعفها. الميكانيكا — التفرع من وسم، أدنى إصلاح، الدمج المعكوس — بسيطة. الانضباط — الحوكمة والأتمتة والاسترجاع المجهّز مسبقاً ونوافذ الدعم الواضحة — هو ما يحتاج ممارسة لترسيخه في ثقافة المنظمة.