The Three Ways in the AI Era -- Parte 3
AI Code Review que no grita '¡que viene el lobo!'
En el artículo anterior nuestro equipo de QuantumAPI arregló el tamaño de lote. La PR monstruo de 47 archivos se convirtió en tres specs pequeñas de 5 archivos cada una. El Primer Principio — Flujo — estaba restaurado.
Entonces la siguiente PR llegó al bot de code review con IA. Dejó 22 comentarios. 19 eran ruido. El reviewer le echó un vistazo, hizo scroll y cerró la pestaña. En una semana, todos los devs del equipo habían aprendido a ignorar al reviewer de IA por completo.
Eso es el Segundo Principio roto. El feedback existe, pero nadie lo lee. En términos DevOps, esto es peor que no tener reviewer — porque el equipo aún se siente cubierto mientras mete bugs en producción.
El Problema

La historia del pastorcillo mentiroso tiene 2.500 años y sigue siendo la mejor metáfora para el mal alerting.
Este es el historial del reviewer de nuestro equipo tras un mes de uso:
- 22 comentarios en una PR de 5 archivos sobre encriptación de campos
- “Posible inyección SQL” en un método que usa consultas parametrizadas con EF Core
- “Uso de algoritmo obsoleto” sobre
ML-KEM-768, el KEM post-cuántico estándar de NIST - “Falta null check” sobre un tipo no-nullable
- “Secreto hardcodeado” sobre el string
"vault-id"— que es un nombre de clave de configuración, no un valor secreto - “Concatenación de strings insegura” sobre un mensaje de log
De 22 comentarios, 3 eran issues reales. El resto era ruido.
Gene Kim escribió en el DevOps Handbook que el Segundo Principio — Feedback — tiene que ser amplificado, rápido y fiable. Quita cualquiera de las tres y todo se cae. Nuestro equipo tenía feedback amplificado (¡22 comentarios!), era rápido (llegaba en 90 segundos), pero no era fiable. Así que lo apagaron mentalmente. Feedback = 0.
El problema no es que los reviewers de IA lean el código mal. El problema es que a la mayoría se les da un prompt genérico sin contexto del dominio, del stack o de las preferencias del equipo. Nunca contratarías a un reviewer humano sin decirle “usamos EF Core, usamos PQC, no nos importan los nits de estilo”. Pero así es como la mayoría despliegan su reviewer de IA.
La Solución

Aquí entra GOTCHA. GOTCHA es una estructura de prompt para agentes de IA. Tiene 6 secciones:
| Letra | Sección | Ejemplo en code review |
|---|---|---|
| G | Goals | Cazar bugs reales y problemas de seguridad en código .NET con PQC. Nada más. |
| O | Orchestration | Leer diff de la PR → cargar la spec enlazada → comparar con el estilo del equipo → producir máximo 5 comentarios. |
| T | Tools | Mistral Large 2, diff de la PR, archivo spec.md, guía de estilo del equipo, docs del SDK QuantumAPI. |
| C | Context | Este codebase: .NET 10, EF Core 10, SDK QuantumAPI v2.3, ML-KEM-768 para KEMs, ML-DSA-65 para firmas. |
| H | Heuristics | Ignora falsos positivos sobre PQC. Nada de nits de estilo. Ningún “falta null check” sobre tipos no-nullable. Marca solo los issues listados en team-review-rules.md. |
| A | Args | El diff real de la PR + el path de la spec + el título de la PR. |
La magia está en Context y Heuristics. Un reviewer genérico no tiene ni uno ni otro. Por eso grita ‘¡que viene el lobo!’. Dale los dos y deja de hacerlo.
Hay un efecto lateral bueno. Los prompts GOTCHA son portables. La misma estructura vale para code review, generación de código, triage de incidentes o cualquier tarea de IA. El equipo va a reutilizar esta misma estructura en los próximos artículos.
Ejecutar
Paso 1 — El reviewer antiguo
El equipo estaba usando un reviewer de IA genérico del marketplace de GitHub con un prompt por defecto:
“Revisa esta pull request en busca de bugs, problemas de seguridad y problemas de calidad de código.”
Eso es todo. Sin contexto. Sin restricciones. Sin conocimiento del dominio. El resultado fue el desastre de 22 comentarios que vimos arriba.
Decidieron sustituirlo por un reviewer custom usando Mistral Large a través de las APIs generativas de Scaleway — hosting europeo, más barato que OpenAI, suficiente para code review. Y un prompt estructurado con GOTCHA.
Paso 2 — Construir el prompt GOTCHA

