Más allá de “aceptar y pegar”: cómo revisar (de verdad) el código que te genera la IA

Más allá de “aceptar y pegar”: cómo revisar (de verdad) el código que te genera la IA

Un flujo concreto para revisar código generado por IA con pruebas, análisis estático y preguntas útiles en el PR.
Compartir:

La IA puede generar código que parece listo para entrar en un PR. El problema empieza cuando esa apariencia sustituye a la revisión.

En un estudio de 438 entrevistas técnicas con programación asistida por IA, el autor observó que los participantes aceptaban las propuestas de la herramienta sin cuestionarlas el 93% de las veces (fuente). Es un dato sobre esas entrevistas, no una tasa de errores del código generado. Sirve para señalar un hábito: aceptar antes de comprobar.

El problema: confianza automática en la IA

Un bloque con nombres razonables y estructura limpia puede pasar por correcto aunque ignore un caso límite. La familiaridad del autocompletado y la presión por terminar una tarea facilitan que se lea menos de lo que se debería.

Ahí se cuelan errores de límites o concurrencia y fallos en patrones que después se repiten: autenticación, acceso a datos o validación de entradas. El código generado necesita la misma revisión que cualquier otra contribución, con una dificultad añadida: quien lo produjo no conoce todas las decisiones del proyecto.

Revisa contra el contexto del proyecto

En este artículo sobre el uso de Copilot por tres Java Champions aparece una idea útil para el trabajo diario: la herramienta puede ahorrar tiempo, pero el criterio sobre el resultado sigue siendo del desarrollador.

Antes de conservar una sugerencia, comprueba si encaja con la arquitectura, los requisitos no funcionales y las convenciones del repositorio. Puede que la solución ya exista en un servicio interno o que una migración antigua explique por qué se evita cierta API. Si el código nuevo ignora ese contexto, corrígelo o descártalo.

Señales rojas en código generado por IA

No todos los cambios requieren la misma profundidad de revisión. Estas señales indican dónde merece la pena detenerse.

APIs y utilidades que no encajan

  • Reimplementa lógica que ya resuelve un servicio o una utilidad del proyecto.
  • Usa una API cuya existencia no has comprobado en la versión instalada.
  • Añade imports que no utiliza.

Por ejemplo, esta función de login debería detener la revisión antes de llegar a los tests:

// Generado por IA
import bcrypt from 'bcryptjs';

export async function loginUser(email: string, password: string) {
  const user = await db.user.findUnique({ where: { email } });
  if (!user) throw new Error('User not found');

  const passwordMatches = await bcrypt.compare(password, user.passwordHash);
  if (!passwordMatches) throw new Error('Invalid credentials');

  const token = jwt.sign({ userId: user.id }, 'secret', { expiresIn: '1h' });
  return { token };
}

El secreto está en el código. Además, la función puede saltarse el servicio de autenticación del proyecto y devuelve errores distintos según exista o no el usuario. Antes de aceptarla, habría que revisar cómo se gestionan las credenciales, los errores y la configuración en esa aplicación. No añadir logs por rutina: primero hay que decidir qué se puede registrar sin exponer datos sensibles.

Límites y estados omitidos

Busca accesos sin comprobar null o undefined, comparaciones < y <= que cambien un límite, y estados que la función no contempla, como pending o cancelled. En una integración externa, revisa también qué ocurre ante un timeout o una respuesta de error.

Para cada cambio, prueba al menos el valor mínimo, el máximo, una colección vacía y el fallo de una dependencia, si esos casos aplican. Es más útil que leer otra vez el camino feliz.

Seguridad y datos sensibles

Detén la revisión si aparecen secretos en claro, entradas externas sin validar, consultas construidas con cadenas o logs que incluyan tokens y contraseñas. Revisa también los permisos: que una petición sea válida no significa que quien la envía pueda acceder al recurso.

Si el cambio toca autenticación, pagos, datos personales o autorización, pide revisión de alguien que conozca esa parte del sistema. Un linter no puede decidir si una persona tiene permiso para ver un pedido.

Complejidad sin motivo

Los if/else anidados, los patrones de diseño añadidos para un caso sencillo o la concurrencia que nadie pidió aumentan el coste de entender el cambio. Pide una justificación concreta. Si no la hay, simplifica.

Cinco pasos antes de hacer commit

1. Define el comportamiento esperado

Escribe qué debe hacer el cambio y qué queda fuera. Puedes pedir a la IA que explique la función paso a paso, pero contrasta esa explicación con el código: una respuesta convincente no demuestra que la implementación cumpla el requisito. Si no puedes describir el comportamiento, todavía no está lista para entrar.

