كنا نفترض أن المدخلات دائماً صحيحة: كيف أنقذتنا ‘البرمجة الدفاعية’ من جحيم الانهيارات غير المتوقعة؟

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

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

نص الليل، بلّش تلفوني يرن. مش مرة ولا مرتين، عشرات المرات. زبائن غاضبون، بيانات مضروبة، والنظام في حالة انهيار شبه كامل. رجعنا على المكتب بسرعة، وجوهنا صفرة والتوتر سيد الموقف. بعد ساعات من التنبيش في الـ logs (سجلات النظام)، اكتشفنا المصيبة. مصيبة بسيطة وتافهة لدرجة بتضحك وبتبكي بنفس الوقت.

أحد الحقول كان مخصص لإدخال “العمر”، وكنا فارضين إنه المستخدم دايماً راح يدخل رقم. لكن يبدو إنه أحد المستخدمين، يمكن عن طريق الخطأ أو الفضول، كتب عمره كتابةً: “ثلاثون عاماً”. الكود تبعنا، اللي كان متوقع رقم صحيح (integer)، استقبل نص (string) وصابته الجلطة البرمجية. هذا الخطأ الصغير عمل تأثير الدومينو وسبب سلسلة من الانهيارات في أجزاء ثانية من النظام.

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

ما هي البرمجة الدفاعية (Defensive Programming)؟

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

بدل ما نكتب كود “المسار السعيد” (Happy Path) بس، اللي هو المسار اللي كل شي فيه مثالي، البرمجة الدفاعية بتجبرنا نفكر: “طيب، شو ممكن يروح غلط؟” (What could go wrong?).

  • ماذا لو المستخدم أدخل نص بدل رقم؟
  • ماذا لو كانت القيمة null أو undefined؟
  • ماذا لو كان الملف اللي بحاول أقرأه مش موجود؟
  • ماذا لو كانت استجابة الـ API اللي بستناها فاضية أو فيها خطأ؟

البرمجة الدفاعية هي الإجابة العملية على كل هاي الأسئلة.

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

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

المبدأ الأول: لا تثق بأي مدخلات أبداً (Never Trust Input)

هذا هو حجر الأساس. “المدخلات” مش بس اللي بيكتبه المستخدم في فورم. المدخلات هي أي بيانات بتدخل على الكود تبعك من مصدر خارجي:

  • مدخلات المستخدم (User Input)
  • استجابات الواجهات البرمجية (API Responses)
  • البيانات اللي بتقرأها من قاعدة البيانات (Database Records)
  • البيانات من ملف (File Content)
  • الوسائط (Arguments) اللي بتمررها للدوال العامة (Public Functions)

مثال عملي (ساذج مقابل دفاعي):

تخيل عندك دالة بسيطة في بايثون بتحسب سعر الخصم.

الكود الساذج (Naive Code):


def calculate_discounted_price(price, discount_percentage):
    # نفترض أن المدخلات أرقام وصحيحة دائماً
    discount_amount = price * (discount_percentage / 100)
    return price - discount_amount

# هذا سيعمل بشكل جيد
# final_price = calculate_discounted_price(100, 20)  # الناتج: 80.0

# لكن ماذا لو...؟
# final_price = calculate_discounted_price(100, "عشرين") # سينهار البرنامج! TypeError

الكود الدفاعي (Defensive Code):


def calculate_discounted_price_defensive(price, discount_percentage):
    # 1. التحقق من نوع المدخلات
    if not isinstance(price, (int, float)) or not isinstance(discount_percentage, (int, float)):
        raise TypeError("السعر ونسبة الخصم يجب أن يكونا أرقاماً.")

    # 2. التحقق من منطقية القيم
    if not (0 <= price):
        raise ValueError("السعر لا يمكن أن يكون سالباً.")
    if not (0 <= discount_percentage <= 100):
        raise ValueError("نسبة الخصم يجب أن تكون بين 0 و 100.")

    # الآن فقط، بعد أن تأكدنا من كل شيء، نقوم بالحساب
    discount_amount = price * (discount_percentage / 100)
    return price - discount_amount

# الآن الكود محصّن
try:
    # final_price = calculate_discounted_price_defensive(100, 20) # يعمل
    # final_price = calculate_discounted_price_defensive(-50, 20) # يطلق خطأ ValueError
    final_price = calculate_discounted_price_defensive(100, "عشرين") # يطلق خطأ TypeError
    print(f"السعر النهائي: {final_price}")
except (TypeError, ValueError) as e:
    print(f"حدث خطأ: {e}") # يعطي رسالة واضحة بدلاً من الانهيار

لاحظ الفرق؟ الكود الثاني أطول، نعم، ولكنه أقوى ألف مرة. إنه لا ينهار، بل يخبرك بالزبط شو المشكلة.