Guardaron esto como .ai/review-prompt.md en el repo:
# Prompt GOTCHA de Code Review — Equipo QuantumAPI
## G — Goals
Eres un reviewer senior de .NET y seguridad para un equipo que integra
QuantumAPI (encriptación post-cuántica) en una app de servicios
financieros. Tu único trabajo es cazar:
1. Bugs reales que causarán fallos en runtime
2. Problemas reales de seguridad (no teóricos)
3. Violaciones de spec (cuando la PR añade cosas fuera de la spec enlazada)
Ignora todo lo demás. Produce COMO MÁXIMO 5 comentarios por PR.
Si no hay nada real que marcar, di "No issues found."
## O — Orchestration
Para cada PR:
1. Lee el diff completo de la PR (no solo el parche)
2. Carga el archivo de spec referenciado en la descripción de la PR
3. Compara el diff con el scope y non-goals de la spec
4. Revisa cada archivo modificado contra las Heuristics de abajo
5. Ordena hallazgos por severidad (bug > seguridad > violación-de-spec)
6. Emite máximo 5, como comentarios inline en la PR
## T — Tools
- Mistral Large 2 (tu modelo)
- Diff de la PR (como input)
- spec.md (path en la descripción de la PR)
- team-review-rules.md (cargado como contexto)
- Docs del SDK QuantumAPI v2.3 (cargadas como contexto)
## C — Context
Stack: .NET 10, EF Core 10 (code-first), PostgreSQL 16, AKS, Azure DevOps.
Cripto: ML-KEM-768 para KEMs, ML-DSA-65 para firmas, AES-256-GCM
para simétrica. Todo envuelto por el SDK QuantumAPI v2.3 — NO sugieras
"migrar de ML-KEM-768 a otra cosa". ML-KEM-768 ES el objetivo.
Tests: xUnit. Auth: JWT vía Entra ID. Logging: Serilog + App Insights.
El equipo tiene una regla: las PRs deben tener ≤ 8 archivos. Las specs
están en `specs/`.
## H — Heuristics
- NUNCA marques "SQL injection" sobre LINQ de EF Core o consultas parametrizadas
- NUNCA marques "algoritmo obsoleto" sobre ML-KEM-*, ML-DSA-*, AES-256
- NUNCA marques "falta null check" sobre tipos de referencia no-nullable
- NUNCA marques "secreto hardcodeado" sobre nombres de claves de config o constantes
- NUNCA dejes comentarios de estilo (naming, formato, comentarios, docs)
- NUNCA sugieras refactors fuera del diff
- SIEMPRE verifica cumplimiento de spec: si la PR toca archivos que no
están en el Scope de la spec, márcalo como violación-de-spec
- SIEMPRE comprueba la propagación de CancellationToken en métodos async
- SIEMPRE comprueba que los secretos nuevos vienen de IConfiguration/KeyVault,
no de strings hardcodeados o literales de variables de entorno
- Si un hallazgo es "quizás" en vez de "seguro", sáltalo
## A — Args
- Diff de la PR: <provisto en runtime>
- Path de la spec: <desde la descripción de la PR>
- Título de la PR: <desde los metadatos de la PR>
- Archivos modificados: <lista desde el diff>
50 líneas. Un archivo en el repo. Versionado con el código.
Paso 3 — Cablearlo en Azure DevOps
Un pipeline pequeño que corre en cada PR. Guardado como pipelines/ai-review.yml:
trigger: none
pr:
branches:
include: [ main ]
pool:
vmImage: ubuntu-latest
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.12'
- script: pip install httpx azure-devops
displayName: Install deps
- script: python scripts/ai_review.py
displayName: AI code review
env:
SCW_SECRET_KEY: $(SCW_SECRET_KEY)
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
Y el script de Python que hace el trabajo real (scripts/ai_review.py, abreviado):
import os, httpx, subprocess, json
def run(cmd): return subprocess.check_output(cmd, shell=True, text=True)
# 1. Leer el diff de la PR
diff = run("git diff origin/main...HEAD --unified=5")
# 2. Cargar el prompt GOTCHA
prompt = open(".ai/review-prompt.md").read()
# 3. Leer la spec enlazada (del título de la PR: "[spec-001] ...")
pr_title = os.environ["SYSTEM_PULLREQUEST_PULLREQUESTTITLE"]
spec_id = pr_title.split("]")[0].strip("[ ")
spec = open(f"specs/{spec_id}/spec.md").read()
# 4. Llamar a Mistral Large vía Scaleway
r = httpx.post(
"https://api.scaleway.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['SCW_SECRET_KEY']}"},
json={
"model": "mistral-large-2411",
"messages": [
{"role": "system", "content": prompt},
{"role": "user", "content": f"SPEC:\n{spec}\n\nDIFF:\n{diff}"},
],
"temperature": 0.1,
},
timeout=120,
)
findings = r.json()["choices"][0]["message"]["content"]
# 5. Postear comentarios vía la API REST de Azure DevOps
post_pr_comment(findings)
60 líneas en total. No es un producto. No es un framework. Es lo justo para sustituir a la máquina de 22 comentarios de ruido.
Paso 4 — Misma PR, reviewer nuevo

