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

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

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

واحد من الشباب الجداد في الفريق، شب صغير ومليان حماس، كان مسؤول عن جزئية رفع الصور للمقالات. وهو بجرب، بدل ما يختار صورة، سحب ملف PDF بالغلط ورماه في مربع الرفع. فجأة، الشاشة صارت بيضا، والنظام كله “علّق”. مش بس صفحته هو، النظام كله صار يرجع خطأ 500 (Internal Server Error) لكل المستخدمين. يا زلمة، كانت لحظة رعب، وكأنك سكبت كاسة قهوة على لاب توب جديد.

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

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

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

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

البرمجة الدفاعية لا تعني التشاؤم، بل تعني الاحترافية والمسؤولية. أنت تبني برمجيات لتعيش في العالم الحقيقي الفوضوي، وليس في عالم مثالي.

لماذا هي مهمة لهذه الدرجة؟

يمكن حدا يسأل: “ليش أغلب حالي؟ المفروض المستخدم يعرف شو يدخل!”. هذا تفكير مثالي، والبرمجة لا تعمل في عالم مثالي. أهميتها تكمن في:

  • بناء أنظمة صامدة (Robust): الكود الدفاعي لا ينهار عند أول مشكلة. بدلًا من الانهيار، يتعامل مع الخطأ بأناقة، ممكن يعرض رسالة للمستخدم، أو يسجل الخطأ للمطورين، أو يستخدم قيمة افتراضية ويكمل شغله.
  • توفير الوقت والجهد على المدى الطويل: قضاء ساعة إضافية في كتابة كود دفاعي يوفر عليك أيام من تصحيح الأخطاء (Debugging) في المستقبل. تصليح خطأ في نظام شغال عند العميل أصعب وأغلى بـ 10 مرات من منعه من الأساس.
  • الحماية والأمان (Security): الكثير من الثغرات الأمنية، مثل حقن SQL (SQL Injection) أو XSS، سببها الأساسي هو الثقة العمياء في مدخلات المستخدم. التحقق من المدخلات هو خط الدفاع الأول ضد هذه الهجمات.
  • راحة البال: والله يا جماعة، ما في أحلى من إنك تنام بالليل وأنت عارف إن نظامك قوي، وإنه مش رح يصحوك من النوم بسبب خطأ تافه كان ممكن تتجنبه.

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

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

1. تحقق، ثم تحقق، ثم تحقق: لا تثق بأي مدخلات!

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

  • مدخلات المستخدم (من فورم، من رابط URL، …إلخ).
  • البيانات القادمة من واجهات برمجية خارجية (APIs).
  • البيانات المقروءة من قاعدة البيانات أو من ملف.
  • حتى البيانات القادمة من أجزاء أخرى من نظامك!

مثال: دالة لحساب الخصم (قبل وبعد)

لنفترض عنا دالة بسيطة بلغة Python بتحسب سعر المنتج بعد الخصم.

النسخة الساذجة (Non-Defensive):


# النسخة التي تثق بالمدخلات ثقة عمياء
def calculate_discounted_price_naive(price, discount_percentage):
    discount_amount = price * (discount_percentage / 100)
    return price - discount_amount

# استدعاء صحيح
print(calculate_discounted_price_naive(100, 20))  # Output: 80.0

# استدعاءات كارثية محتملة
# print(calculate_discounted_price_naive(100, "twenty")) # يسبب TypeError
# print(calculate_discounted_price_naive(-50, 20))      # نتيجة غير منطقية
# print(calculate_discounted_price_naive(100, None))     # يسبب TypeError

هذا الكود سينهار أو يعطي نتائج خاطئة مع أي مدخل غير متوقع. الآن شوف النسخة الدفاعية.

النسخة الدفاعية (Defensive):


# النسخة الحصينة التي تتوقع الأسوأ
def calculate_discounted_price_defensive(price, discount_percentage):
    # التحقق من أن المدخلات ليست None
    if price is None or discount_percentage is None:
        # لا تترك الخطأ يمر بصمت، سجل الخطأ أو ارمِ استثناءً واضحًا
        raise ValueError("السعر ونسبة الخصم لا يمكن أن يكونا فارغين")

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

    # التحقق من منطقية القيم
    if price < 0 or discount_percentage  100:
        raise ValueError("القيم المدخلة غير منطقية (سعر سالب أو خصم غير صالح)")

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

# استدعاء صحيح
print(calculate_discounted_price_defensive(100, 20)) # Output: 80.0

# الاستدعاءات الكارثية الآن يتم التعامل معها بأناقة
try:
    calculate_discounted_price_defensive(100, "twenty")
