Menu

Prompts para revisar código con IA

Un buen prompt de revisión de código dice qué buscar, pide una gravedad para cada hallazgo, exige una entrada concreta que lo provoque y un diff, y le dice al modelo que se salte el estilo. Luego revisas cada hallazgo, porque algunos estarán mal.

Puedes editar cada prompt de esta página y abrirlo después en ChatGPT, Claude u otra app de IA.

Un prompt de revisión de código debe decirle al modelo qué buscar, qué gravedad tiene cada hallazgo y qué prueba aportar. "Revisa este código" recibe una lista amable de sugerencias en la que un agujero de seguridad queda entre un consejo sobre docstrings y una preferencia de nombres. Un prompt de revisión con una lista de comprobación, niveles de gravedad y una petición de diffs recibe un informe con el que puedes actuar, hallazgo por hallazgo.

De "revisa esto" a una lista de comprobación con gravedad

El código de las dos pestañas tiene dos errores reales. Compara cómo los presenta cada respuesta.

¿Puedes revisar este código? def get_user(conn, username): cur = conn.cursor() cur.execute(f"SELECT id, email FROM users WHERE name = '{username}'") row = cur.fetchone() return {"id": row[0], "email": row[1]}
Try it
Example replyReplies vary between models and runs.

En general, la función es corta y fácil de seguir. Algunas sugerencias:

  • Añade anotaciones de tipo, por ejemplo username: str, para que la firma sea más clara.
  • Añade un docstring que explique qué devuelve la función.
  • Usa consultas parametrizadas. Construir SQL con f-strings puede provocar inyección SQL.
  • Controla los usuarios inexistentes. fetchone() puede devolver None.
  • Nombres: fetch_user quizá describiría mejor el acceso a la base de datos.

La primera respuesta encontró los dos errores, y ese es el problema de juzgar una revisión por lo que menciona. La inyección aparece en tercer lugar en una lista de cinco, suavizada con un "puede provocar", sin solución, al lado de un consejo sobre docstrings. Quien la leyera por encima añadiría anotaciones de tipo y pasaría a otra cosa.

El segundo prompt cambió cuatro cosas:

  • El alcance. "Solo errores, seguridad y manejo de errores. Sáltate el estilo" elimina el ruido. El estilo es cosa de un linter y un formateador, que son más rápidos y nunca se contradicen.
  • La gravedad. Obliga al modelo a ordenar, y una lista ordenada te dice por dónde empezar.
  • Una entrada que lo provoca. "x' OR '1'='1" convierte un riesgo vago en una demostración que puedes ejecutar. También es tu mejor filtro contra los falsos positivos, como muestra la última sección.
  • Diffs. Un diff pequeño por hallazgo se puede aplicar o rechazar por separado. Una función reescrita mezcla cada corrección con cambios que nadie pidió.

"Si no hay nada en un nivel de gravedad, no inventes nada" importa más de lo que parece. Cuando se le pide una revisión, un modelo tiende a producir hallazgos aunque haya poco que encontrar, y los más débiles inflan la lista. Un permiso explícito para no informar nada en un nivel reduce ese relleno.

El contexto que necesita el revisor

Un revisor humano sabe para qué sirve el código y de dónde vienen sus entradas. El modelo solo sabe lo que pegas. "username llega directamente de un formulario de inicio de sesión" es la frase que convierte la inyección SQL en un hallazgo de gravedad alta en lugar de uno teórico. Contexto útil para una revisión:

  • De dónde vienen las entradas: usuarios, otro servicio interno, un archivo de configuración que controlas tú.
  • Qué llama al código y qué espera recibir.
  • El entorno de ejecución: versión del lenguaje, framework, driver de la base de datos. La corrección de la inyección de arriba usa ? porque sqlite3 usa ese marcador; otros drivers usan %s.
  • Lo que más te importa en este cambio: la corrección, la seguridad, el rendimiento o las tres cosas.

Un prompt de revisión reutilizable

Este bloque convierte la lista de comprobación en una plantilla. Cambia el lenguaje, el enfoque y el código, y conserva las reglas. Desactivar la parte de restricciones es una forma rápida de ver cuánto ruido vuelve sin ella.

Plantilla de revisión de código
Fill in
Parts
Eres un revisor sénior de JavaScript. Informas de problemas reales y de nada más.
Revisa el código de abajo, centrándote en los errores y el manejo de errores.
Esto se ejecuta en el navegador. `ids` es un array de ids numéricos que vienen de nuestra propia API.
async function loadAll(ids) { const results = []; ids.forEach(async (id) => { const res = await fetch(`/api/items/${id}`); results.push(await res.json()); }); return results; }
Para cada hallazgo: gravedad (alta, media o baja), la línea, una entrada o situación que lo provoque y un diff mínimo. Los más graves primero.
Nada de comentarios sobre estilo, nombres o formato. Si no estás seguro de que un hallazgo sea real, dilo y di qué lo confirmaría. Si no encuentras nada, di "Sin hallazgos".
Try it
Example replyReplies vary between models and runs.

