Code Review en Java: qué revisar y cómo dar retroalimentación en la práctica
\n\nCode Review (revisión de código) es una verificación sistemática del código de otros con el objetivo de detectar errores, mejorar la calidad y compartir conocimientos. En el desarrollo en Java, esto es especialmente importante debido al tipado estricto, las particularidades de la concurrencia y los requisitos de rendimiento. Sin embargo, muchos desarrolladores reducen la revisión a una simple comprobación de estilo o, por el contrario, la convierten en una crítica tóxica. En este artículo, analizaremos qué es exactamente lo que se debe revisar en el código Java y cómo dar retroalimentación para que sea útil, no dolorosa.
\n\n1. Qué revisar en el código Java: lista de verificación para el revisor
\n\nPara que la revisión sea efectiva, no solo hay que mirar la sintaxis, sino también la arquitectura, la seguridad y el rendimiento. Estas son las categorías principales que se deben revisar.
\n\n1.1 Arquitectura y diseño
\n- \n
- Principios SOLID: ¿la clase viola el principio de responsabilidad única? ¿La interfaz se está convirtiendo en un "objeto divino"? \n
- Acoplamiento y cohesión: las clases deben tener un acoplamiento débil (loose coupling) y una alta cohesión interna (high cohesion). \n
- Uso de patrones: ¿está justificado el uso de patrones complejos? A veces un simple if-else es mejor que una fábrica con una docena de clases. \n
// Mal: la clase lo hace todo a la vez\npublic class ReportService {\n public void generateReport() { /* generación */ }\n public void sendEmail() { /* envío */ }\n public void saveToDatabase() { /* guardado */ }\n}\n\n// Bien: separación de responsabilidades\npublic class ReportGenerator { ... }\npublic class EmailSender { ... }\npublic class ReportRepository { ... }\n\n\n1.2 Manejo de excepciones y errores
\n- \n
- Supresión de excepciones: no se debe escribir un
catch (Exception e)vacío, ya que oculta errores. \n - Especificidad: capture excepciones específicas, no
ExceptionoThrowable. \n - Registro (logging): las excepciones deben registrarse con suficiente contexto (traza de pila, datos de entrada). \n
// Mal: tragar la excepción\ntry {\n processFile(file);\n} catch (Exception e) {\n // no hacemos nada\n}\n\n// Bien: registro y relanzamiento\ntry {\n processFile(file);\n} catch (IOException e) {\n log.error("Error al procesar el archivo: {}", file, e);\n throw new ProcessingException("No se pudo procesar el archivo", e);\n}\n\n\n1.3 Rendimiento y memoria
\n- \n
- Operaciones redundantes: creación de objetos en bucles, copias innecesarias de cadenas con
new String(). \n - Uso de colecciones: ¿se eligió el tipo correcto (ArrayList vs LinkedList)? ¿Hay sincronizaciones innecesarias? \n
- Stream API: ¿el stream perjudica la legibilidad? A veces un bucle normal es más claro. \n
// Mal: creación de cadena en un bucle\nString result = "";\nfor (String s : list) {\n result += s; // cada vez una nueva cadena\n}\n\n// Bien: StringBuilder\nStringBuilder sb = new StringBuilder();\nfor (String s : list) {\n sb.append(s);\n}\n\n\n1.4 Concurrencia y sincronización
\n- \n
- Uso correcto de synchronized: ¿no se ha bloqueado una sección de código demasiado grande? \n
- Colecciones concurrentes: ¿está usando
ConcurrentHashMapen lugar de una tabla hash con sincronización externa? \n - Riesgo de deadlock: ¿no se están bloqueando los recursos en un orden diferente? \n
// Riesgo de deadlock\nsynchronized (lockA) {\n synchronized (lockB) { ... }\n}\n// En otro hilo:\nsynchronized (lockB) {\n synchronized (lockA) { ... }\n}\n\n\n2. Cómo escribir código para una revisión fácil
\n\nUna buena revisión comienza con un buen código. Y