Por qué el code review no es sobre críticas, sino sobre calidad
\nImagina: abres un pull request de un colega. Hay 500 líneas de código, huele a copia y pega, y en un lugar alguien olvidó cerrar un recurso. Tu tarea no es solo encontrar errores, sino mejorar el código. El code review en Java no es una supervisión policial, sino una creación conjunta. Un buen revisor salva al equipo de la deuda técnica; uno malo desmotiva o pasa por alto vulnerabilidades.
\nEn este artículo te contaré qué verificar en un code review en Java para no perder tiempo en nimiedades, pero sin pasar por alto lo crítico. Obtendrás una lista de verificación lista para usar y ejemplos de la práctica.
\n\nArquitectura y diseño: mirando la raíz
\nAntes de sumergirte en los detalles, evalúa el panorama general. Una mala arquitectura no se cura con formato.
\nViolación de SOLID
\nVerifica si la clase se ha convertido en un "objeto divino". Ejemplo de violación del Principio de Responsabilidad Única:
\npublic class OrderService {\n public void createOrder(Order order) {\n // validación\n // cálculo de descuento\n // envío de email\n // registro en log\n // guardado en BD\n }\n}\n\nAquí hay cinco razones para cambiar. Lo correcto es extraer el registro en log y el envío a servicios separados.
\nComplejidad excesiva
\nA veces el código resuelve un problema simple de manera compleja. Pregúntate: "¿Se puede hacer más simple?" Si es así, escríbelo en la revisión. Recuerda el principio KISS.
\n\nSeguridad: agujeros que no se deben pasar por alto
\nLa revisión de código Java debe incluir verificación de seguridad. Incluso si el proyecto es interno.
\nInyección SQL
\nLa concatenación de cadenas es mala:
\n// MALO\nString query = "SELECT * FROM users WHERE name = '" + userName + "'";\nStatement stmt = connection.createStatement();\nResultSet rs = stmt.executeQuery(query);\n\nLo correcto es usar PreparedStatement:
// BUENO\nString query = "SELECT * FROM users WHERE name = ?";\nPreparedStatement pstmt = connection.prepareStatement(query);\npstmt.setString(1, userName);\nResultSet rs = pstmt.executeQuery();\n\nFuga de datos sensibles
\nVerifica que no se estén registrando contraseñas, tokens o números de tarjeta. Usa @ToString.Exclude en Lombok o una exclusión explícita.
Comparación insegura de objetos
\n// MALO (comparación de referencias)\nif (password == storedPassword) { ... }\n// BUENO\nif (password.equals(storedPassword)) { ... }\n\n\nRendimiento: cuellos de botella
\nNo optimices prematuramente, pero tampoco pases por alto las ralentizaciones obvias.
\nOperaciones innecesarias en bucles
\n// MALO: se llama a length() cada vez\nfor (int i = 0; i < list.size(); i++) { ... }\n// BUENO\nint size = list.size();\nfor (int i = 0; i < size; i++) { ... }\n\nUso de Stream para tareas simples
\nA veces un bucle normal se lee mejor y funciona más rápido:
\n// Excesivo\nlist.stream().filter(x -> x > 5).collect(Collectors.toList());\n// Simple\nList<Integer> result = new ArrayList<>();\nfor (int x : list) {\n if (x > 5) result.add(x);\n}\n\nFuga de memoria
\nVerifica si olvidaron cerrar un InputStream, Connection o Session. Usa try-with-resources:
try (Connection conn = getConnection();\n PreparedStatement stmt = conn.prepareStatement(sql)) {\n // trabajo\n} catch (SQLException e) {\n log.error("Error de BD", e);\n}\n\n\nLegibilidad y estilo: el código como un libro
\nLa lista de verificación de code review incluye obligatoriamente la legibilidad. El código se escribe una vez, se lee decenas de veces.
\nNúmeros y cadenas mágicas
\n// MALO\nif (status == 3) { ... }\n// BUENO\nprivate static final int STATUS_COMPLETED = 3;\nif (status == STATUS_COMPLETED) { ... }\n\n