2. Comprueba dónde encaja

Revisa si el cambio está en la capa correcta y si reutiliza servicios, repositorios o helpers existentes. A veces la mejor corrección es borrar parte del bloque generado y llamar a una función que ya está probada.

3. Ejecuta linter, formateador y comprobación de tipos

Hazlo antes de preparar el PR. ESLint, RuboCop o Pylint pueden señalar imports y variables sin usar; el comprobador de tipos detecta otras incompatibilidades. El formateador evita discutir espacios y saltos de línea. Ninguna de estas herramientas valida la lógica de negocio, pero deja menos ruido para revisarla.

4. Prueba el comportamiento y un borde

Un test del caso principal y otro del límite que más preocupa son un buen punto de partida. El valor esperado debe salir del requisito, no de ejecutar la implementación generada y copiar su resultado.

describe('calculateDiscount', () => {
  it('aplica el descuento máximo configurado', () => {
    const result = calculateDiscount({ amount: 1000, userTier: 'gold' });
    expect(result.total).toBe( /* valor esperado */ );
  });

  it('no aplica descuento a importes negativos', () => {
    expect(() => calculateDiscount({ amount: -10, userTier: 'gold' }))
      .toThrow();
  });
});

El primer test es una plantilla: falta sustituir el comentario por el importe que establezcan las reglas de descuento. Si la IA genera los tests, revísalos también. Puede escribir pruebas que confirmen el mismo error que introdujo en la función.

5. Revisa entradas, secretos y permisos

Para código que recibe datos externos o toca persistencia, comprueba dónde se valida la entrada, qué información se registra, cómo se obtienen los secretos y quién puede ejecutar cada operación. Puedes pedir a un modelo que busque posibles fallos de seguridad, pero verifica cada hallazgo antes de modificar el código.

Pruebas, análisis estático y modelos: qué aporta cada capa

El compilador y el linter encuentran problemas mecánicos. Los tests comprueban los casos que has descrito. Una herramienta de análisis estático puede detectar patrones peligrosos. Otro modelo puede proponer casos borde que no habías considerado. Ninguna capa sustituye la revisión del comportamiento y del contexto del proyecto.

Para usar un modelo como revisor, dale una tarea delimitada: «Señala casos borde que este cambio no cubre» o «Busca rutas en las que un usuario pueda acceder a un recurso ajeno». Si tienes acceso a otra herramienta, puedes pedirle una revisión independiente. Cambiar de modelo no garantiza independencia ni acierto; trata sus observaciones como hipótesis que hay que comprobar.

En CI, ejecuta las pruebas y el análisis estático que ya use el equipo. Si acordáis umbrales de cobertura o reglas para hallazgos de seguridad en módulos sensibles, haced que bloqueen el PR. Un pipeline verde significa que han pasado esos controles, no que el cambio sea correcto.

Llevarlo al code review

En la descripción del PR, indica qué partes se generaron con IA, qué adaptaste y qué verificaste. Una nota junto al cambio es más útil que llenar el código de comentarios permanentes como AI-SUGGESTED.

La revisión debería pedir explicaciones sobre el resultado, no centrarse en el estilo que ya controla el formateador: qué pasa con un estado cancelado, por qué se consulta esa tabla o dónde se comprueba el permiso. Si la persona autora no puede responder, toca revisar el cambio antes de aprobarlo.

Conviene dejar por escrito dos límites del equipo: qué datos no pueden enviarse a un modelo y qué áreas, como permisos, pagos o cifrado, requieren una revisión especializada. Si empezáis a usar IA con frecuencia, registrad los tipos de errores que aparecen en los PR y en producción para ajustar la checklist. No hace falta atribuir cada fallo a la herramienta; interesa saber qué controles no lo detectaron.

Una checklist que cabe en el PR

Añade a docs/ai-checklist.md o a la plantilla de PR solo las comprobaciones que el equipo vaya a utilizar:

  • ¿Reutiliza servicios y utilidades existentes cuando corresponde?
  • ¿Hay pruebas del comportamiento principal y de los bordes relevantes?
  • ¿Pasan el linter, la comprobación de tipos y los controles de CI aplicables?
  • ¿Se han revisado entradas, secretos y permisos si el cambio los toca?
  • ¿Puede la persona autora explicar qué hace el código y qué casos deja fuera?

Para probar el flujo sin añadir proceso a todo el repositorio, empezad por el próximo PR que incluya código generado en una zona sensible. Anotad qué detectó cada control y ajustad la checklist a partir de ese caso.

Compartir:

Artículos relacionados