كانت أخطاؤنا صامتة وقاتلة: كيف أنقذتنا ‘البرمجة الدفاعية’ من جحيم ‘مش عارف شو صار’؟

يا جماعة الخير، السلام عليكم.

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

بعد حوالي أسبوعين، بدأ قسم المحاسبة يلاحظ شغلات غريبة. أرصدة ما بتتطابق، فواتير قيمتها صفر مع إنها لازم تكون بمبالغ كبيرة، وحالة من الفوضى بدأت تتسلل للنظام. المشكلة الكبرى؟ ما في أي أخطاء في سجلات النظام (Logs). لا يوجد أي انهيار (Crash)، لا يوجد أي رسالة خطأ. النظام كان شغال، لكنه كان ينتج “قمامة” صامتة. دخلنا في دوامة، أو زي ما بنحكيها “أكلنا هوا”. قضينا أيامًا وليالي في غرفة الاجتماعات، بنحلل كل سطر كود، بنراجع كل معاملة، والضغط النفسي كان رهيبًا. كل واحد فينا كان حاسس بالاتهام، وشعار المرحلة كان “أنا مش عارف شو صار!”.

بعد حفر عميق ومؤلم، اكتشفنا الكارثة. كانت في دالة (function) بسيطة تستقبل سعر المنتج ونسبة الخصم. في بعض الحالات النادرة، كانت واجهة برمجة التطبيقات (API) الخارجية ترسل لنا قيمة null بدلًا من نسبة الخصم. الكود تبعنا، ببرائته، كان يتعامل مع null كأنها صفر، فكان يضرب السعر بصفر، والنتيجة النهائية للمعاملة تطلع صفر. خطأ بسيط، صامت، لكنه كان قاتلًا. كلفنا أسابيع من تصحيح البيانات يدويًا، وكاد أن يكلفنا ثقة العميل.

هذيك الحادثة كانت صفعة قوية صحّتني على مفهوم “البرمجة الدفاعية” (Defensive Programming). ومن يومها، صار هذا المبدأ هو الأساس اللي ببني عليه كل سطر كود بكتبه.

ما هي البرمجة الدفاعية؟ القيادة الوقائية في عالم الكود

تخيل أنك تسوق سيارة. أنت لا تفترض أن كل السائقين الآخرين محترفون وسيلتزمون بالقوانين. بالعكس، أنت تقود بشكل دفاعي: تترك مسافة أمان، تنظر في المرايا باستمرار، وتتوقع الحركات الغبية من الآخرين. هذا بالضبط هو جوهر البرمجة الدفاعية.

البرمجة الدفاعية هي عقلية ومنهجية تهدف إلى كتابة كود يستمر في العمل بشكل متوقع حتى عند مواجهة ظروف غير متوقعة أو بيانات خاطئة. أنت لا تثق بأي شيء: لا مدخلات المستخدم، لا استجابات الـ API، ولا حتى الكود الذي كتبته أنت بنفسك قبل ستة أشهر!

الهدف ليس فقط منع التطبيق من الانهيار، بل الأهم هو ضمان عدم وصوله إلى حالة فاسدة (corrupt state) والاستمرار في العمل بصمت وهو ينتج نتائج كارثية.

لماذا الأخطاء الصامتة هي الأخطر؟

قد تستغربون، أليس انهيار التطبيق أسوأ شيء؟ الجواب هو: لا. الخطأ الصارخ الذي يولد استثناءً (Exception) وينهي البرنامج هو صديقك. لماذا؟

  • واضح ومباشر: يخبرك أن هناك مشكلة، وغالبًا ما يحدد مكانها بدقة (stack trace).
  • يوقف النزيف: يمنع الخطأ من الانتشار وإفساد المزيد من البيانات أو التسبب في سلوكيات خاطئة أخرى.
  • سهل التتبع: يمكنك رؤيته في سجلات الأخطاء والبدء في تصحيحه فورًا.

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

أسلحة أبو عمر في البرمجة الدفاعية

