كيف أنقذتنا ‘الإصدارات الدلالية’ (Semantic Release) من فوضى ما قبل الإطلاق؟

يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.

خليني أحكيلكم قصة صارت معي قبل كم سنة، قصة بتلخص المعاناة اللي كتير من المبرمجين بعيشوها. كنا فريق شغالين على مشروع مهم، والساعة صارت 2 بعد نص الليل، عيوننا حمر من التعب والقهوة صارت زي المي. إحنا على وشك نطلق إصدار جديد للعميل، والكل متوتر. مدير المشروع بسألني: “أبو عمر، جاهزين؟ شو رقم الإصدار الجديد؟ وشو نكتب في إعلان التحديثات؟”

هون بلشت الكركبة. واحد من الشباب بحكي الإصدار لازم يكون 1.7.0، والثاني بحكي لا، التغييرات بسيطة، خليه 1.6.2. وأنا فتحت سجل الـ commits في Git عشان أشوف شو عملنا بالضبط، وإذا هو عبارة عن فوضى عارمة: “fix bug”, “update”, “more stuff”, “final changes”… رسائل ما حدا بفهم منها إشي. قعدنا ساعة كاملة نحاول نلملم التغييرات ونكتب “سجل التغييرات” (Changelog) بإيدنا، وبالآخر طلعنا بإصدار جديد وإحنا مش متأكدين 100% إذا نسينا إشي أو لأ. يومها قلت لحالي: “يا زلمة، لازم يكون في طريقة أحسن من هيك! هاي مش شغلة تنعمل يدويًا في 2024”.

ومن يومها، بلشت رحلة البحث عن حل، ولقيت الكنز اللي اسمه ‘Semantic Release’. واليوم، بدي أشاركم كيف هاي الأداة السحرية أنقذتنا من الفوضى وحوّلت عملية الإطلاق من كابوس إلى متعة.

قبل الأتمتة: ما هو ترقيم الإصدارات الدلالي (Semantic Versioning)؟

قبل ما نغوص في الأداة نفسها، لازم نفهم المبدأ اللي بتعتمد عليه. ترقيم الإصدارات الدلالي، أو SemVer اختصارًا، هو مش مجرد أرقام عشوائية بنحطها، هو عبارة عن اتفاقية ومعيار عالمي لوصف التغييرات في كل إصدار. الصيغة بسيطة جدًا:

MAJOR.MINOR.PATCH (رئيسي.فرعي.تصحيح)

  • MAJOR (رئيسي): بنزيد هذا الرقم (مثلاً من 1.7.5 إلى 2.0.0) لما نعمل تغييرات جذرية غير متوافقة مع الإصدارات السابقة (Breaking Changes). يعني الكود القديم اللي كان يستخدم مكتبتك ممكن يتكسّر ويحتاج تعديل.
  • MINOR (فرعي): بنزيده (مثلاً من 1.7.5 إلى 1.8.0) لما نضيف ميزات جديدة، لكنها متوافقة مع الإصدارات السابقة. الكود القديم بضل شغال بدون مشاكل.
  • PATCH (تصحيح): بنزيده (مثلاً من 1.7.5 إلى 1.7.6) لما نعمل إصلاحات لأخطاء (bug fixes)، وهاي الإصلاحات طبعًا بتكون متوافقة مع الإصدارات السابقة.

هذا المعيار بخلي التواصل بين المطورين والمشاريع أوضح. لما تشوف مكتبة تحدّثت من إصدار 2.1.0 إلى 2.2.0، بتعرف فورًا إنه في ميزات جديدة بس كل إشي آمن للتحديث. أما لو التحديث كان لـ 3.0.0، هون لازم تنتبه وتقرأ دليل الترقية لأنه في تغييرات جذرية.

الفوضى اليدوية: لماذا كانت إدارة الإصدارات كابوسًا؟

