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

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

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

واحد من الشباب الجداد في الفريق، شب صغير ومليان حماس، كان مسؤول عن جزئية رفع الصور للمقالات. وهو بجرب، بدل ما يختار صورة، سحب ملف 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) هي صديقك الوفي: عندما تفشل عملية تحقق، لا تفشل بصمت. سجل الخطأ مع البيانات التي سببت المشكلة. عندما يكلمك العميل ويقول “النظام ما اشتغل”، هذه السجلات ستكون دليلك الوحيد لمعرفة ما حدث.

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

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

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

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

أبو عمر

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

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

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

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

آخر المدونات

التوسع والأداء العالي والأحمال

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

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

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

كانت دفاترنا لا تتطابق أبداً: كيف أنقذنا ‘نظام التسوية الآلي’ من جحيم الأخطاء المالية الصامتة؟

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

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

كانت حاوياتنا جزراً منعزلة: كيف أنقذنا Kubernetes من جحيم التنسيق اليدوي؟

أشارككم قصة من أرض المعركة التقنية، كيف انتقلنا من فوضى إدارة حاويات Docker اليدوية إلى عالم الأتمتة المنظم مع Kubernetes. مقالة عملية للمطورين ومسؤولي الأنظمة...

9 مايو، 2026 قراءة المزيد
أدوات وانتاجية

كانت معرفتي التقنية تتلاشى: كيف أنقذني نظام ‘الدماغ الثاني’ من جحيم إعادة اختراع العجلة؟

أشارككم قصتي كـ "أبو عمر"، مطور برمجيات، مع تلاشي المعرفة التقنية وكيف أنقذني بناء "دماغ ثانٍ" باستخدام أداة مثل Obsidian. اكتشفوا كيف تحولت من إعادة...

9 مايو، 2026 قراءة المزيد
أتمتة العمليات

كانت مهامنا الخلفية كابوساً من السباغيتي: كيف أنقذتنا ‘محركات سير العمل’ (Workflow Engines) من جحيم الفشل الصامت؟

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

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

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

أشارككم قصة حقيقية من قلب المعركة التقنية، حيث واجهنا مهمة تحديث نظام قديم ومعقد. في هذه المقالة، سأشرح كيف أنقذنا نمط "التين الخانق" (Strangler Fig...

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