يا جماعة الخير، السلام عليكم ورحمة الله. معكم أخوكم أبو عمر.
قبل كم سنة، ورثت مشروع ضخم من مبرمج سابق ترك الشركة. كان نظام إدارة معقد، والحكي بينا، الشيفرة كانت عبارة عن طلاسم! أتذكر ذاك اليوم جيدًا، كان يوم خميس، وكنت أحاول إصلاح علّة (bug) مستعصية في نظام معالجة الطلبات. المشكلة كانت أن بعض الطلبات تدخل في حالة غريبة لا هي مُلغاة ولا هي مكتملة.
بعد ساعات من التنقيب في الكود، وصلت لسطر زي هيك:
if (order.status == 7) {
// ... منطق معقد وغامض
}
وقفت صافنًا… “7”؟ شو يعني 7؟ يا زلمة، لا يوجد أي تعليق، ولا أي توثيق. قلّبت المشروع فوقاني تحتاني، وبحثت في قاعدة البيانات، وفتحت كل ملف خطر على بالي. بعد حوالي ثلاث ساعات من البحث المحموم، وجدتها! وجدتها مدفونة في تعليق قديم في ملف بعيد كل البعد عن منطق الطلبات، مكتوب أنه “الحالة 7 تعني ‘معلّق بانتظار موافقة المدير المالي'”.
في تلك اللحظة، لم أشعر بالانتصار بقدر ما شعرت بالإحباط. ثلاث ساعات من وقتي ضاعت بسبب رقم يتيم! هذا الرقم هو ما نسميه في عالم البرمجة “الرقم السحري” (Magic Number)، ويومها أخذت عهدًا على نفسي أن أحاربه في كل شيفرة أكتبها أو أراجعها. اليوم، سأشارككم أسلحتي في هذه المعركة.
ما هي “الأرقام السحرية”؟ ولماذا هي كابوس المبرمجين؟
ببساطة، الرقم السحري هو أي رقم يظهر في شيفرتك البرمجية بدون أي تفسير أو اسم يدل على معناه. هو مجرد قيمة عددية (أو حتى نصية أحيانًا) تجعل من يقرأ الكود يسأل نفسه: “ماذا يمثل هذا الرقم؟”.
دعونا نأخذ مثالاً بسيطًا لنظام إدارة مهام:
function getTaskStatus(status) {
if (status === 0) {
return "جديدة";
} else if (status === 1) {
return "قيد التنفيذ";
} else if (status === 2) {
return "مكتملة";
} else if (status === -1) {
return "ملغاة";
}
}
// في مكان آخر من الكود
if (currentUser.task.status === 2) {
console.log("أحسنت! لقد أنجزت المهمة.");
}
هذا الكود “يعمل”، لكنه حقل ألغام حقيقي. لماذا؟
- صعوبة القراءة (Readability): عندما تقرأ
if (status === 2)، أنت لا تعرف فورًا ماذا تعني “2”. هل هي مكتملة؟ هل هي مؤجلة؟ يجب عليك العودة للدالةgetTaskStatusأو البحث في مكان آخر لفهم السياق. - صعوبة الصيانة (Maintainability): تخيل لو قررنا تغيير قيمة الحالة “مكتملة” من 2 إلى 5. سيتوجب عليك البحث في المشروع بأكمله عن كل الأماكن التي استخدم فيها الرقم 2 واستبدالها، مع المخاطرة بأنك قد تستبدل رقم 2 يُستخدم في سياق آخر تمامًا!
- مصدر للأخطاء (Bugs): ما الذي يمنعك من أن تخطئ وتكتب
if (status === 3)عن طريق السهو؟ الرقم 3 ليس له معنى هنا، والبرنامج لن يعطيك أي خطأ، ولكنه ببساطة لن يعمل كما هو متوقع.
السلاح الأول: الثوابت (Constants) – رصاصة الرحمة على الغموض
أول وأبسط حل لهذه المشكلة هو استخدام الثوابت. الثابت هو متغير نُعرّفه مرة واحدة ونعطيه اسمًا ذا معنى، ولا يمكن تغيير قيمته لاحقًا.
تطبيق عملي: لنُصلح شيفرة المهام
بدلاً من استخدام الأرقام مباشرة، سنقوم بتعريفها كثوابت في مكان مركزي واحد:
// في ملف إعدادات أو ملف خاص بالثوابت
const TASK_STATUS_NEW = 0;
const TASK_STATUS_IN_PROGRESS = 1;
const TASK_STATUS_COMPLETED = 2;
const TASK_STATUS_CANCELLED = -1;
// الآن، الكود يصبح أوضح بكثير
function getTaskStatus(status) {
if (status === TASK_STATUS_NEW) {
return "جديدة";
} else if (status === TASK_STATUS_IN_PROGRESS) {
return "قيد التنفيذ";
} else if (status === TASK_STATUS_COMPLETED) {
return "مكتملة";
} else if (status === TASK_STATUS_CANCELLED) {
return "ملغاة";
}
}
// انظر إلى مدى وضوح هذا السطر الآن!
if (currentUser.task.status === TASK_STATUS_COMPLETED) {
console.log("أحسنت! لقد أنجزت المهمة.");
}
الفارق شاسع! الآن الكود يتحدث عن نفسه. TASK_STATUS_COMPLETED لا تترك أي مجال للشك. وإذا احتجنا في المستقبل لتغيير قيمة الحالة المكتملة، نغيرها في مكان واحد فقط.
نصيحة من أبو عمر: اعتمد أسلوب تسمية واضح للثوابت، مثل استخدام الأحرف الكبيرة (UPPER_SNAKE_CASE). هذا يجعل من السهل تمييزها بصريًا عن المتغيرات العادية في الكود.
السلاح المتقدم: التعدادات (Enums) – تنظيم وتجميع الحالات
الثوابت حل ممتاز، ولكن عندما يكون لديك مجموعة من الحالات المترابطة (مثل حالات الطلب، أو أنواع المستخدمين، أو مستويات الصلاحية)، هناك أداة أفضل وأكثر تنظيمًا: التعدادات (Enumerations أو Enums).
الـ Enum هو نوع بيانات خاص يتيح لك تجميع مجموعة من الثوابت ذات الصلة تحت اسم واحد.
الارتقاء بالشيفرة باستخدام Enums
لنأخذ مثالنا السابق ونطوره باستخدام Enums (سأستخدم صيغة تشبه لغة TypeScript كمثال، ولكن المبدأ موجود في لغات كثيرة مثل C#, Java, Python, وغيرها).
enum TaskStatus {
New = 0,
InProgress = 1,
Completed = 2,
Cancelled = -1,
}
// الدالة أصبحت أكثر قوة وأمانًا
function getTaskStatus(status: TaskStatus): string {
// يمكن استخدام switch statement هنا للتنظيم أكثر
switch (status) {
case TaskStatus.New:
return "جديدة";
case TaskStatus.InProgress:
return "قيد التنفيذ";
case TaskStatus.Completed:
return "مكتملة";
case TaskStatus.Cancelled:
return "ملغاة";
default:
// التعامل مع الحالات غير المتوقعة
throw new Error("حالة غير معروفة");
}
}
// الكود في غاية الوضوح والأمان
if (currentUser.task.status === TaskStatus.Completed) {
console.log("أحسنت! لقد أنجزت المهمة.");
}
// محاولة استخدام قيمة خاطئة ستسبب خطأ (في اللغات ذات الأنواع الصارمة)
// processTask(5); // هذا السطر سيُظهر خطأ وقت الترجمة!
لماذا الـ Enums أفضل في هذه الحالة؟
- التجميع والتنظيم: كل الحالات المتعلقة بالمهام مجموعة تحت اسم واحد هو
TaskStatus. هذا يجعل الكود أكثر تنظيمًا. - الأمان من الأخطاء (Type Safety): في اللغات التي تدعم أنواع البيانات بقوة (like TypeScript, C#)، لا يمكنك تمرير أي رقم عشوائي لدالة تتوقع
TaskStatus. يجب أن تستخدم قيمة من الـ Enum نفسه (مثلTaskStatus.Completed)، وهذا يمنع فئة كاملة من الأخطاء المنطقية. - الإكمال التلقائي (Autocomplete): عند كتابة
TaskStatus.، سيقترح عليك محرر الأكواد كل الخيارات المتاحة (New, InProgress, …)، مما يقلل من الأخطاء الكتابية ويسرّع عملية التطوير.
من دفتر أبو عمر: نصائح من القلب للشيفرة النظيفة
على مدار سنوات عملي، تعلمت بعض الدروس التي أحب أن أشاركها معكم:
- لا تؤجل عمل اليوم إلى الغد: إذا رأيت رقمًا سحريًا أثناء كتابة الكود، لا تقل “سأعود إليه لاحقًا”. عرّفه كثابت أو ضمن Enum على الفور. هذا سيوفر عليك وعلى فريقك ساعات من الصداع في المستقبل.
- فكّر في زميلك المستقبلي (الذي قد يكون أنت!): اكتب شيفرة يمكن فهمها بعد ستة أشهر من الآن. أنت المستقبلي هو شخص آخر، ذاكرته ليست مثالية، وسيشكرك على وضوحك اليوم.
- استخدم أدوات تحليل الكود (Linters): هناك أدوات مثل ESLint (لـ JavaScript) أو StyleCop (لـ C#) يمكن إعدادها لتنبيهك تلقائيًا عند استخدامك لأرقام سحرية. اجعلها جزءًا من سير عملك.
- ليس كل رقم هو رقم سحري: يجب استخدام المنطق. أرقام مثل 0 أو 1 في تهيئة حلقات التكرار (
for i = 0) أو -1 كقيمة للإشارة إلى “لم يتم العثور عليه” في بعض الدوال القياسية، هي متعارف عليها وليست سحرية. الرقم السحري هو الرقم الذي يمثل مفهومًا خاصًا بالبزنس (مثل حالة الطلب، نوع المستخدم، …الخ).
الخلاصة: شيفرة نظيفة، حياة أسهل ✅
التخلص من الأرقام السحرية ليس مجرد ترف أو هوس بـ “الكود النظيف”. إنه استثمار مباشر في قابلية الصيانة، وتقليل الأخطاء، وزيادة سرعة التطوير على المدى الطويل. هو الفرق بين بناء بيت أساسه متين وواضح، وبناء بيت على أرض مليئة بالألغام قد تنفجر في أي لحظة.
ابدأ اليوم. افتح مشروعك الحالي، ابحث عن تلك الأرقام الغامضة، وأعطها أسماءً ذات معنى باستخدام الثوابت أو التعدادات. قد يبدو الأمر مجهودًا إضافيًا في البداية، ولكنه مجهود سيوفر عليك وعلى فريقك أضعافًا مضاعفة من الوقت والجهد في المستقبل.
أتمنى لكم شيفرة نظيفة وخالية من الألغام. والله يعطيكم العافية.