Saltar al contenido
Lección 3 de 12

Prompting para Generacion de Codigo

8 min read

El Objetivo: Codigo de Produccion al Primer Intento

La diferencia entre un desarrollador que usa IA efectivamente y uno que no, frecuentemente se reduce a los prompts de generacion de codigo. Un prompt efectivo produce codigo que compila, maneja casos extremos, sigue las convenciones de tu proyecto y esta listo para revision. Un prompt inefectivo produce codigo que tecnicamente funciona pero requiere 20 minutos de limpieza manual antes de ser aceptable para merge.

Las tecnicas en esta leccion te llevaran a codigo de produccion al primer intento para la mayoria de tareas. No siempre -- la logica compleja aun requiere iteracion. Pero para el 80% de las tareas de generacion de codigo que siguen patrones conocidos, puedes y debes esperar salida correcta en el primer intento.

Especificar el Stack Tecnologico Explicitamente

Nunca asumas que la IA conoce tu stack. Incluso si puede leer tu package.json, declarar explicitamente el stack reduce el espacio de soluciones y previene que la IA genere codigo con librerias o patrones incompatibles.

Malo:

Crea un endpoint de registro de usuarios

Bueno:

Crea un endpoint de registro de usuarios en nuestra API Express.js usando
TypeScript. Stack: Express 4, TypeScript 5, Prisma ORM con PostgreSQL, Zod
para validacion de entrada, bcrypt para hash de passwords. El endpoint debe
estar en src/routes/auth.ts.

La declaracion del stack elimina la ambiguedad. La IA no usara Mongoose (MongoDB), Sequelize, ni SQL directo. No generara JavaScript en lugar de TypeScript. Usara Zod en lugar de Joi o Yup para validacion.

Incluir Contexto de Archivos

Una de las tecnicas de generacion de codigo mas poderosas es referenciar archivos existentes. Esto le da a la IA un ejemplo concreto que igualar.

En src/components/Button.tsx, crea un nuevo componente React llamado
IconButton. Debe seguir exactamente el mismo patron que el componente Button
existente en el mismo archivo: mismo estilo de interfaz de props, mismo
enfoque de clases Tailwind, mismo patron de forwardRef. La diferencia es que
IconButton toma una prop adicional "icon" (un React.ReactNode) y la renderiza
antes de los children.

La tecnica de "patron existente" funciona porque la IA puede leer ese archivo e igualar directamente la estructura. Compara esto con describir el patron en palabras -- decir "usa forwardRef con generics apropiados de TypeScript y clases utilitarias de Tailwind con variantes CVA" es mas verboso y menos preciso que decir "sigue el patron en Button.tsx."

El Patron de Archivo de Referencia

Cuando generas codigo que debe integrarse con un codebase existente, proporciona una referencia:

Crea un nuevo handler de ruta API para GET /api/orders/:id. Usa
src/routes/products.ts como referencia para:
- Patron de manejo de errores (try/catch con AppError)
- Formato de respuesta (res.json con campos status y data)
- Uso de middleware de autenticacion
- Patrones de consulta Prisma

La orden debe incluir sus items de linea y la informacion del cliente.
Incluye manejo apropiado de 404 cuando la orden no se encuentra.

Definir Comportamiento: Entradas, Salidas, Casos Extremos

Especifica que entra, que sale, y que pasa cuando las cosas van mal.

Vago:

Escribe una funcion para procesar pagos

Especifico:

Escribe una funcion processPayment en src/services/payment.ts:

Entrada: { amount: number, currency: string, customerId: string, paymentMethodId: string }
Salida: { success: boolean, transactionId: string | null, error: string | null }

Comportamiento:
- Validar que amount sea positivo y currency sea uno de ['USD', 'EUR', 'GBP']
- Llamar a la API de Stripe usando nuestro cliente stripe existente de src/lib/stripe.ts
- En exito, devolver el transaction ID
- En error card_declined de Stripe, devolver { success: false, error: 'Tarjeta rechazada' }
- En error rate_limit de Stripe, reintentar una vez despues de 1 segundo
- En cualquier otro error, registrar el error con nuestro logger y devolver un mensaje de error generico
- Nunca exponer detalles de error de Stripe al llamador

Este prompt no deja nada a la interpretacion. La IA conoce la firma de la funcion, el tipo de retorno, el camino feliz y cada escenario de error. La salida sera precisa.

Anti-Patrones de Generacion de Codigo

Ciertas frases en prompts consistentemente producen resultados pobres porque son subjetivas o no medibles:

"Escribe codigo limpio" -- Limpio segun quien? Uncle Bob? Tu tech lead? La comunidad de Go? En su lugar, especifica que quieres decir: "Usa retornos tempranos en lugar de ifs anidados," "Manten las funciones bajo 20 lineas," "Usa nombres de variables descriptivos como customerEmail en lugar de ce."

"Sigue las mejores practicas" -- Las mejores practicas de quien? El termino significa cosas diferentes en diferentes comunidades. En su lugar: "Sigue el patron de manejo de errores de nuestro codebase existente," o "Usa consultas parametrizadas para prevenir inyeccion SQL."