except TypeError as e:
    print(f"حدث خطأ متوقع: {e}") # Output: حدث خطأ متوقع: السعر ونسبة الخصم يجب أن يكونا أرقامًا

شايفين الفرق؟ الكود الثاني أطول شوي، ولكنه قوي، واضح، ويمكن الاعتماد عليه.

2. تعامل مع الأخطاء بأناقة (Graceful Error Handling)

لما يصير خطأ، عندك خيارين: يا تترك البرنامج ينهار ويشتم المستخدم، يا إما تمسك الخطأ وتتصرف معه بحكمة. استخدم كتل try...catch (في JavaScript/Java/C#) أو try...except (في Python) بكثرة.


# مثال بلغة JavaScript: التعامل مع رد من API
async function fetchUserData(userId) {
    try {
        const response = await fetch(`https://api.example.com/users/${userId}`);

        // تحقق من نجاح الطلب (مش كل رد 200 يعني نجاح)
        if (!response.ok) {
            console.error(`خطأ في الشبكة: ${response.status}`);
            return null; // ارجع بقيمة آمنة
        }

        const data = await response.json(); // هذه العملية ممكن تفشل لو الرد مش JSON
        return data;
    } catch (error) {
        // هذا يمسك أخطاء الشبكة (مثل فصل الانترنت) أو أخطاء الـ JSON parsing
        console.error("فشل في جلب البيانات، ربما مشكلة في الشبكة أو في رد الخادم", error);
        // لا تترك المستخدم معلق، ارجع بقيمة افتراضية أو null
        return null;
    }
}

3. استخدم التوكيدات (Assertions) للعقود الداخلية

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

الفكرة هي: استخدم التوكيدات للتأكد من الأشياء اللي “مستحيل” تصير. لو صارت، معناها في خطأ منطقي في كودك أنت، مش خطأ من المستخدم.


# مثال في Python
def process_internal_data(items):
    # هذا الشرط يجب أن يكون صحيحًا دائمًا إذا كان باقي الكود يعمل بشكل صحيح
    # نحن نؤكد أن القائمة ليست فارغة قبل معالجتها
    assert len(items) > 0, "تم استدعاء الدالة بقائمة فارغة، وهذا لا يجب أن يحدث!"
    
    # ... باقي منطق الدالة ...
    print("تمت معالجة العناصر بنجاح")

# استدعاء صحيح
process_internal_data([1, 2, 3])

# استدعاء خاطئ من جزء آخر من الكود (خطأ برمجي)
# process_internal_data([]) # سيؤدي إلى AssertionError مع رسالة واضحة

ملاحظة مهمة: التوكيدات عادةً يتم تعطيلها في النسخة النهائية (Production) لتحسين الأداء، لذا لا تعتمد عليها للتحقق من مدخلات المستخدم، بل استخدم معالجة الأخطاء العادية (If-statements, try-except) لتلك الحالات.

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

  • تبنى عقلية “الشكّاك”: قبل كتابة أي دالة، اسأل نفسك: “ما هي أغبى الطرق التي يمكن أن يساء بها استخدام هذه الدالة؟”. فكر في القيم الفارغة (null/None)، الأنواع الخاطئة (رقم مكان نص)، القيم خارج النطاق (عمر سالب)، النصوص الفارغة، إلخ.
  • مراجعات الكود (Code Reviews) هي خط دفاعك الأول: زميلك ممكن يكتشف افتراض أنت ما انتبهت له. شجعوا ثقافة “ليش ما حطيت check هون؟” في الفريق.
  • استخدم مكتبات للتحقق (Validation Libraries): لا تعيد اختراع العجلة. في كل لغة تقريبًا توجد مكتبات قوية تسهل عملية التحقق من البيانات مثل Zod و Joi في عالم JavaScript/TypeScript، أو Pydantic في عالم Python.
  • السجلات (Logs) هي صديقك الوفي: عندما تفشل عملية تحقق، لا تفشل بصمت. سجل الخطأ مع البيانات التي سببت المشكلة. عندما يكلمك العميل ويقول “النظام ما اشتغل”، هذه السجلات ستكون دليلك الوحيد لمعرفة ما حدث.

الخلاصة: من الآخر

يا جماعة، البرمجة الدفاعية مش رفاهية أو “شغلة زيادة”. هي أساس بناء البرمجيات الاحترافية والموثوقة. هي الفرق بين نظام ينهار مع أول عاصفة، ونظام يبقى صامدًا وقويًا في وجه فوضى العالم الحقيقي.

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

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

أبو عمر

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

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

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

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

آخر المدونات

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

تحديثات قاعدة البيانات بدون توقف: كيف أنقذنا نمط التوسيع والتعاقد (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 قراءة المزيد
البودكاست