يا أهلاً وسهلاً فيكم يا جماعة. اسمي أبو عمر، مبرمج فلسطيني قضيت سنين عمري بين الأكواد والمشاريع، وشفت فيها اللي بفرح القلب واللي بجيب الضغط. اليوم بدي أحكيلكم قصة صارت معي ومع فريقي، قصة عن مشروع كان على وشك ينهار، مش بسبب خطأ في المنطق أو مشكلة عند العميل، لا… بل بسبب الكود نفسه.
كنا شغالين على نظام كبير ومعقد، نظام ورثناه عن فريق ثاني. في البداية، كانت الأمور ماشية. لكن مع كل طلب تعديل جديد، أو محاولة لإضافة ميزة، كانت “تولّع معنا”. كنا نصلّح شغلة، تخرب شغلتين بمكان ثاني ما إلهم علاقة. صار الواحد فينا قبل ما يلمس أي سطر كود، يقرأ المعوذات ويحكي “يا رب استر”. وصلنا لمرحلة سمّيناها بين بعض “جحيم أخشى أن ألمسه”. الكود صار مثل حقل ألغام، هش لدرجة مخيفة، ورائحته البرمجية، يا لطيف، كانت لا تُطاق.
في يوم من أيام اليأس، وإحنا مجتمعين وغارقين في المشاكل، واحد من الشباب الجداد حكى جملة بسيطة: “يا جماعة، المشكلة مش في البقز (Bugs)، المشكلة في ريحة الكود”. وقتها، ضحكنا، بس الجملة هاي علقت في بالي. قضينا الليلة وأنا أبحث وأقرأ عن مصطلح “روائح الكود” أو “Code Smells”. واكتشفت إنه إحنا ما كنا بنعاني من أخطاء برمجية، بل من أعراض لمشاكل أعمق بكثير في تصميم وهيكلة الكود. ومن هنا بدأت رحلة إنقاذ مشروعنا.
ما هي “روائح الكود” (Code Smells)؟
خليني أبسطلك اياها. “رائحة الكود” مش خطأ برمجي (Bug). الكود ممكن يكون شغال 100% ويؤدي وظيفته، لكن طريقة كتابته فيها “إشي غلط”. هي علامة، مؤشر، أو عرض لمشكلة تصميمية أعمق في الكود. تماماً مثل رائحة الطعام في الثلاجة، قد لا يعني أن الطعام فاسد تماماً بعد، ولكنه تحذير واضح بأن شيئاً ما على وشك أن يفسد.
المصطلح هذا أبدعه المبرمج الأسطوري “مارتن فاولر”، وهو يقول إن هذه الروائح هي إشارات ترشدك إلى متى وأين يجب أن تقوم بعملية “إعادة الهيكلة” (Refactoring). تجاهلها يعني أنك تبني بيتاً على أساسات هشة، مسألة وقت فقط قبل أن ينهار كل شيء فوق رأسك.
أشهر روائح الكود وكيفية اصطيادها (مع أمثلة)
على مر السنين، تعلمت أصطاد هاي الروائح من على بعد ميل. خلونا نستعرض أشهرها وكيف نتعامل معها.
1. الكود المكرر (Duplicated Code)
هاي أشهر رائحة وأكثرها انتشاراً. لما تلاقي نفسك بتنسخ وتلصق نفس قطعة الكود في مكانين أو أكثر، لازم يضوي عندك ضوء أحمر. هذا انتهاك لمبدأ “لا تكرر نفسك” (Don’t Repeat Yourself – DRY).
المشكلة: لو احتجت تعدل المنطق هذا، لازم تتذكر تعدله في كل الأماكن اللي نسخته فيها، ولو نسيت مكان واحد… يا ويلك!
مثال (قبل):
// JavaScript
function calculateOrderTotal(order) {
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
// Add tax
total = total * 1.15;
return total;
}
function generateInvoice(order) {
let invoiceTotal = 0;
for (const item of order.items) {
invoiceTotal += item.price * item.quantity;
}
// Add tax
invoiceTotal = invoiceTotal * 1.15;
console.log(`Invoice Total: ${invoiceTotal}`);
}
لاحظت التكرار؟ منطق حساب المجموع مع الضريبة مكرر بالحرف.
مثال (بعد إعادة الهيكلة):
// JavaScript
function calculateSubtotal(items) {
let total = 0;
for (const item of items) {
total += item.price * item.quantity;
}
return total;
}
function applyTax(total) {
return total * 1.15;
}
function calculateOrderTotal(order) {
const subtotal = calculateSubtotal(order.items);
return applyTax(subtotal);
}
function generateInvoice(order) {
const invoiceTotal = calculateOrderTotal(order);
console.log(`Invoice Total: ${invoiceTotal}`);
}
الآن، لو تغيرت نسبة الضريبة، بنعدلها في مكان واحد فقط. شغل نظيف ومرتب.
2. الدوال الطويلة (Long Methods)
لما تشوف دالة (Function) أطول من شاشة العرض عندك، هاي رائحة كريهة جداً. الدالة الجيدة يجب أن تفعل شيئاً واحداً فقط، وتفعله بشكل جيد. الدوال الطويلة عادةً ما تفعل مليون شغلة.
المشكلة: صعبة الفهم، صعبة التعديل، وصعبة الاختبار.
نصيحة من أبو عمر: إذا احتجت تضيف تعليق داخل دالة عشان تشرح قسم منها، فهذا غالباً مؤشر أن هذا القسم لازم يصير دالة منفصلة باسم واضح.
مثال (قبل):
// Python
def process_user_data(user_id):
# 1. Fetch user from database
print(f"Fetching user {user_id}...")
user = db.get_user(user_id)
if not user:
return {"error": "User not found"}
# 2. Validate user email
print(f"Validating email for {user.name}...")
if "@" not in user.email:
user.is_valid = False
else:
user.is_valid = True
# 3. Check if user is active and has recent activity
print(f"Checking activity for {user.name}...")
last_login_delta = datetime.now() - user.last_login
if user.is_active and last_login_delta.days < 30:
user.status = "active_recent"
else:
user.status = "inactive"
# 4. Send notification
print(f"Sending notification to {user.name}...")
email_service.send(user.email, "Profile Processed", f"Your profile status is {user.status}")
return user
مثال (بعد إعادة الهيكلة):
// Python
def validate_email(user):
user.is_valid = "@" in user.email
def determine_user_status(user):
last_login_delta = datetime.now() - user.last_login
if user.is_active and last_login_delta.days < 30:
user.status = "active_recent"
else:
user.status = "inactive"
def send_status_notification(user):
email_service.send(user.email, "Profile Processed", f"Your profile status is {user.status}")
def process_user_data(user_id):
user = db.get_user(user_id)
if not user:
return {"error": "User not found"}
validate_email(user)
determine_user_status(user)
send_status_notification(user)
return user
شوف كيف صارت الدالة الرئيسية سهلة القراءة، عبارة عن خطوات واضحة. كل خطوة معقدة صارت في دالة خاصة فيها.
3. الفئات الضخمة (Large Class / God Class)
هذه الفئة (Class) التي تعرف كل شيء وتفعل كل شيء. تجد فيها منطق التعامل مع قاعدة البيانات، ومنطق التحقق من المدخلات، ومنطق إرسال الإيميلات، وإدارة المستخدمين… كارثة! هذا انتهاك مباشر لـ “مبدأ المسؤولية الواحدة” (Single Responsibility Principle).
المشكلة: أي تغيير بسيط في أي جزء من النظام قد يتطلب تعديل هذه الفئة، مما يجعلها هشة جداً ومرعبة.
الحل: قسّم! انظر إلى المسؤوليات المختلفة التي تقوم بها الفئة، وحاول فصل كل مسؤولية في فئة جديدة خاصة بها. مثلاً، فئة Order الضخمة يمكن تقسيمها إلى OrderProcessor, OrderValidator, OrderRepository.
4. الأسماء الغامضة (Mysterious Names)
لما تشوف متغيرات بأسماء مثل d, list1, temp, x… هذه جريمة برمجية. الكود يُقرأ أكثر مما يُكتب، فاجعله قابلاً للقراءة.
- سيء:
let d; // elapsed time in days - جيد:
let elapsedTimeInDays;
- سيء:
function proc(data) { ... } - جيد:
function processCustomerData(customerList) { ... }
نصيحة من أبو عمر: لا تخف من الأسماء الطويلة والواصفة. اسم المتغير أو الدالة يجب أن يخبرك بقصة كاملة عن وظيفته.
أدوات عملية لاصطياد هذه الروائح
صحيح أن الخبرة هي أفضل أداة، لكن التكنولوجيا يمكن أن تساعدنا. هناك أدوات تحليل ثابت للكود (Static Analysis Tools) تعمل كأنها “أنف إلكتروني” يشم هذه الروائح تلقائياً ويحذرك منها.
- لعالم JavaScript/TypeScript: لا غنى عن ESLint مع إضافات مثل SonarJS.
- لعالم Python: أدوات مثل Pylint و Flake8 ممتازة.
- لعالم Java: أدوات مثل Checkstyle و SonarQube هي المعيار.
- لعالم C#: إضافة ReSharper من JetBrains أو Roslyn Analyzers المدمجة في Visual Studio تقوم بعمل رائع.
هذه الأدوات يمكن دمجها مع محرر الكود الخاص بك لتعطيك ملاحظات فورية وأنت تكتب، وهذا يمنع تكون الروائح من الأساس.
إعادة الهيكلة (Refactoring): الجراحة التجميلية للكود
اكتشاف الرائحة هو نصف المعركة، النصف الآخر هو “إعادة الهيكلة” لتنظيفها. إعادة الهيكلة هي عملية تغيير الهيكل الداخلي للكود دون تغيير سلوكه الخارجي. الهدف هو تحسين التصميم، زيادة القابلية للقراءة، وتقليل التعقيد.
وهنا تأتي أهم نصيحة في المقال كله:
لا تقم بإعادة الهيكلة بدون شبكة أمان!
ما هي شبكة الأمان؟ هي مجموعة شاملة من الاختبارات الآلية (Automated Tests)، مثل اختبارات الوحدة (Unit Tests). هذه الاختبارات تضمن أن تغييراتك لم “تكسر” أي شيء في النظام.
خطوات عملية لإعادة الهيكلة بأمان:
- حدد رائحة الكود التي تريد التخلص منها.
- تأكد من وجود اختبارات تغطي السلوك الحالي للكود. إذا لم تكن موجودة، اكتبها أولاً!
- شغّل الاختبارات وتأكد من أنها كلها ناجحة (لونها أخضر).
- قم بعملية إعادة الهيكلة (مثلاً، استخراج دالة جديدة).
- شغّل الاختبارات مرة أخرى. يجب أن تبقى كلها ناجحة. إذا فشل أي اختبار، فأنت تعرف أنك أفسدت شيئًا ما. تراجع عن تغييراتك وحاول مرة أخرى.
- بمجرد نجاح الاختبارات، تكون قد أنجزت المهمة بنجاح.
هذه الدورة (عدّل -> اختبر -> كرّر) هي سر الحفاظ على كود صحي وقوي على المدى الطويل.
الخلاصة: شم الكود قبل أن تشم رائحة الفشل! 👃
تجربتنا مع ذلك المشروع كانت قاسية، لكنها علمتنا درساً لن ننساه. الكود النظيف ليس رفاهية، بل هو ضرورة حتمية لنجاح أي مشروع برمجي واستمراريته. “روائح الكود” هي بمثابة إنذار مبكر، وتجاهلها هو وصفة لكارثة محققة.
اجعل من عادة “شم” الكود جزءًا من روتينك اليومي. اتبع “قاعدة الفتى الكشاف”: اترك الكود دائمًا أنظف مما وجدته. حتى لو كان تحسينًا بسيطًا، مع الوقت، هذه التحسينات الصغيرة تتراكم لتصنع فرقًا هائلاً.
تذكر دائماً أنك لا تكتب الكود للكمبيوتر فقط، بل تكتبه لنفسك في المستقبل ولزملائك في الفريق. فكن لطيفاً معهم، واكتب كوداً يفتخرون بقراءته وصيانته. يلا يا شباب، شدّوا حيلكم، وخلينا نكتب كود “ريحته حلوة”! 💪