"Optimiza esto" -- Optimizar para que? Velocidad? Memoria? Legibilidad? Tamano del bundle? En su lugar: "Reduce el numero de consultas a la base de datos de N+1 a una sola consulta con joins," o "Memoiza este computo para que solo se ejecute cuando la entrada cambie."

"Hazlo production-ready" -- Esta frase significa todo y nada. En su lugar, lista las cualidades especificas: "Agrega manejo de errores, validacion de entrada, logging y tipos TypeScript."

Ejemplos Reales

Generando un Endpoint de API

Crea un endpoint REST en src/routes/users.ts para PATCH /api/users/:id.

Proposito: Permitir a los usuarios actualizar su perfil (name, bio, avatarUrl).

Requisitos:
- Usar el authMiddleware existente para verificar el token JWT
- Los usuarios solo pueden actualizar su propio perfil (comparar req.user.id
  con params.id)
- Validar entrada con Zod: name (string, 1-100 chars, opcional), bio (string,
  max 500 chars, opcional), avatarUrl (URL valida, opcional)
- Usar Prisma para actualizar solo los campos proporcionados
- Devolver el objeto usuario actualizado (excluir passwordHash de la respuesta)
- Devolver 403 si el usuario intenta actualizar el perfil de otro usuario
- Devolver 404 si el ID de usuario no existe

Seguir el mismo patron que el handler GET /api/users/:id en el mismo archivo.

Generando un Componente React

Crea un componente React en src/components/UserProfile.tsx.

Props:
- user: { id: string, name: string, email: string, avatarUrl?: string, bio?: string }
- isEditable: boolean
- onSave: (updates: Partial<User>) => Promise<void>

Comportamiento:
- Modo visualizacion: Muestra info del usuario en layout de tarjeta usando
  nuestro componente Card existente
- Modo edicion (cuando isEditable y el usuario hace clic en "Editar"):
  Formulario inline con inputs controlados para name, bio y avatarUrl
- El boton de guardar llama a onSave solo con los campos que cambiaron
- Mostrar estado de carga durante el guardado (deshabilitar formulario,
  mostrar spinner)
- Mostrar toast de error en fallo de guardado usando nuestro hook existente
  useToast
- Revertir a modo visualizacion en guardado exitoso

Estilos: Usar Tailwind CSS. Igualar el estilo de nuestro componente existente
ProductCard en src/components/ProductCard.tsx.

Generando una Funcion Utilitaria

Crea una funcion utilitaria en src/utils/retry.ts:

export async function withRetry<T>(
  fn: () => Promise<T>,
  options?: { maxAttempts?: number, delayMs?: number, backoff?: boolean }
): Promise<T>

Opciones por defecto: maxAttempts = 3, delayMs = 1000, backoff = true

Comportamiento:
- Llamar a fn(). Si tiene exito, devolver el resultado.
- Si lanza una excepcion, esperar delayMs y reintentar.
- Si backoff es true, duplicar el delay en cada reintento (1s, 2s, 4s).
- Despues de maxAttempts fallos, lanzar el ultimo error.
- Registrar cada intento de reintento con console.warn incluyendo numero de
  intento y mensaje de error.

Incluir documentacion JSDoc y exportar el tipo de opciones.

El Flujo de Trabajo Generar-y-Refinar

Para generacion de codigo compleja, usa un flujo de trabajo de dos pasos:

Paso 1 -- Generar el primer borrador:

Crea la clase OrderService en src/services/OrderService.ts con metodos:
createOrder, getOrderById, getOrdersByCustomer, cancelOrder. Usa Prisma para
acceso a la base de datos. Incluye manejo de errores basico.

Paso 2 -- Refinar con feedback especifico:

Buena estructura. Ahora refina el OrderService:
1. Agregar validacion de entrada usando Zod a createOrder
2. El metodo cancelOrder debe verificar si el estado de la orden permite
   cancelacion (solo ordenes 'pending' y 'confirmed' pueden cancelarse)
3. Agregar paginacion a getOrdersByCustomer (basada en cursor, 20 por pagina)
4. Envolver todas las llamadas a Prisma en try/catch que convierta
   PrismaClientKnownRequestError a nuestra clase personalizada AppError

Este flujo de trabajo es mas rapido que intentar especificar todo de antemano para tareas complejas. El primer paso te da el esqueleto, el segundo paso agrega los detalles. Cada paso es lo suficientemente simple para que la IA lo ejecute con precision.

Sobre-Especificacion vs Sub-Especificacion

Hay un equilibrio entre dar a la IA demasiado detalle y muy poco. La sub-especificacion produce codigo generico. La sobre-especificacion desperdicia tu tiempo escribiendo el prompt y puede confundir a la IA si tus instrucciones se contradicen.

La regla: especifica las cosas que hacen tu codigo unico. Los patrones estandar (un endpoint CRUD basico, un componente React simple) necesitan menos especificacion porque la IA ha visto miles de ejemplos. Los requisitos inusuales (logica de negocio personalizada, manejo de errores no estandar, integracion con APIs propietarias) necesitan mas especificacion porque la IA no puede adivinarlos.

Cuando tengas dudas, comienza con un prompt moderadamente detallado. Si la salida falta algo, agrega ese detalle especifico en un seguimiento en lugar de reescribir el prompt completo.