القصة اللي حكيتها في البداية هي مثال بسيط على المشاكل اللي بتصير لما تكون العملية يدوية:

  • الخطأ البشري: نسيان تحديث رقم الإصدار في ملف package.json، أو كتابة رقم غلط، أو نسخ ولصق تغييرات بشكل خاطئ في ملف CHANGELOG.md.
  • سجل تغييرات غير متناسق: كل مطور بكتب ملاحظاته بطريقته. واحد بكتب بالتفصيل، والثاني بكتب كلمتين. النتيجة ملف فوضوي صعب فهمه.
  • انقطاع التواصل: فريق التسويق أو إدارة المنتج ما بعرفوا شو الميزات الجديدة اللي لازم يعلنوا عنها، والمستخدمين النهائيين ما بعرفوا شو تصلّح أو شو الجديد.
  • وقت ضائع: بدل ما المطور يركز على كتابة الكود وحل المشاكل، بضيع ساعات في كل عملية إطلاق وهو بعمل مهام روتينية مملة: يقرأ الـ commits، يكتب الـ changelog، يعمل tag جديد في Git، ويرفع الحزمة على npm أو أي منصة ثانية.

المنقذ: ‘Semantic Release’ وكيف يعمل السحر؟

هنا يأتي دور semantic-release. هي أداة تقوم بأتمتة عملية الإصدار بالكامل. الفكرة عبقرية وبسيطة: بدل ما الإنسان يقرر رقم الإصدار الجديد ويكتب سجل التغييرات، الأداة بتعمل كل هاد بناءً على رسائل الـ commits اللي أنت بتكتبها في Git.

كيف يفهم ‘Semantic Release’ رسائل الـ Commit؟

السر كله يكمن في اتباع معيار محدد لكتابة رسائل الـ commit اسمه “Conventional Commits”. هذا المعيار بفرض عليك صيغة معينة للرسالة، وهي الصيغة اللي بتخلي الآلة تفهم طبيعة التغيير اللي عملته.

الصيغة العامة هي: <type>(<scope>): <subject>

  • type: هو أهم جزء، وبحدد نوع التغيير. أشهر الأنواع:
    • feat: لإضافة ميزة جديدة (سيؤدي إلى إصدار MINOR).
    • fix: لإصلاح خطأ (سيؤدي إلى إصدار PATCH).
    • docs: لتعديل التوثيق فقط (لن يؤدي إلى إصدار جديد).
    • style, refactor, test, chore: أنواع أخرى لتنظيم العمل، لا تؤدي لإصدار جديد.
  • scope (اختياري): لتحديد الجزء من المشروع اللي صار عليه التغيير (مثال: auth, api, ui).
  • subject: وصف مختصر وواضح للتغيير.

وللإشارة إلى تغيير جذري (Breaking Change)، يمكنك إضافة ! بعد الـ type، أو إضافة فقرة BREAKING CHANGE: في جسم الرسالة.

أمثلة عملية:


# هذا الـ commit سيؤدي إلى إصدار MINOR (مثلاً من 1.2.3 إلى 1.3.0)
feat(auth): add user login with email and password

# هذا الـ commit سيؤدي إلى إصدار PATCH (مثلاً من 1.3.0 إلى 1.3.1)
fix(api): correct calculation for order total

# هذا الـ commit لن يؤدي إلى أي إصدار
docs(readme): update installation instructions

# هذا الـ commit سيؤدي إلى إصدار MAJOR (مثلاً من 1.3.1 إلى 2.0.0)
feat(api)!: change user data structure in API response
  
BREAKING CHANGE: The `user.fullName` field has been replaced with `user.firstName` and `user.lastName` to provide more granular data.

دورة حياة الإصدار المؤتمتة

