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

القصة: يوم الكارثة، أو كيف تعلمنا الدرس بالطريقة الصعبة

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

أجا يوم الإطلاق، قهوتنا الصبح كانت بنكهة النصر، كلنا متفائلين. أطلقنا النظام، والأمور أول ساعة كانت تمام. فجأة، بلشت توصلنا اتصالات من العميل: “النظام واقع!”، “الصفحة الرئيسية ما بتفتح!”، “في رسالة خطأ غريبة بتطلع!”.

دخلنا على السيرفرات، قلوبنا بتدق زي طبول الحرب. بعد حفر وتنقيب في سجلات الأخطاء (Logs)، اكتشفنا المصيبة. أحد المستخدمين، موظف بسيط عند العميل، حاول يضيف اسمه في ملفه الشخصي. المشكلة؟ اسمه كان فيه رمز تعبيري (emoji) بسيط: 🙂. الكود تبعنا، اللي كان متفائل زيادة عن اللزوم، كان يتوقع حروف وأرقام بس. لما استقبل الـ emoji، قاعدة البيانات رفضته، والكود ما كان عنده أي فكرة كيف يتعامل مع هذا الرفض، فانهار النظام كله. نعم، يا جماعة، سمايلي فيس 🙂 أسقط نظاماً كلف شهوراً من العمل.

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

ما هي البرمجة الدفاعية (Defensive Programming)؟ مش مجرد try-catch!

لما نحكي “برمجة دفاعية”، كثير ناس بيفكروا فوراً بـ try-catch. صحيح هي جزء من الموضوع، بس القصة أكبر من هيك بكثير. البرمجة الدفاعية هي عقلية ومنهجية قبل ما تكون مجرد كود.

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

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

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

  • المبرمج المتفائل: “بالتأكيد سيمرر لي المستخدم رقماً صحيحاً في حقل العمر!”
  • المبرمج الدفاعي: “ماذا لو أدخل المستخدم ‘خمسة وعشرون’ كنص؟ ماذا لو تركه فارغاً؟ ماذا لو أدخل رقماً سالباً؟ ماذا لو أدخل رمزاً تعبيرياً لوجه غاضب 😡؟”

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

أسلحة المبرمج الدفاعي: كيف تحصّن كودك؟

طيب يا أبو عمر، كلام جميل، بس كيف نطبق هذا الحكي عملياً؟ بسيطة، هي مجموعة من التقنيات والأساليب اللي لازم تصير جزء من روتينك اليومي.

1. التحقق من المدخلات (Input Validation): لا تثق بأحد، ولا حتى بنفسك!

هذه هي القاعدة الذهبية الأولى. أي بيانات تدخل إلى دالة (function) أو وحدة (module) من مصدر خارجي يجب التحقق منها. المصدر الخارجي ممكن يكون:

  • مدخلات المستخدم في نموذج (form).
  • بيانات قادمة من API.
  • بيانات مقروءة من ملف أو قاعدة بيانات.
  • حتى البيانات القادمة من جزء آخر من نظامك!

مثال (JavaScript): لنفترض أن لدينا دالة تحسب سعر الخصم.

الكود المتفائل (السيء):

function calculateDiscount(price, percentage) {
  // ماذا لو كان الـ percentage نصاً أو قيمة سالبة؟ كارثة!
  return price * (percentage / 100);
}

الكود الدفاعي (الجيد):

function calculateDiscount(price, percentage) {
  // 1. التحقق من النوع (Type checking)
  if (typeof price !== 'number' || typeof percentage !== 'number') {
    throw new Error("المدخلات يجب أن تكون أرقاماً.");
  }

  // 2. التحقق من النطاق (Range checking)
  if (price < 0 || percentage  100) {
    throw new Error("القيم غير منطقية. السعر والنسبة يجب أن تكونا موجبة والنسبة لا تتجاوز 100.");
  }
  
  // الآن فقط، بعد التحقق، يمكننا تنفيذ المنطق البرمجي بأمان
  return price * (percentage / 100);
}