Volvieron a pasar la misma PR de encriptación de 5 archivos por el reviewer nuevo. La salida:
[AI Review] 4 hallazgos para PR #412
1. src/Infrastructure/QuantumApiFieldEncryptor.cs:18
Se acepta el parámetro CancellationToken pero no se propaga a
client.Vault.EncryptAsync. Esto romperá la cancelación.
Severidad: bug
2. src/Infrastructure/Data/QuantumEncryptedConverter.cs:31
El value converter no maneja input null. Lanzará excepción al
leer un campo encriptado nullable. Añade coalesce de null.
Severidad: bug
3. src/Infrastructure/QuantumApiFieldEncryptor.cs:9
QuantumApiOptions.VaultId se lee desde appsettings.json en
AddQuantumEncryption. La spec exige que el vault ID venga de
variable de entorno. Viola restricción de la spec.
Severidad: violación-de-spec
4. tests/Infrastructure.Tests/QuantumApiFieldEncryptorTests.cs
Falta test de integración que cubra el criterio de aceptación
de round-trip write/read de la spec.
Severidad: violación-de-spec
4 comentarios. Todos accionables. Todos reales. El reviewer arregló los dos primeros en 20 minutos. El tercero y el cuarto eran violaciones de spec genuinas que un reviewer humano con prisa probablemente habría pasado por alto.
| Métrica | Reviewer antiguo | Reviewer GOTCHA |
|---|---|---|
| Comentarios por PR | 22 | 4 |
| Señal-a-ruido | 14% | 100% |
| Comentarios atendidos | 14% | 100% |
| Confianza en el reviewer | ”ignóralo" | "léelo primero” |
| Tiempo de ejecución | 90s | 70s |
| Coste mensual (Scaleway) | — | ~12 € |
Segundo Principio restaurado. El feedback vuelve a ser amplificado, rápido y fiable.
Plantilla
Prompt GOTCHA de code review reutilizable. Guárdalo como .ai/review-prompt.md en tu repo y rellena los <placeholders>:
# Prompt GOTCHA de Code Review — <nombre del equipo>
## G — Goals
<¿Qué debe cazar el reviewer? ¿Qué debe ignorar?>
## O — Orchestration
<Paso a paso: leer diff → cargar spec → comparar → ordenar → emitir>
## T — Tools
<Modelo, diff, spec, guía de estilo, docs del SDK>
## C — Context
<Stack, versiones, algoritmos específicos del dominio, convenciones del equipo>
## H — Heuristics
- NUNCA <falso positivo específico del dominio 1>
- NUNCA <falso positivo específico del dominio 2>
- SIEMPRE <check obligatorio específico del dominio 1>
- SIEMPRE <check obligatorio específico del dominio 2>
- Si un hallazgo es "quizás" en vez de "seguro", sáltalo
## A — Args
- Diff de la PR: <runtime>
- Path de la spec: <runtime>
- Archivos modificados: <runtime>
Regla del pulgar: tu sección Heuristics empieza vacía. Añade una regla NUNCA cada vez que tengas un falso positivo. Añade una regla SIEMPRE cada vez que un bug real se cuele. En 4-6 semanas el prompt se convierte en un documento vivo del gusto de tu equipo.
El Reto
Coge tu reviewer de IA actual. Saca los últimos 10 comentarios que dejó. Cuenta cuántos atendieron tus devs de verdad. Si es menos del 50%, tu reviewer está gritando ‘¡que viene el lobo!’. Dedica una hora esta semana a escribir un prompt GOTCHA para tu contexto. Mide la señal-a-ruido la semana siguiente.
En el próximo artículo atacamos el Tercer Principio — Aprendizaje Continuo. Nuestro equipo de QuantumAPI ahora entrega PRs pequeñas con review útil. Pero cuando producción se rompió el mes pasado, nadie del equipo pudo explicar por qué ML-KEM-768 y no ML-KEM-1024. El código se entregó, el conocimiento no. Es hora de usar la IA como tutor, no solo como generador.
→ Artículo 4: Aprendizaje Continuo cuando la IA escribe la mitad de tu código (próximamente)
Si esta serie te ayuda, considera patrocinarme en GitHub o invitarme a un café.
Esta es la parte 3 de 6 de la serie “Los Tres Principios en la Era de la IA”. Anterior: Specs antes que código.
Loading comments...