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

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

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

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

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

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

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

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

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

الكود الدفاعي لا يفترض “المسار السعيد” (Happy Path) فقط، بل يستعد لكل المسارات المظلمة والملتوية التي قد يسلكها المستخدمون أو الأنظمة الأخرى.

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

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

1. التحقق من المدخلات (Sanitizing and Validating Inputs)

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

مهمتك هي التحقق من كل شيء قبل معالجته.

  • التحقق من النوع (Type Checking): هل هذا الحقل رقم حقاً؟ هل هو نص؟
  • التحقق من النطاق (Range Checking): إذا كان الحقل للعمر، فهل القيمة بين 0 و 120؟ لا يمكن أن يكون العمر سالباً.
  • التحقق من الصيغة (Format Checking): هل صيغة البريد الإلكتروني صحيحة؟ هل صيغة التاريخ سليمة؟
  • التحقق من عدم الفراغ (Non-null/Not-empty Checking): هل الحقل المطلوب فارغ؟

مثال عملي (JavaScript)

لنتخيل دالة تحسب السعر الإجمالي بناءً على الكمية. هذا هو الكود “الساذج” الذي كتبته في البداية:

// الطريقة الخطرة (الساذجة)
function calculateTotal(price, quantity) {
  // ماذا لو كانت quantity نصاً مثل "قطعتين"؟
  // سيعطينا الناتج NaN (Not a Number) الذي قد يسبب مشاكل لاحقاً
  return price * quantity; 
}

الآن، لنعد كتابتها بأسلوب دفاعي:

// الطريقة الدفاعية (الحصينة)
function calculateTotalDefensive(price, quantity) {
  // 1. التحقق من أن المدخلات أرقام
  if (typeof price !== 'number' || typeof quantity !== 'number') {
    console.error("خطأ: السعر والكمية يجب أن يكونا أرقاماً.");
    return 0; // إرجاع قيمة آمنة افتراضية
  }

  // 2. التحقق من أن الأرقام منطقية (ليست سالبة)
  if (price < 0 || quantity < 0) {
    console.error("خطأ: لا يمكن أن يكون السعر أو الكمية قيمة سالبة.");
    return 0; // إرجاع قيمة آمنة
  }

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

// تجربة
console.log(calculateTotalDefensive(10, 5)); // الناتج: 50
console.log(calculateTotalDefensive(10, "خمسة")); // الناتج: 0 (مع رسالة خطأ في الـ console)
console.log(calculateTotalDefensive(10, -2)); // الناتج: 0 (مع رسالة خطأ في الـ console)

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

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

حتى مع التحقق من المدخلات، ستحدث أخطاء. قد يفشل الاتصال بالشبكة، قد تكون قاعدة البيانات غير متاحة، قد تتغير واجهة برمجية خارجية (API) فجأة. البرمجة الدفاعية لا تهدف لمنع الأخطاء 100%، بل تهدف للتعامل معها عند حدوثها بطريقة لا تؤدي لانهيار التطبيق.

هنا يأتي دور كتل try...catch. إنها تسمح لك بـ “تجربة” تنفيذ كود قد يفشل، وفي حال فشله، يمكنك “التقاط” الخطأ والتعامل معه.

مثال عملي: جلب بيانات من API

تخيل أنك تجلب بيانات مستخدم من سيرفر. ماذا لو كان السيرفر معطلاً؟

// طريقة غير آمنة
async function fetchUserData(userId) {
  const response = await fetch(`https://api.example.com/users/${userId}`);
  const userData = await response.json(); // سينهار هنا لو فشل الطلب
  console.log(userData.name);
}

الآن، الطريقة الدفاعية:

// طريقة آمنة باستخدام try...catch
async function fetchUserDataDefensive(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);

    // التحقق من نجاح الاستجابة (e.g., status 200)
    if (!response.ok) {
      // إلقاء خطأ بشكل مقصود ليتم التقاطه في الـ catch
      throw new Error(`فشل الاتصال بالـ API. رمز الحالة: ${response.status}`);
    }

    const userData = await response.json();
    console.log(userData.name);
    // اعرض بيانات المستخدم في الواجهة
    
  } catch (error) {
    // هذا الكود سيعمل فقط في حال حدوث أي خطأ في كتلة الـ try
    console.error("حدث خطأ أثناء جلب بيانات المستخدم:", error.message);
    // هنا يمكنك عرض رسالة لطيفة للمستخدم
    // مثلاً: "عفواً، لم نتمكن من تحميل بياناتك. الرجاء المحاولة لاحقاً."
  }
}

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

3. استخدام التأكيدات (Assertions)

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

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

عادةً ما يتم تعطيل التأكيدات في النسخة الإنتاجية (Production) من البرنامج لأنها تستهلك بعض الموارد، وهدفها هو اكتشاف الأخطاء المنطقية أثناء مرحلة التطوير والاختبار.

مثال عملي

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

function divide(numerator, denominator) {
  // تأكيد للمطور: هذا يجب ألا يحدث أبداً في منطق البرنامج
  console.assert(denominator !== 0, "خطأ منطقي: تم استدعاء دالة القسمة مع قاسم يساوي صفر!");

  // الكود الفعلي
  return numerator / denominator;
}

divide(10, 2); // يعمل بصمت
divide(10, 0); // سيطبع رسالة الخطأ في الـ console

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

نصائح من “الختيار” (خبرتي الشخصية)

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

  1. فكّر مثل المستخدم “المخرّب”: قبل أن تطلق أي ميزة جديدة، حاول أن تكسرها بنفسك. ماذا يحدث لو أدخلت نصوصاً عربية في حقل إنجليزي؟ ماذا لو أدخلت رموزاً تعبيرية 😈؟ ماذا لو ضغطت على الزر 100 مرة في الثانية؟ هذا الاختبار الفوضوي سيكشف لك نقاط ضعف لم تخطر لك على بال.
  2. لا تُفرط في الدفاع: البرمجة الدفاعية مهمة، لكن المبالغة فيها قد تجعل الكود معقداً وصعب القراءة. القاعدة الجيدة هي: كن صارماً جداً عند “حدود” نظامك (نقاط إدخال البيانات)، وكن أكثر ثقة بالكود الداخلي الذي تتحكم فيه أنت. لا داعي للتحقق من أن 2+2=4 في كل مرة.
  3. الرسائل الواضحة كنز: عندما تلتقط خطأ، لا تكتفِ بطباعة رسالة غامضة. سجّل (log) أكبر قدر ممكن من المعلومات المفيدة التي ستساعدك لاحقاً على تصحيح الخطأ: ما هي الدالة التي فشلت؟ ما هي المدخلات التي سببت الفشل؟ رسالة الخطأ “Error” لا تفيد أحداً، لكن رسالة “Failed to parse user age. Input was ‘twenty’, expected a number” هي كنز من المعلومات.
  4. اجعل الفشل خياراً آمناً: صمم أجزاء تطبيقك بحيث إذا فشل جزء، لا ينهار الكل. في صفحة الملف الشخصي مثلاً، إذا فشل تحميل “آخر النشاطات”، اعرض باقي الصفحة (الاسم، الصورة) مع رسالة “تعذر تحميل النشاطات” بدلاً من عرض شاشة بيضاء فارغة.

الخلاصة: الكود الحصين هو الذي يعيش طويلاً 💪

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

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

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

أتمنى لكم كتابة كود حصين ومستقر. وإلى لقاء آخر في مقالة جديدة. 🙏

أبو عمر

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

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

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

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

آخر المدونات

اختبارات الاداء والجودة

تطبيقي كان يعمل كالساعة… حتى أتى ‘الجمعة السوداء’: كيف أنقذني ‘اختبار الحِمل’ (Load Testing) من انهيار مفاجئ؟

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

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

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

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

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

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

أشارككم يا جماعة قصة من الميدان، كيف حولت نظامًا برمجيًا معقدًا ومترابطًا إلى تحفة فنية مرنة وقابلة للتوسع باستخدام المعمارية القائمة على الأحداث (Event-Driven Architecture)....

28 مارس، 2026 قراءة المزيد
تسويق رقمي

محتواي كان شبحاً في محركات البحث: كيف أنقذتني البيانات المنظمة (Structured Data) من جحيم الغموض الرقمي؟

أشارككم قصتي مع موقعي الذي كان خفياً تماماً في جوجل، وكيف استطعت إخراجه للنور باستخدام البيانات المنظمة (Structured Data) و Schema.org. هذه ليست مجرد مقالة...

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

خوادمي كانت تلتهم ميزانيتي: كيف أنقذتني الحوسبة “بدون خوادم” (Serverless) من فواتير السحابة المتضخمة؟

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

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