1. Alta: la función devuelve antes de que termine ninguna petición (línea 3) forEach no espera a los callbacks asíncronos. loadAll devuelve de inmediato el array results vacío, y los push ocurren después. Cualquiera que la llame recibe [].

2. Media: las peticiones fallidas se tratan como datos (línea 5) Una respuesta 404 o 500 no es una excepción en fetch. Un cuerpo de error en JSON se analiza y se añade como si fuera un elemento, y un cuerpo que no es JSON hace que res.json() lance una excepción dentro de un callback al que nadie espera.

Solución para los dos, que además mantiene los resultados en el mismo orden que ids:

 async function loadAll(ids) {
-  const results = [];
-  ids.forEach(async (id) => {
-    const res = await fetch(`/api/items/${id}`);
-    results.push(await res.json());
-  });
-  return results;
+  return Promise.all(
+    ids.map(async (id) => {
+      const res = await fetch(`/api/items/${id}`);
+      if (!res.ok) throw new Error(`Item ${id}: HTTP ${res.status}`);
+      return res.json();
+    })
+  );
 }

La línea del rol fija un estándar ("problemas reales y nada más"), que es donde más ayuda el prompting de rol. Si un programa lee la revisión, por ejemplo un script que publica los hallazgos como comentarios en un pull request, pide JSON con campos fijos en lugar de prosa; salida estructurada muestra cómo.

Gestionar los falsos positivos

Algunos hallazgos estarán mal. El modelo no puede ver al llamador que valida la entrada, la restricción de la base de datos que hace imposible un caso ni el motivo por el que una línea de aspecto extraño es intencionada. También puede equivocarse sobre cómo se comporta una biblioteca; alucinaciones de la IA explica por qué una afirmación segura no es una afirmación comprobada.

Prueba cada hallazgo con su entrada. Si no puedes producir una entrada que llegue al problema en tu sistema, rebátelo con el contexto que faltaba en lugar de aceptar la corrección:

Rebatir un hallazgo
Señalaste esta función porque `cents` podría ser undefined o una cadena: function formatPrice(cents) { return `$${(cents / 100).toFixed(2)}`; } Solo se llama desde `renderCart`, que sale antes de tiempo salvo que `Number.isInteger(cents)` sea verdadero. ¿Sigue siendo un problema? Si no lo es, retira el hallazgo.
Try it
Example replyReplies vary between models and runs.

Con esa comprobación, no: formatPrice solo recibe enteros, así que el hallazgo no aplica y lo retiro.

Una cosa que la comprobación no cubre: los enteros negativos pasan Number.isInteger, y formatPrice(-500) devuelve "$-5.00". Si a esta función pueden llegar reembolsos o descuentos, quizá prefieras "-$5.00". Si no pueden llegar, no hay nada que cambiar.

Un modelo al que se le muestra una evidencia nueva debe retirar el hallazgo o explicar por qué se mantiene teniendo en cuenta esa evidencia. Las dos cosas son útiles. Un modelo que simplemente da la razón a cada objeción no está revisando, así que juzga la respuesta por sus razones, no por si te dio la razón. Para el código que escribió el propio modelo, haz la revisión en una conversación nueva, para que no se lea a través del mismo razonamiento que produjo el código. Los mismos hábitos de alcance, contexto y pruebas hacen mejor el código desde el principio; consulta prompts para escribir código.

Preguntas frecuentes

¿Cuál es un buen prompt para revisar código?

Indica qué revisar (errores, seguridad, manejo de errores), pide una gravedad para cada hallazgo y exige una entrada concreta que provoque el problema más un diff que lo arregle. Dile al modelo que se salte el formato y los nombres salvo que se lo pidas. Dale el contexto que tendría un revisor humano: para qué sirve el código y qué lo llama.

¿La IA puede sustituir la revisión de código humana?

No, pero es una primera pasada útil. Es rápida y se le dan bien los patrones de error comunes, como un None sin controlar, un await que falta o un SQL construido con cadenas. No conoce las reglas de tu producto, el resto del código ni por qué se tomó una decisión, y algunos de sus hallazgos estarán mal. Úsala antes de una revisión humana, no en su lugar.

¿Por qué una revisión de código con IA señala problemas que no son reales?

El modelo solo ve el código que pegaste. Si quien llama ya valida un valor, o un caso no puede darse nunca en tu sistema, el modelo no puede saberlo, así que señala el riesgo igualmente. Pedir una entrada concreta que provoque el fallo filtra muchos de ellos: un hallazgo sin ninguna entrada realista que lo provoque suele ser un falso positivo.

¿Debo pedirle a la IA que reescriba mi código durante una revisión?

Pide diffs pequeños, uno por hallazgo, en lugar de un archivo reescrito. Una reescritura completa mezcla las correcciones con cambios que nadie pidió, y tendrías que revisar también la reescritura. Los diffs te permiten aceptar o rechazar cada hallazgo por separado.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR