Workflow Sistemático de Revisión de Código con IA: Planificar, Generar, Validar
Guía práctica para mantener la calidad del código a velocidad de IA
Introducción: El Costo Oculto del Código que “Funciona”
El siguiente código genera un endpoint de registro de usuarios. Compila correctamente, pasa las pruebas básicas y funciona en staging:
app.post('/api/register', async (req, res) => {
const { email, password, username } = req.body;
const user = await db.query(
`INSERT INTO users (email, password, username)
VALUES ('${email}', '${password}', '${username}') RETURNING *`
);
res.json({ success: true, user });
});
Problemas críticos:
- Vulnerabilidad de inyección SQL
- Contraseña almacenada en texto plano
- Sin validación de entrada
- Sin manejo de errores
- Sin protección contra duplicados
Este es el resultado típico cuando se genera código con IA sin un proceso de validación. La IA optimiza para código que funciona, no para código correcto.
La solución es implementar un workflow sistemático que separe la generación de la validación.
El Workflow de Tres Fases
Principio fundamental: Cada fase tiene un propósito específico y utiliza herramientas diferentes. La IA de generación (Cursor, Copilot, Claude) optimiza velocidad. La IA de revisión (Claude Code, CodeRabbit) optimiza seguridad y calidad.
Fase 1: Planificación Estructurada
Una planificación clara produce mejores resultados de la IA generativa. Antes de escribir código, complete el siguiente template:
Template de Planificación
Feature: [Nombre del feature]
Propósito: [Una oración describiendo el objetivo]
Entradas:
- campo: tipo, reglas de validación
- campo: tipo, reglas de validación
Salidas:
Éxito:
- código HTTP, estructura de respuesta
Error:
- código HTTP, estructura de error
Casos Edge:
- ¿Qué sucede si...?
- ¿Qué sucede si...?
Seguridad:
- Consideración 1
- Consideración 2
Dependencias:
- Librería/servicio requerido
Ejemplo Aplicado: Registro de Usuario
Feature: User Registration Endpoint
Propósito: Crear nuevos usuarios con validación completa y almacenamiento seguro
Entradas:
- email: string, formato RFC 5322, único en BD
- password: string, mínimo 8 caracteres, al menos 1 número y 1 mayúscula
- username: string, 3-20 caracteres alfanuméricos
Salidas:
Éxito:
- 201 Created, { user: { id, email, username, createdAt } }
Error:
- 400 Bad Request, { errors: [{ field, message }] }
- 409 Conflict, { error: "El email ya está registrado" }
- 500 Internal Error, { error: "Error interno del servidor" }
Casos Edge:
- Email con formato válido pero dominio inexistente
- Password que cumple requisitos mínimos pero es común (123456Aa)
- Username con caracteres Unicode que parecen alfanuméricos
- Solicitudes duplicadas simultáneas (race condition)
Seguridad:
- Hash de password con bcrypt (cost factor 12)
- Queries parametrizadas (prevenir SQL injection)
- Rate limiting en endpoint
- No revelar si email existe en mensaje de error genérico
- Sanitización de inputs antes de logging
Dependencias:
- bcrypt para hashing
- zod para validación
- express-rate-limit para throttling
Fase 2: Generación con Contexto
Con el template completo, se construye un prompt estructurado que incluye todos los requisitos.
Mejores Prácticas de Generación
| Práctica | Descripción |
|---|---|
| Una tarea por prompt | Generar un endpoint, componente o función a la vez |
| Contexto limpio | Nuevo chat para cada tarea, evitar contextos contaminados |
| Stack explícito | Especificar versiones, convenciones y estructura del proyecto |
| Iteración deliberada | 2-3 iteraciones refinando, no esperar perfección inicial |
Prompt Estructurado
Crear un endpoint Express + TypeScript para registro de usuarios.
REQUISITOS TÉCNICOS:
- POST /api/v1/auth/register
- Validación con Zod
- Hash con bcrypt (12 rounds)
- Queries parametrizadas con pg (node-postgres)
- TypeScript strict mode
VALIDACIONES:
- email: formato RFC 5322
- password: mínimo 8 chars, 1 número, 1 mayúscula
- username: 3-20 chars alfanuméricos
RESPUESTAS:
- 201: { user: { id, email, username, createdAt } }
- 400: { errors: [{ field: string, message: string }] }
- 409: { error: string } para email duplicado
- 500: { error: string } genérico
SEGURIDAD:
- No incluir password en ninguna respuesta
- Mensaje genérico para email duplicado
- Logging sin datos sensibles
ESTRUCTURA DEL PROYECTO:
src/
controllers/
middleware/
validators/
types/
Fase 3: Validación Automatizada
El código generado requiere validación con una segunda perspectiva especializada en seguridad.
Arquitectura de Validación
Opción 1: Claude Code como Revisor de Seguridad
Claude Code permite ejecutar análisis de seguridad directamente desde la terminal. Configuración recomendada:
Instalación y Setup
# Instalar Claude Code
npm install -g @anthropic-ai/claude-code
# Instalar herramientas de análisis
npm install -D eslint @eslint/js eslint-plugin-security
npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
Configuración ESLint para Seguridad
// eslint.config.js
import security from 'eslint-plugin-security';
import tseslint from '@typescript-eslint/eslint-plugin';
export default [
{
plugins: {
security,
'@typescript-eslint': tseslint
},
rules: {
'security/detect-object-injection': 'error',
'security/detect-non-literal-regexp': 'error',
'security/detect-unsafe-regex': 'error',
'security/detect-buffer-noassert': 'error',
'security/detect-child-process': 'warn',
'security/detect-disable-mustache-escape': 'error',
'security/detect-eval-with-expression': 'error',
'security/detect-no-csrf-before-method-override': 'error',
'security/detect-non-literal-fs-filename': 'warn',
'security/detect-non-literal-require': 'warn',
'security/detect-possible-timing-attacks': 'error',
'security/detect-pseudoRandomBytes': 'error',
'security/detect-sql-injection': 'error'
}
}
];
Prompt de Revisión para Claude Code
Crear un archivo CLAUDE.md en la raíz del proyecto con instrucciones de revisión:
# Instrucciones de Revisión de Seguridad
Al revisar código, analizar sistemáticamente:
## 1. Inyección
- [ ] SQL Injection: ¿Se usan queries parametrizadas?
- [ ] NoSQL Injection: ¿Se validan operadores de MongoDB?
- [ ] Command Injection: ¿Se sanitizan inputs para exec/spawn?
- [ ] XSS: ¿Se escapan outputs en templates?
## 2. Autenticación
- [ ] ¿Passwords hasheados con bcrypt/argon2 (cost >= 10)?
- [ ] ¿Tokens con expiración apropiada?
- [ ] ¿Rate limiting en endpoints de auth?
## 3. Autorización
- [ ] ¿Verificación de ownership en recursos?
- [ ] ¿RBAC/ABAC implementado correctamente?
## 4. Datos Sensibles
- [ ] ¿Secrets en variables de entorno (no hardcoded)?
- [ ] ¿Logging sin datos sensibles?
- [ ] ¿Respuestas sin información interna?
## 5. Dependencias
- [ ] Ejecutar: npm audit
- [ ] Verificar: no dependencias deprecated
## Comandos de Análisis
npx eslint --ext .ts,.js src/
npm audit --audit-level=moderate
Ejecutar Revisión con Claude Code
# Navegar al proyecto
cd mi-proyecto
# Iniciar revisión de seguridad
claude-code
# Dentro de Claude Code, ejecutar:
> Revisa el archivo src/controllers/auth.controller.ts
> siguiendo las instrucciones de CLAUDE.md.
> Ejecuta los comandos de análisis y reporta vulnerabilidades.
Opción 2: CodeRabbit (Integración CI/CD)
CodeRabbit se integra directamente con GitHub/GitLab para revisión automática en cada PR.
# .github/workflows/code-review.yml
name: Automated Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
security-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Dependencies
run: npm ci
- name: Security Audit
run: npm audit --audit-level=moderate
- name: ESLint Security
run: npx eslint --ext .ts,.js src/
# CodeRabbit se activa automáticamente en PRs
# después de instalarlo en el repositorio
Flujo de Decisión de Seguridad
Ejemplo Completo: De Vulnerable a Seguro
Código Vulnerable (Generación sin validación)
// ⚠️ NO USAR EN PRODUCCIÓN - Solo para demostración
import express from 'express';
import { Pool } from 'pg';
const app = express();
const pool = new Pool();
app.post('/api/register', async (req, res) => {
const { email, password, username } = req.body;
// 🔴 SQL Injection
const result = await pool.query(
`INSERT INTO users (email, password, username)
VALUES ('${email}', '${password}', '${username}')
RETURNING *`
);
// 🔴 Password en respuesta
res.json({ success: true, user: result.rows[0] });
});
Vulnerabilidades detectadas:
- Inyección SQL por concatenación de strings
- Password almacenado en texto plano
- Password retornado en respuesta
- Sin validación de entrada
- Sin manejo de errores
- Sin rate limiting
Código Seguro (Post-validación)
// src/controllers/auth.controller.ts
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import bcrypt from 'bcrypt';
import { pool } from '../config/database';
import { AppError } from '../middleware/errorHandler';
// Esquema de validación
const registerSchema = z.object({
email: z
.string()
.email('Formato de email inválido')
.max(255, 'Email demasiado largo'),
password: z
.string()
.min(8, 'Mínimo 8 caracteres')
.regex(/[0-9]/, 'Debe contener al menos un número')
.regex(/[A-Z]/, 'Debe contener al menos una mayúscula'),
username: z
.string()
.min(3, 'Mínimo 3 caracteres')
.max(20, 'Máximo 20 caracteres')
.regex(/^[a-zA-Z0-9]+$/, 'Solo caracteres alfanuméricos')
});
// Tipos
interface UserResponse {
id: string;
email: string;
username: string;
createdAt: Date;
}
interface RegisterBody {
email: string;
password: string;
username: string;
}
export const register = async (
req: Request<{}, {}, RegisterBody>,
res: Response,
next: NextFunction
): Promise<void> => {
try {
// 1. Validar input
const validation = registerSchema.safeParse(req.body);
if (!validation.success) {
const errors = validation.error.errors.map(err => ({
field: err.path.join('.'),
message: err.message
}));
res.status(400).json({ errors });
return;
}
const { email, password, username } = validation.data;
// 2. Hash password (cost factor 12)
const hashedPassword = await bcrypt.hash(password, 12);
// 3. Query parametrizada (previene SQL injection)
const query = `
INSERT INTO users (email, password_hash, username, created_at)
VALUES ($1, $2, $3, NOW())
RETURNING id, email, username, created_at as "createdAt"
`;
const result = await pool.query<UserResponse>(query, [
email.toLowerCase(),
hashedPassword,
username
]);
// 4. Respuesta sin password
res.status(201).json({
user: result.rows[0]
});
} catch (error: unknown) {
// 5. Manejo de errores específicos
if (error instanceof Error && 'code' in error) {
const pgError = error as { code: string };
// Unique constraint violation (email duplicado)
if (pgError.code === '23505') {
// Mensaje genérico (no revelar si email existe)
res.status(409).json({
error: 'No se pudo completar el registro'
});
return;
}
}
// Log sin datos sensibles
console.error('Registration error:', {
timestamp: new Date().toISOString(),
path: req.path,
// NO incluir: email, password, username
});
next(new AppError('Error interno del servidor', 500));
}
};
// src/routes/auth.routes.ts
import { Router } from 'express';
import rateLimit from 'express-rate-limit';
import { register } from '../controllers/auth.controller';
const router = Router();
// Rate limiting: 5 intentos por IP cada 15 minutos
const registerLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: { error: 'Demasiados intentos, intente más tarde' },
standardHeaders: true,
legacyHeaders: false
});
router.post('/register', registerLimiter, register);
export default router;
Checklist de Implementación
Utilizar esta lista de verificación antes de cada deploy:
Pre-Generación
- Template de planificación completado
- Casos edge identificados
- Requisitos de seguridad documentados
Post-Generación
- Revisión manual de estructura
- Convenciones del proyecto respetadas
- Tipos TypeScript correctos
Validación Automatizada
-
npm auditsin vulnerabilidades críticas - ESLint security sin errores
- Claude Code / CodeRabbit sin issues de seguridad
Seguridad Específica
- Queries parametrizadas (no concatenación)
- Passwords hasheados (bcrypt >= 10 rounds)
- Inputs validados con esquema (Zod, Joi)
- Errores sin información sensible
- Rate limiting en endpoints críticos
- CORS configurado correctamente
Conclusión
El workflow de tres fases transforma la generación de código con IA de un riesgo potencial a una ventaja competitiva:
- Planificar antes de generar reduce iteraciones y mejora la calidad del output
- Generar con contexto completo produce código más cercano a producción
- Validar con herramientas especializadas captura lo que la generación omite
La clave está en reconocer que la IA de generación y la IA de validación tienen objetivos diferentes y complementarios. Integrar ambas en un flujo sistemático permite mantener la velocidad de desarrollo sin sacrificar la seguridad.
Recursos Adicionales
- OWASP Top 10 - Vulnerabilidades web más críticas
- Claude Code Documentation - Guía oficial
- ESLint Plugin Security - Reglas de seguridad
- Zod Documentation - Validación de esquemas TypeScript
Publicado en yoDEV.dev - La comunidad de desarrolladores de Latinoamérica