عندما تدمج تغييراتك في الفرع الرئيسي (مثل main أو master)، يقوم سير عمل الـ CI/CD (مثل GitHub Actions) بتشغيل semantic-release، والذي يقوم بالخطوات التالية تلقائيًا:

  1. تحليل الـ Commits: يقرأ كل رسائل الـ commit منذ آخر إصدار تم.
  2. تحديد الإصدار القادم: بناءً على أنواع الـ commits (feat, fix, !)، يقرر ما إذا كان الإصدار القادم هو MAJOR أو MINOR أو PATCH. إذا لم يجد أيًا منها، فلن يتم إنشاء إصدار جديد.
  3. إنشاء سجل التغييرات: يقوم تلقائيًا بإنشاء أو تحديث ملف CHANGELOG.md، مع تقسيم التغييرات إلى أقسام واضحة مثل “✨ Features” و “🐛 Bug Fixes”.
  4. تحديث الملفات وعمل Tag: يقوم بتحديث رقم الإصدار في ملف package.json (أو ملفات أخرى)، ثم يقوم بإنشاء Git tag جديد (مثل v2.0.0).
  5. النشر: يقوم بنشر الحزمة البرمجية إلى مستودع الحزم (مثل npm)، وإنشاء “Release” على GitHub مع إرفاق سجل التغييرات الخاص بهذا الإصدار.

كل هذا يتم في دقائق، بدون أي تدخل بشري!

تطبيق عملي: لنقم بإعداد ‘Semantic Release’ في مشروع Node.js

الكلام النظري جميل، لكن خلينا نشوف كيف بنطبق هالحكي بشكل عملي. سأستخدم كمثال مشروع Node.js مع GitHub Actions.

المتطلبات الأساسية

  • مشروع Node.js يحتوي على ملف package.json.
  • مستودع Git على منصة GitHub.
  • حساب على npm (إذا كنت تريد النشر عليه).

الخطوة 1: التثبيت والإعداد

أولاً، نقوم بتثبيت الحزم اللازمة كـ dev dependencies:

npm install --save-dev semantic-release @semantic-release/git @semantic-release/changelog @semantic-release/npm @semantic-release/github

هذه الحزم هي “ملحقات” (plugins) تسمح لـ semantic-release بالتفاعل مع Git، وتحديث سجل التغييرات، والنشر على npm و GitHub.

الخطوة 2: تهيئة ملف الإعدادات

يمكنك إنشاء ملف .releaserc.json في جذر المشروع، أو إضافة قسم "release" داخل ملف package.json. أنا أفضل ملف منفصل لتنظيم أفضل.

محتوى ملف .releaserc.json:

{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    ["@semantic-release/changelog", {
      "changelogFile": "CHANGELOG.md"
    }],
    ["@semantic-release/npm", {
      "npmPublish": true
    }],
    ["@semantic-release/git", {
      "assets": ["package.json", "CHANGELOG.md"],
      "message": "chore(release): ${nextRelease.version} [skip ci]nn${nextRelease.notes}"
    }],
    "@semantic-release/github"
  ]
}
  • branches: نحدد الفروع التي سيتم إطلاق الإصدارات منها (عادة main).
  • plugins: نحدد الملحقات التي سيتم استخدامها وبالترتيب. كل ملحق له وظيفة محددة في دورة حياة الإصدار.

الخطوة 3: إعداد الـ CI/CD (مثال GitHub Actions)

الآن، نحتاج إلى إخبار GitHub بتشغيل هذه العملية تلقائيًا. ننشئ ملفًا جديدًا هنا: .github/workflows/release.yml

name: Release

on:
  push:
    branches:
      - main

jobs:
  release:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run semantic-release
        run: npx semantic-release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

شرح سريع:

  • on: push: branches: [main]: هذا يعني أن سير العمل سيعمل فقط عند حدوث push على فرع main.
  • fetch-depth: 0: مهم جدًا، لأنه يسمح لـ semantic-release بالوصول إلى كل تاريخ الـ commits.
  • env: هنا نمرر التوكينات اللازمة. GITHUB_TOKEN يتوفر تلقائيًا في GitHub Actions. أما NPM_TOKEN، فيجب عليك إنشاؤه من حسابك في npm وإضافته كـ “Secret” في إعدادات المستودع على GitHub.

نصيحة من أبو عمر: يا جماعة، أهم إشي الالتزام! لازم كل الفريق يلتزم بصيغة الـ Conventional Commits، وإلا كل الشغل هاد بروح عالفاضي. استخدموا أدوات مثل commitlint مع husky (Git hooks) عشان تجبروا الكل يلتزم بالصيغة الصحيحة عند عمل commit. هيك بتضمنوا إنه ما في مجال للغلط.

