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

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

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

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

دخلنا على السيرفرات، قلوبنا بتدق زي طبول الحرب. بعد حفر وتنقيب في سجلات الأخطاء (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، ولا تدع كودك يكون متفائلاً أكثر من اللازم. 😉

أبو عمر

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

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

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

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

آخر المدونات

أتمتة العمليات

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

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

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

كانت قراراتنا أشباحاً تطاردنا: كيف أنقذتنا ‘سجلات القرارات المعمارية’ (ADRs) من جحيم “لماذا فعلنا ذلك؟”

قصص من قلب الميدان عن مشاريع كادت أن تنهار بسبب قرارات معمارية غامضة، وكيف كانت 'سجلات القرارات المعمارية' (ADRs) طوق النجاة الذي علّمنا أهمية توثيق...

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

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

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

25 مايو، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

من فوضى المكونات إلى لغة بصرية موحدة: كيف يبني ‘نظام التصميم’ (Design System) جسراً بين المطورين والمصممين؟

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

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

كان كودنا غارقاً في بحر SQL: كيف أنقذنا ‘الربط الكائني العلائقي’ (ORM) من جحيم الاستعلامات المتكررة؟

أشارككم قصة حقيقية من مسيرتي كمبرمج، عن مشروع كاد أن يغرق في فوضى استعلامات SQL المتكررة. سنكتشف معًا كيف كانت تقنية الربط الكائني العلائقي (ORM)...

25 مايو، 2026 قراءة المزيد
الشبكات والـ APIs

كان كل مايكروسيرفس قلعة منعزلة: كيف أنقذتنا ‘بوابة الواجهات البرمجية’ (API Gateway) من جحيم الفوضى؟

في عالم الخدمات المصغرة (Microservices)، يمكن أن تتحول المرونة إلى فوضى عارمة. هذه قصة من تجربتي كـ "أبو عمر"، مبرمج فلسطيني، وكيف كانت بوابة الواجهات...

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