Introducción: ¿por qué tu revisión se convierte en un campo de batalla?
\n\nEnviaste un Merge Request. Todo parece limpio, la lógica es correcta, el código compila. Una hora después llega la respuesta del senior: «-10 puntos para Gryffindor» y 35 comentarios en 200 líneas de código. ¿Te suena?
\n\nLa revisión de código en C++ no es solo una verificación de sintaxis. Es un arte que separa a los juniors de los seniors. C++ le da al desarrollador un enorme poder sobre la memoria y el rendimiento, pero un gran poder conlleva una gran responsabilidad por comportamiento indefinido y fugas de recursos.
\n\nEn este artículo, nos meteremos en la cabeza de un revisor experimentado. He recopilado observaciones reales que los seniors dejan en las code reviews de proyectos en producción. Si escribes en C++ y quieres que tus PR sean aceptados a la primera, lee con atención. Y si eres team lead, usa esta lista de verificación para la incorporación de nuevos miembros.
\n\n1. Dispararse en el pie: gestión manual de memoria
\n\nLa discusión más común en las revisiones es "¿por qué no usas punteros inteligentes?". Los seniors odian el new y delete directos en C++ moderno (C++11 en adelante).
Ejemplo incorrecto
\nclass DataProcessor {\n int* buffer;\n size_t size;\npublic:\n DataProcessor(size_t s) : size(s) {\n buffer = new int[s]; // ¿Quién va a eliminar esto?\n }\n\n ~DataProcessor() {\n delete[] buffer; // ¿Y si hay una excepción en el constructor?\n }\n};\n\nComentario del senior: "Violación de RAII. Si el constructor lanza una excepción después de asignar memoria, el destructor no se llamará. Fuga de memoria. Usa std::vector o std::unique_ptr."
Ejemplo correcto
\nclass DataProcessor {\n std::vector<int> buffer;\npublic:\n explicit DataProcessor(size_t s) : buffer(s) {}\n};\n\nA los seniors les encanta RAII (Resource Acquisition Is Initialization). Si escribes new en el código, prepárate para explicar por qué std::make_unique o std::vector no te sirven. En el 95% de los casos, sí te sirven.
2. Const-correctness: pónganse una «coma»
\n\nUn const omitido es una bandera roja para cualquier desarrollador senior de C++. No es solo estilo, es un contrato con el lector del código y el compilador.
Lo que molesta en la revisión
\nstd::string getName() { // ¿Por qué no const?\n return name;\n}\n\nvoid printData(std::vector<int>& data) { // ¿Copia? ¿Referencia? ¿Modifica?\n for (auto& item : data) {\n std::cout << item;\n }\n}\n\nComentario del senior: "El método getName() no modifica el objeto — hazlo const. El parámetro data no se modifica — pásalo como const std::vector<int>&. De lo contrario, el código que lo llama no sabe si vas a modificar sus datos o no."
Lo corregimos
\nstd::string getName() const { // método const\n return name;\n}\n\nvoid printData(const std::vector<int>& data) { // referencia const\n for (const auto& item : data) {\n std::cout << item;\n }\n}\n\nLa regla es simple: si un método no cambia el estado del objeto — const. Si una función no modifica un argumento — const T&. Const correctness en C++ no es una opción, es un estándar.
3. Move semantics: copiar no es nuestra religión
\n\nEl C++ moderno se trata de mover, no de copiar. Los seniors detectan de inmediato dónde creas copias innecesarias y matas el rendimiento.
\n\nPatrón incorrecto
\nstd::vector<int> processData(std::vector<int> data) { // Copia en la entrada\n // ... mucha lógica ...\n return data; // ¿Copia en la salida? ¿O NRVO?\n}\n\nvoid caller() {\n std::vector<int> bigData = {1, 2, 3, ... 1000000};\n auto result = processData(bigData); // ¡Otra copia!\n}\n\nComent