المبدأ الثاني: انهار برشاقة (Fail Gracefully)

حتى مع أفضل التحصينات، الأخطاء ستحدث. الفكرة مش إنه نمنع الأخطاء 100%، هاد مستحيل. الفكرة هي كيف يتعامل نظامك مع الخطأ لما يصير.

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

فكر فيها زي لمبة “Check Engine” في السيارة. لما تولع، السيارة ما بتنفجر! بتكمل مشي (يمكن بأداء أقل) وبتعطيك إشارة واضحة إنه فيه مشكلة لازم فني يشوفها. الكود تبعك لازم يكون هيك:

  • للمستخدم: أظهر رسالة بسيطة وودودة. “عذراً، حدث خطأ ما. فريقنا يعمل على إصلاحه.”
  • للمطور (في الـ logs): سجّل كل تفصيلة ممكنة عن الخطأ: رسالة الخطأ الكاملة، الـ stack trace، المدخلات اللي سببت المشكلة، هوية المستخدم (إذا أمكن). هاد هو الصندوق الأسود اللي بيساعدك تحل المشكلة بسرعة.

المبدأ الثالث: استخدم التأكيدات (Assertions)

الـ Assertions هي أدوات رائعة للمبرمجين للتحقق من افتراضاتهم أثناء عملية التطوير. هي عبارة عن جملة بتقول: “أنا متأكد 100% إنه الشرط هذا لازم يكون صحيح في هاي النقطة من الكود. إذا ما كان صحيح، فيه مصيبة في اللوجيك تبعي ولازم البرنامج يوقف فوراً عشان أنتبهله”.

مثال بسيط في بايثون:


def connect_to_database(user):
    # أنا كـ مبرمج، أفترض أني لن أستدعي هذه الدالة إلا لمستخدم له صلاحيات
    assert user.is_admin, "محاولة اتصال بقاعدة البيانات من قبل مستخدم ليس أدمن!"
    # ... باقي كود الاتصال

نقطة مهمة: الـ Assertions تُستخدم عادةً في بيئة التطوير والاختبار، ويمكن (وغالبًا ما يتم) تعطيلها في بيئة الإنتاج (Production) لتحسين الأداء. لذلك، لا تعتمد عليها لمعالجة أخطاء المدخلات من المستخدمين (استخدم `if/else` و `try/except` لذلك)، بل استخدمها لاصطياد الأخطاء المنطقية في الكود تبعك.

نصائح عملية من صندوق عدة أبو عمر

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

1. قائمة “الأسئلة المذعورة” (The Paranoid Checklist)

قبل ما تعتبر أي دالة مكتملة، اسأل حالك هاي الأسئلة عن كل متغير ومدخل فيها:

  • ماذا لو كانت قيمته null أو None؟
  • ماذا لو كان نصاً فارغاً ""؟
  • ماذا لو كان رقماً سالباً وأنا أتوقع رقماً موجباً؟
  • ماذا لو كان صفراً؟ (القسمة على صفر مشكلة أزلية!)
  • ماذا لو كانت المصفوفة (Array) فارغة؟
  • ماذا لو احتوى النص على رموز غريبة أو إيموجي؟

مجرد التفكير في هاي الحالات راح يخلي الكود تبعك أقوى بعشر مرات.

2. مراجعات الكود (Code Reviews) هي خط دفاعك الأول

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

3. التزم بالبساطة (Keep It Simple)

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

4. التسجيل (Logging) ليس رفاهية

لا تقلل أبداً من أهمية وجود نظام تسجيل (Logging) جيد. لما تصير مشكلة في نظامك وهو شغال على سيرفرات الإنتاج، الـ logs هي عيونك وآذانك الوحيدة. سجل الأحداث المهمة، سجل الأخطاء مع تفاصيلها، وحتى سجل التحذيرات. في يوم من الأيام، سطر log واحد ممكن يوفر عليك ساعات من الصداع.

الخلاصة… وقهوة الصباح ✅

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

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

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

أبو عمر

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

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

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

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

آخر المدونات

أدوات وانتاجية

كانت ملاحظاتي التقنية ثقباً أسود: كيف أنقذني ‘العقل الثاني للمبرمج’ من جحيم المعرفة المبعثرة؟

أشارككم تجربتي الشخصية كـ "أبو عمر"، مبرمج فلسطيني، في التحول من فوضى الملاحظات المبعثرة إلى نظام "العقل الثاني" المنظم باستخدام أدوات مثل Obsidian ومنهجية Zettelkasten....

2 يونيو، 2026 قراءة المزيد
​معمارية البرمجيات

خدماتنا المتشابكة: كيف أنقذتنا المعمارية القائمة على الأحداث (EDA) من جحيم الاعتماديات؟

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

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