على مر السنين، جمعت ترسانة من التقنيات والمبادئ التي أستخدمها لتحصين الكود. هذه ليست مجرد نظريات، بل هي أسلحة أستخدمها يوميًا في “المعركة”.

1. حارس البوابة: التحقق من المدخلات (Input Validation)

هذه هي القاعدة الذهبية الأولى. لا تثق أبدًا بالمدخلات القادمة إلى دوالك (functions) أو وحداتك (modules). تحقق من كل شيء قبل أن تبدأ بالعمل عليه. هذا المبدأ يُعرف بـ “Garbage In, Garbage Out” أو “GIGO”، والذي يعني أنك إذا أدخلت قمامة، فستحصل على قمامة.

لنتخيل دالة بسيطة لحساب عمر المستخدم. النسخة “البريئة” قد تبدو هكذا:


// النسخة البريئة (سيئة)
function calculateAge(birthYear) {
  const currentYear = new Date().getFullYear();
  return currentYear - birthYear; // ماذا لو كان birthYear هو "أبو عمر" أو null؟
}

هذا الكود هو قنبلة موقوتة. ماذا لو استدعيناه هكذا: calculateAge(null) أو calculateAge('some-text')؟ ستحصل على نتيجة NaN (Not a Number)، وهي نتيجة صامتة قد تنتشر في نظامك.

النسخة الدفاعية تبدو مختلفة تمامًا:


// النسخة الدفاعية (جيدة)
function calculateAge(birthYear) {
  const currentYear = new Date().getFullYear();

  // 1. التحقق من النوع
  if (typeof birthYear !== 'number') {
    throw new Error('سنة الميلاد يجب أن تكون رقمًا.');
  }

  // 2. التحقق من النطاق المنطقي
  if (birthYear > currentYear || birthYear < 1900) {
    throw new Error('سنة الميلاد غير منطقية.');
  }

  // الآن فقط، بعد أن تأكدنا أن المدخلات سليمة، نقوم بالعملية الحسابية
  return currentYear - birthYear;
}

لاحظ أننا نستخدم throw new Error. هذا ما يسمى بمبدأ “الفشل السريع والصاخب” (Fail Fast and Loud). من الأفضل ألف مرة أن ينهار التطبيق مع رسالة واضحة بدلًا من أن يكمل عمله ببيانات فاسدة.

2. شبكة الأمان: استخدام القيم الافتراضية بحذر

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

متى يكون مناسبًا؟ في الحالات غير الحرجة، مثل إعدادات المستخدم. إذا لم يحدد المستخدم لون الواجهة، فمن المنطقي أن نعطيه اللون الافتراضي “الفاتح” بدلًا من إيقاف التطبيق.


# مثال جيد لاستخدام القيم الافتراضية
def get_user_preferences(user_id):
    """
    تجلب تفضيلات المستخدم من قاعدة البيانات.
    إذا لم توجد قيمة، تستخدم قيمة افتراضية آمنة.
    """
    prefs = db.fetch_preferences(user_id) # قد تعود بـ None أو قاموس فارغ
    
    # استخدم .get() الذي يوفر قيمة افتراضية بأمان
    theme = prefs.get('theme', 'light') 
    notifications_enabled = prefs.get('notifications_enabled', True)
    
    return {'theme': theme, 'notifications': notifications_enabled}

متى يكون كارثيًا؟ في العمليات الحسابية الحرجة، كما في قصتي في البداية. لو أن دالة الخصم استخدمت 0 كقيمة افتراضية، لكانت الكارثة نفسها قد حدثت. هنا، الفشل الصاخب هو الخيار الوحيد.

3. فحص السلامة العقلية: التأكيدات (Assertions)

التأكيدات (Assertions) هي أداة رائعة للتحقق من الافتراضات داخل الكود الخاص بك. هي ليست للتحقق من مدخلات خارجية، بل للتحقق من أن منطق الكود الداخلي سليم. إنها كأنك تقول للكود: “أنا أفترض أن هذه القيمة يجب أن تكون موجبة دائمًا في هذه المرحلة. إذا لم تكن كذلك، فهناك خطأ فادح في منطقي البرمجي، أوقف كل شيء فورًا”.