// الاستخدام الآمن
try {
  let discount = calculateDiscount(100, "عشرة"); // سيتم إلقاء خطأ هنا
  console.log(discount);
} catch (e) {
  console.error("حدث خطأ: " + e.message);
}

2. التعامل مع القيم الفارغة (Null/Undefined): وباء الـ NullPointerException

كم مرة رأيت الخطأ Cannot read properties of null أو NullPointerException؟ هذا الخطأ هو كابوس المبرمجين. البرمجة الدفاعية تفرض عليك أن تتوقع دائماً أن أي كائن (object) قد يكون null.

مثال (JavaScript): محاولة الوصول لبيانات متداخلة في كائن.

الكود المتفائل (السيء):

// إذا كان response أو response.data غير موجود، سينهار الكود.
const userName = response.data.user.name;

الكود الدفاعي (الجيد):

باستخدام تقنية Optional Chaining (?.) و Nullish Coalescing (??) الحديثة:

// إذا كان أي جزء من السلسلة null أو undefined، ستعود القيمة الافتراضية 'زائر' بأمان.
const userName = response?.data?.user?.name ?? 'زائر';
console.log(`مرحباً، ${userName}`);

هذا السطر الصغير يختصر أربعة if statements ويجعل الكود أكثر أماناً وقراءة.

3. التأكيدات (Assertions): صرخة في وجه الكود الخاطئ

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

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

مثال (Python):

def calculate_average(numbers):
  # تأكيد: يجب ألا تكون القائمة فارغة، وإلا فما معنى المتوسط؟
  # هذا خطأ منطقي يجب أن لا يحدث أبداً في كود سليم.
  assert len(numbers) > 0, "لا يمكن حساب المتوسط لقائمة فارغة."
  
  return sum(numbers) / len(numbers)

# الاستخدام الصحيح
calculate_average([1, 2, 3]) 

# هذا السطر سيُطلق خطأ AssertionError في بيئة التطوير، مما ينبه المبرمج فوراً
# calculate_average([])

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

الكود الدفاعي لا ينهار. بدلاً من ذلك، يتعامل مع الخطأ بطريقة مدروسة. هذا قد يعني:

  • تسجيل الخطأ (Logging): تسجيل تفاصيل الخطأ في ملف أو خدمة مراقبة لتحليله لاحقاً.
  • إظهار رسالة للمستخدم: عرض رسالة بسيطة ومفيدة للمستخدم مثل “حدث خطأ ما، يرجى المحاولة مرة أخرى” بدلاً من صفحة بيضاء أو رسالة خطأ تقنية.
  • إرجاع قيمة افتراضية: كما رأينا في مثال ?? 'زائر'.
  • إعادة المحاولة (Retry Logic): في حالة التعامل مع الشبكات، قد يكون من المفيد إعادة محاولة الطلب مرة أو مرتين قبل الاستسلام.

نصائح من “أبو عمر”: خلاصة سنين من التجارب

  • الكود الذي تكتبه اليوم، سيقرأه شخص آخر غداً (قد يكون أنت!): اكتب كوداً واضحاً ودفاعياً، فـ “أنت” المستقبلي سيشكرك عندما يعود لإصلاح شيء ما بعد 6 أشهر وقد نسي كل شيء عن هذا الكود.
  • افترض الأسوأ دائماً: الشبكة ستقطع، قاعدة البيانات ستكون بطيئة، المستخدم سيدخل بيانات “عجيبة غريبة”. كن مستعداً.
  • اجعل الفشل خياراً متوقعاً: صمم دوالّك (functions) بحيث يكون الفشل جزءاً من مخرجاتها الطبيعية (مثلاً، إرجاع null أو إلقاء خطأ متوقع) بدلاً من الانهيار غير المتحكم فيه.
  • لا تعد اختراع العجلة: استخدم مكتبات موثوقة للتحقق من المدخلات (مثل zod في عالم JavaScript/TypeScript أو Pydantic في Python). هذه المكتبات تم اختبارها على آلاف الحالات التي لم تفكر بها حتى.

الخلاصة: كن مبرمجاً واقعياً، لا متفائلاً أعمى

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

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

أبو عمر

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

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

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

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

آخر المدونات

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

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