Uso Claude Code y ChatGPT prácticamente todos los días. Me ayudan mucho, pero con reglas estrictas para que no terminen metiendo código falopa en el repo.
En la Parte 1 hablé de la filosofía general y de qué tareas delego o no. Acá me meto en el día a día:
- El patrón mental que sigo siempre
- Cómo los uso para debugging
- Cómo los uso para tests
- Cómo los uso para ideas de arquitectura
- Cómo les doy contexto sin regalarles el repo
- Un checklist rápido de rutina diaria
La idea no es que la IA programe por vos, sino que te saque de encima lo que te distrae de pensar.
Patrón base: primero pensás vos, después la IA complementa
Mi patrón mental es simple:
- Entendés y reformulás el problema
- Escribís tu solución o al menos tu approach
- Recién ahí traés a la IA para refinar, cuestionar o automatizar
flowchart LR P["Problema"] --> H1["Humano piensa"] H1 --> Draft["Borrador de código"] Draft --> IA["IA sugiere mejoras"] IA --> H2["Humano revisa y decide"] H2 --> Repo["Código final"]Paso 1: formulás el problema en tu cabeza
Antes de abrir Claude o ChatGPT, trato de que mi cabeza haga el laburo básico:
- ¿Qué estoy tratando de resolver exactamente?
- ¿Dónde está el borde difícil? (algoritmia, API, performance, DX)
- ¿Qué condiciona la solución? (stack actual, restricciones de negocio)
Si no lo podés explicar en 3 o 4 frases, todavía no está para la IA. Es un filtro para que no te use de goma de borrar mental.
Paso 2: escribís tu versión primero
Aunque sea pseudo código, siempre escribo algo mío antes de pedir ayuda. Ejemplo real de mi proyecto Hospeda:
// Hook para filtrar destinos - lo escribo funcional primeroexport function useFilteredDestinations( destinations: Destination[], filters: DestinationFilters) { return useMemo(() => { let result = destinations;
if (filters.region) { result = result.filter((d) => d.region === filters.region); }
if (filters.minRating != null) { result = result.filter((d) => d.rating >= filters.minRating); }
if (filters.hasAccommodations) { result = result.filter((d) => d.accommodationCount > 0); }
return result; }, [ destinations, filters.region, filters.minRating, filters.hasAccommodations, ]);}¿Es mejorable? Seguro. ¿Funciona? Sí. Primero lo dejo funcional, después pido ayuda para pulirlo.
Paso 3: IA como revisor y generador de alternativas
Recién acá le digo algo tipo:
Este hook de React funciona, pero lo quiero:- Más legible- Más fácil de extender si agrego filtros nuevos- Manteniendo el mismo comportamiento
Proponeme 2 alternativas de refactor, explicando pros y contras.Si propone algo mejor, lo comparo. Si mete cosas raras, lo descarto. La clave: no pregunto “¿cómo hago esto?”. Pregunto “¿cómo mejorás esto que ya funciona?”.
Debugging: IA como segundo par de ojos
flowchart TD Bug[Bug raro] --> Repro[Reproducís el error] Repro --> Q1{¿Stack trace claro?}
Q1 -- "Sí" --> Inspect[Inspeccionás código] Q1 -- "No" --> IAContext[Le das contexto a la IA]
Inspect --> Q2{¿Encontraste la causa?} IAContext --> Hyp[IA propone hipótesis] Hyp --> Q2
Q2 -- "Sí" --> Fix[Aplicás el fix] Q2 -- "No" --> DeepDive[Debug más profundo] DeepDive --> IARefine[Volvés a la IA con hallazgos] IARefine --> Hyp
Fix --> Tests[Corrés tests] Tests --> Done[Merge]Uso la IA cuando:
- El error no es obvio
- El stack trace es críptico
- Ya probaste 2-3 cosas y seguís igual
Ejemplo de prompt para debugging
Contexto:- Front: React 19 + TanStack Query- API: Hono- DB: Drizzle + PostgreSQL
Error en consola:TypeError: Cannot read properties of undefined (reading 'items')
Componente que explota:
function DestinationsList() { const { data, isLoading } = useQuery({ queryKey: ["destinations"], queryFn: () => destinationService.list({ page: 1, pageSize: 20 }), });
if (isLoading) return <Spinner />;
return ( <ul> {data.items.map((d) => <li key={d.id}>{d.name}</li>)} </ul> );}
Qué sospechas harías y qué cosas revisarías paso a paso.No inventes código nuevo, focalizate en posibles causas.La IA no “magia” el bug, pero te ordena las hipótesis, te recuerda casos borde que se te escaparon, y te hace de checklist cuando ya venís cansado.
Cuando el stack trace no alcanza
También la uso para entender errores de Drizzle o PostgreSQL:
Tengo este error de Drizzle:
Error: Column "destinations.slug" must appear in the GROUP BY clauseor be used in an aggregate function
Con este query:
const rows = await db .select({ slug: destinations.slug, count: sql<number>`count(*)`.as("count"), }) .from(destinations) .groupBy(destinations.region);
Explicame:1) Qué está diciendo exactamente el error2) Por qué pasa con este query3) Dos formas correctas de reescribirloLo que le pido: que explique. Lo que hago yo: elijo la solución y la adapto.
Tests: intención humana, esqueleto IA
flowchart TD Regla[Regla de negocio] --> Coment[Escribís intención como comentario] Coment --> Prompt[Prompt a la IA con el pattern] Prompt --> Skeleton[IA genera esqueleto] Skeleton --> Review[Ajustás mocks y asserts] Review --> RunTests[Corrés tests] RunTests --> Q{¿Pasan?} Q -- "Sí" --> Commit[Commit] Q -- "No" --> Fix[Arreglás] Fix --> RunTestsPara tests unitarios o de integración:
- Escribo a mano la intención del test
- Lo dejo como comentario en el archivo
- Le pido a la IA que lo transforme en un test con mi stack
Ejemplo de comentario:
// Quiero testear:// - Que DestinationService.list() devuelve paginado correctamente// - Que respeta los filtros de búsqueda// - Que si el actor no tiene permisos, devuelve FORBIDDENPrompt típico:
Con este comentario como guía y usando el pattern de tests de mi proyecto(Vitest + factories), generame los tests completos.
Pattern que uso:
import { beforeEach, describe, expect, it, vi } from 'vitest';import { createActor } from '../../factories/actorFactory';import { createModelMock } from '../../utils/modelMockFactory';
describe('DestinationService.list', () => { let service: DestinationService; let model: ReturnType<typeof createModelMock>;
beforeEach(() => { model = createModelMock(); service = new DestinationService({ logger: mockLogger }, model); vi.restoreAllMocks(); });
it('should return paginated list', async () => { // ... });});La IA arma el esqueleto. Vos ajustás mocks, assertions y casos borde que conocés del negocio.
Ejemplo con función simple
Función para generar slugs:
export function generateSlug(name: string, existingSlugs: string[]): string { const base = name .toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, '');
let slug = base; let counter = 1;
while (existingSlugs.includes(slug)) { slug = `${base}-${counter}`; counter++; }
return slug;}Prompt:
Generame tests con Vitest para esta función, cubriendo:- Caso base con nombre simple- Nombre con acentos y caracteres especiales- Nombre que ya existe (debe agregar sufijo numérico)- Múltiples colisiones (debe incrementar el contador)
No uses valores mágicos raros, mantenelo simple.La IA hace el borrador, vos te quedás con el criterio.
Arquitectura: IA como colega rompebolas
Cuando estoy decidiendo algo de arquitectura, uso la IA como ese colega que siempre opina, a veces dice pavadas, pero te obliga a justificar lo que hacés.
Ejemplo real:
Contexto de proyecto:- Monorepo con apps/web (Astro), apps/admin (TanStack Start), apps/api (Hono)- packages: @repo/db (Drizzle), @repo/service-core, @repo/schemas (Zod)
Quiero decidir dónde vive la lógica para "featured destinations" que:- Lee accommodations de cada destination- Calcula un score basado en reviews y bookings- Devuelve una lista ordenada
Proponeme dos opciones:1) Método custom en DestinationService2) Query directo en el route handler
En cada caso: ventajas, desventajas, impacto en testing y reuso.No le delego la decisión. La uso para ver ángulos que quizá me estoy perdiendo.
Contexto sin regalar el repo
Contexto mínimo pero suficiente
No hace falta pegar 2000 líneas de código. Prefiero:
- 1 archivo clave
- Una descripción corta de la arquitectura
- Los tipos relevantes
Mi regla:
- Si la respuesta depende de un detalle específico → pego el código relevante
- Si es pregunta de diseño → describo, no pego
Ejemplo con handler real
export const destinationListRoute = createListRoute({ method: 'get', path: '/', handler: async (ctx, _params, _body, query) => { const actor = getActorFromContext(ctx); const result = await destinationService.list(actor, query || {});
if (result.error) throw result.error;
return { items: result.data?.items || [], pagination: getPaginationResponse(result.data?.total || 0, query), }; },});Prompt:
Mi arquitectura de services:- Todos extienden BaseCrudService- Cada service tiene permission hooks y normalizers- Pattern de llamada: service.list(actor, filters)
Con este handler, qué refactors te parecen razonables para:- Manejar el error de forma más prolija- Evitar el optional chaining repetido
No inventes servicios nuevos, trabajá con lo que hay.Límites que pongo sí o sí
Nada de credenciales ni datos sensibles
En el apuro te podés mandar una macana. Reglas que me puse:
- Nunca pego tokens de API
- Nunca pego secrets de producción
- Nunca pego connection strings reales
- Nunca pego datos reales de usuarios
En vez de la connection string real, mando:
postgres://USER:PASS@HOST:PORT/DB_NAME y aclaro que es placeholder.
Nada de decisiones finales de negocio
Cosas que no delego:
- Criterios de pricing
- Reglas de permisos por rol
- Cosas legales o de compliance
La IA me puede ayudar a expresarlo en código, pero la regla la defino yo.
Casos donde me ahorró tiempo de verdad
Refactor grande sin morir entre diffs
Tenía que mover todos los imports de @repo/db/schema/* a @repo/db sin
romper nada en el monorepo.
Flujo:
- Le pedí a Claude Code que detecte imports viejos, los reemplace, y me muestre diffs por archivo
- Yo revisé cada diff con calma
Trabajo que a mano eran 3-4 horas se fue a ~1 hora con control humano.
Tests que jamás iba a escribir “por fiaca”
Tenía services con métodos custom (getSummary, getStats, getFeatured)
que “sabía” que tenía que testear, pero nunca llegaba.
Usé IA para generar el esqueleto de tests siguiendo mi pattern. Yo ajusté lógica, nombres y agregué casos borde.
Resultado: pasaron de “algún día” a “hoy”.
Debug de bug cruzado
Los filtros de búsqueda no se aplicaban igual en el service y en el route handler.
Usé la IA para:
- Hacer un mapa del flujo de filtros: query params → handler →
service.list()→model.findAll() - Armar un checklist de debugging
Me ordenó la cabeza más que el código. Solo eso ya te ahorra tiempo.
Checklist de rutina diaria
- Lo entiendo yo primero — Si no lo podés explicar en 3 frases, no está para IA
- Escribís algo tuyo — Borrador de código, idea, comentario de test
- IA complementa, no crea desde cero — Refactors, tests, docs, explicaciones
- Siempre revisás diffs — Nada entra al repo sin pasar por tus ojos
- Corrés tests — Si tocás código testeado, los tests tienen que pasar
- Cuidás qué pegás — Nada de secrets, nada de datos reales
- No dejás que decida por vos — Arquitectura, negocio y seguridad son tu responsabilidad
Cierre
Claude y ChatGPT pueden ser parte de tu rutina diaria sin convertir tu repo en un frankenstein.
La clave está en tres cosas:
- Patrón mental claro: primero pensás vos, después la IA ayuda
- Flujos concretos: debugging, tests, refactors y diseño asistido (no tercerizado)
- Límites sanos: sin secrets, sin decisiones de negocio delegadas, sin commits ciegos
Si los tratás como herramientas y no como oráculos, son como tener un junior hiper rápido al lado… pero el senior que firma los commits seguís siendo vos.
Referencias
- Claude Code — Asistente integrado para trabajar sobre el repo
- ChatGPT — Para texto largo, ejemplos y debugging guiado
- Parte 1: IA como copiloto, no como piloto suicida — La filosofía general
- Configurando Claude Code para monorepos grandes — Setup para proyectos complejos