معظم لغات البرمجة توفر آلية للتأكيدات يتم تفعيلها فقط في وضع التطوير (Development) وتعطيلها في وضع الإنتاج (Production) لتجنب أي تأثير على الأداء.


def calculate_invoice_total(items):
    subtotal = sum(item.price for item in items)
    
    # لنفترض أننا لا نتعامل مع فواتير مجانية أبدًا
    # هذا التأكيد يضمن أنه إذا حدث خطأ ما وأصبح المجموع صفرًا أو سالبًا،
    # سنكتشفه فورًا أثناء التطوير.
    assert subtotal > 0, f"المجموع الفرعي لا يمكن أن يكون صفرًا أو سالبًا. القيمة الحالية: {subtotal}"
    
    tax = subtotal * 0.15
    total = subtotal + tax
    return total

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

نصائح من مطبخ أبو عمر البرمجي

مع الخبرة، تتكون لديك مجموعة من القواعد الصغيرة التي تحميك. إليكم بعضًا منها:

  • “ثق ولكن تحقق”: حتى لو كنت تستدعي دالة كتبتها أنت بنفسك في ملف آخر، لا تفترض أنها ستعيد لك دائمًا ما تتوقعه. ماذا لو قام زميلك بتعديلها؟ تعامل مع مخرجات دوالك الأخرى كأنها قادمة من مصدر خارجي.
  • الكود الذي لا تكتبه لا يحتوي على أخطاء: كلما كان الكود أبسط وأصغر، كان الدفاع عنه أسهل. اتبع مبدأ المسؤولية الواحدة (Single Responsibility Principle). اجعل دوالك صغيرة، تفعل شيئًا واحدًا فقط، وتفعله جيدًا.
  • اجعل حالات الخطأ واضحة: بدلًا من إعادة null أو -1 للإشارة إلى خطأ، فكر في استخدام أنماط أكثر وضوحًا. يمكنك إما إلقاء استثناء (throw exception) أو إعادة كائن (object) يصف الحالة، مثل { success: false, error: 'User not found' }.
  • التسجيل (Logging) هو صديقك الوفي: عندما تواجه حالة غير متوقعة ولكنك تمكنت من معالجتها (باستخدام قيمة افتراضية مثلًا)، قم بتسجيل تحذير (Log a warning). سيساعدك هذا في اكتشاف المشاكل المحتملة لاحقًا دون إيقاف النظام. سجل الأخطاء الفادحة كأخطاء (Log an error). هذه السجلات هي خريطتك للكنز عندما تقع في جحيم “مش عارف شو صار”.

الخلاصة: من مطور خائف إلى مبرمج واثق 🛡️

البرمجة الدفاعية ليست مجرد إضافة المزيد من جمل if إلى الكود. إنها تغيير في العقلية. هي الانتقال من التفاؤل الساذج (“كل شيء سيكون على ما يرام”) إلى الواقعية الحذرة (“ماذا يمكن أن يحدث من خطأ هنا؟ وكيف أتعامل معه؟”).

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

اكتبوا كودكم وكأن كل مدخل هو مؤامرة، وستنامون قريري العين في الليل. دمتم مبدعين!

أبو عمر

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

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

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

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

آخر المدونات

خوارزميات

كانت تبعيات مهامنا كابوساً لا ينتهي: كيف أنقذنا ‘الفرز الطوبولوجي’ من جحيم التنفيذ العشوائي؟

أذكر جيداً تلك الأيام التي كانت فيها فوضى تنفيذ المهام المترابطة تهدد بإغراق مشروعنا. في هذه المقالة، أشارككم كيف كانت خوارزمية 'الفرز الطوبولوجي' هي طوق...

30 أبريل، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

كانت واجهاتنا خليطاً عجيباً: كيف أنقذتنا ‘رموز التصميم’ (Design Tokens) من فوضى التناقضات؟

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

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

كانت الأحمال المفاجئة تكسر ظهرنا: كيف أنقذتنا الحوسبة بدون خوادم (Serverless) من جحيم التوسع اليدوي؟

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

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