لماذا مراجعة الكود ليست انتقادًا، بل تتعلق بالجودة
\nتخيل: تفتح طلب سحب (pull request) من زميل. هناك 500 سطر من الكود، ورائحة النسخ واللصق، وفي مكان ما نسي شخص ما إغلاق مورد. مهمتك ليست مجرد العثور على الأخطاء، بل جعل الكود أفضل. مراجعة الكود بلغة جافا ليست رقابة شرطية، بل إبداع مشترك. المراجع الجيد ينقذ الفريق من الديون التقنية، أما السيئ فيثبط الهمم أو يسمح بمرور الثغرات.
\nفي هذه المقالة، سأشرح ما يجب التحقق منه في مراجعة الكود بلغة جافا، حتى لا تضيع الوقت في التفاصيل الصغيرة، ولكن لا تفوت الأمور الحرجة. ستحصل على قائمة تحقق جاهزة وأمثلة من الممارسة.
\n\nالبنية والتصميم: انظر إلى الجذر
\nقبل الغوص في التفاصيل، قيّم الصورة العامة. البنية السيئة لا تُعالج بالتنسيق.
\nانتهاك مبادئ SOLID
\nتحقق مما إذا كانت الفئة قد تحولت إلى "كائن إلهي". مثال على انتهاك مبدأ المسؤولية الفردية:
\npublic class OrderService {\n public void createOrder(Order order) {\n // التحقق\n // حساب الخصم\n // إرسال البريد الإلكتروني\n // تسجيل السجلات\n // الحفظ في قاعدة البيانات\n }\n}\n\nهنا خمسة أسباب للتغيير. الصحيح هو فصل تسجيل السجلات والإرسال إلى خدمات منفصلة.
\nالتعقيد المفرط
\nأحيانًا يحل الكود مشكلة بسيطة بطريقة معقدة. اسأل نفسك: "هل يمكن أن يكون أبسط؟" إذا كانت الإجابة نعم، فاكتب ذلك في المراجعة. تذكر مبدأ KISS.
\n\nالأمان: ثغرات لا يجب تفويتها
\nمراجعة كود جافا يجب أن تتضمن التحقق من الأمان. حتى لو كان المشروع داخليًا.
\nحقن SQL
\nربط السلاسل النصية هو شر:
\n// سَيء\nString query = "SELECT * FROM users WHERE name = '" + userName + "'";\nStatement stmt = connection.createStatement();\nResultSet rs = stmt.executeQuery(query);\n\nالصحيح هو استخدام PreparedStatement:
// جيد\nString query = "SELECT * FROM users WHERE name = ?";\nPreparedStatement pstmt = connection.prepareStatement(query);\npstmt.setString(1, userName);\nResultSet rs = pstmt.executeQuery();\n\nتسريب البيانات الحساسة
\nتحقق من عدم تسجيل كلمات المرور أو الرموز المميزة أو أرقام البطاقات. استخدم @ToString.Exclude في Lombok أو التجاهل الصريح.
مقارنة غير آمنة للكائنات
\n// سَيء (مقارنة المراجع)\nif (password == storedPassword) { ... }\n// جيد\nif (password.equals(storedPassword)) { ... }\n\n\nالأداء: نقاط الاختناق
\nلا تقم بالتحسين المبكر، لكن لا تفوت أيضًا حالات التباطؤ الواضحة.
\nعمليات غير ضرورية في الحلقات
\n// سَيء: يتم استدعاء length() في كل مرة\nfor (int i = 0; i < list.size(); i++) { ... }\n// جيد\nint size = list.size();\nfor (int i = 0; i < size; i++) { ... }\n\nاستخدام Stream للمهام البسيطة
\nأحيانًا تكون الحلقة العادية أسرع في القراءة والأداء:
\n// مفرط\nlist.stream().filter(x -> x > 5).collect(Collectors.toList());\n// بسيط\nList<Integer> result = new ArrayList<>();\nfor (int x : list) {\n if (x > 5) result.add(x);\n}\n\nتسرب الذاكرة
\nتحقق مما إذا تم نسيان إغلاق InputStream أو Connection أو Session. استخدم try-with-resources:
try (Connection conn = getConnection();\n PreparedStatement stmt = conn.prepareStatement(sql)) {\n // العمل\n} catch (SQLException e) {\n log.error("خطأ في قاعدة البيانات", e);\n}\n\n\nقابلية القراءة والأسلوب: الكود ككتاب
\nقائمة التحقق لمراجعة الكود تتضمن بالضرورة قابلية القراءة. الكود يُكتب مرة واحدة، ويُقرأ عشرات المرات.
\nالأرقام والسلاسل السحرية
\n// سَيء\nif (status == 3) { ... }\n// جيد\nprivate static final int STATUS_COMPLETED = 3;\nif (status == STATUS_COMPLETED) { ... }\n\n