ما بعد الأتمتة: الفوائد التي لمسناها

بعد تطبيق هذا النظام، تغيرت حياتنا كفريق تطوير بشكل جذري:

  • زيادة الثقة وراحة البال: لم نعد نقلق بشأن عملية الإطلاق. كل شيء مؤتمت وموثوق. لا يوجد قلق “هل نسينا شيئًا؟”.
  • توفير هائل للوقت: المطورون يركزون على ما يبرعون فيه: كتابة الكود. الأداة تتكفل بالباقي.
  • تواصل أوضح 100%: سجل التغييرات CHANGELOG.md أصبح المصدر الوحيد والموثوق للحقيقة. فريق المنتج، التسويق، وحتى المستخدمون، يمكنهم قراءته بسهولة لمعرفة كل جديد.
  • إصدارات أسرع وأكثر تكرارًا: بما أن العملية سهلة جدًا، أصبحنا لا نتردد في إطلاق إصدار جديد حتى لو كان لإصلاح خطأ بسيط. هذا يعني أن التحسينات تصل للمستخدمين بشكل أسرع.

الخلاصة: من الفوضى إلى النظام بضغطة زر 🚀

الانتقال من إدارة الإصدارات اليدوية إلى نظام مؤتمت باستخدام semantic-release كان واحدًا من أفضل القرارات التقنية التي اتخذناها. لقد حررنا من مهام روتينية مملة، وقلل الأخطاء البشرية إلى الصفر، وجعل عملية تطوير البرمجيات أكثر احترافية ومتعة.

إذا كنت لا تزال تعاني من فوضى ما قبل الإطلاق، وتضيع ساعات في مهام كان يجب أن تكون مؤتمتة، فأنصحك بشدة أن تعطي هذا النهج فرصة. قد يتطلب الأمر بعض الجهد في الإعداد الأولي وتعويد الفريق على كتابة رسائل commit منظمة، لكن العائد على المدى الطويل يستحق كل دقيقة.

جربوها في مشروع جانبي، وشوفوا الفرق بأنفسكم. صدقوني، ما راح ترجعوا للطريقة القديمة أبدًا. والله ولي التوفيق.

أبو عمر

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

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

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

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

آخر المدونات

برمجة وقواعد بيانات

تحديثات قاعدة البيانات بدون توقف: كيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من جحيم التوقفات المجدولة؟

هل سئمت من إيقاف الخدمة مع كل تحديث لهيكلة قاعدة البيانات؟ أشارككم قصة حقيقية وكيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من ليالي النشر الطويلة والمُجهدة،...

4 يونيو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت إعادة المحاولة كارثة: كيف أنقذتنا مفاتيح عدم تكرار العمليات (Idempotency Keys) من جحيم الفواتير المزدوجة؟

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

4 يونيو، 2026 قراءة المزيد
الحوسبة السحابية

من التوقف التام إلى النجاة: كيف أنقذتنا استراتيجية “الضوء المرشد” (Pilot Light) يوم انقطعت السحابة؟

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

4 يونيو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

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

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

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

من الانتظار لأيام إلى الدفع في ثوانٍ: كيف أنقذتنا شبكات الدفع الفوري من جحيم التحويلات البنكية؟

أسرد لكم من واقع تجربتي كـ "أبو عمر"، كيف عانينا من بطء وتكلفة التحويلات البنكية الدولية، وكيف جاءت شبكات الدفع الفوري ومعيار ISO 20022 لتكون...

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

كان كل خادم لدينا ‘ندفة ثلج’ فريدة: كيف أنقذنا ‘الكود كبنية تحتية’ (IaC) من جحيم الانجراف اليدوي؟

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

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

كانت تغطية الاختبارات 100% لكن الأخطاء تتسرب: كيف أنقذنا “الاختبار الطفري” من جحيم الثقة الزائفة؟

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

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