Workflow de debugging con Claude Code: observar, reproducir, corregir y probar
Guía práctica para depurar con Claude Code usando TypeScript, Node, Vitest, prompts, pruebas y CTA.
Claude Code puede proponer un arreglo plausible a partir de un error, pero debugging de producción necesita más que un parche plausible. Una sesión fiable separa observación, hipótesis, reproducción mínima, implementación y prueba de regresión. Si saltas pasos, el bug puede desaparecer en una pantalla y volver por otro input, idioma o deploy.
Este artículo usa TypeScript, Node y Vitest. Combínalo con Claude Code and TDD si quieres conservar la condición fallida, con error handling patterns si el modelo de excepciones no está claro y con build error triage loop cuando el comando de prueba falla antes del bug real.
Da a Claude Code un orden de investigación
Un pedido vago como “arregla este error” empuja a Claude Code hacia el primer patch razonable. Es mejor dar error completo, stack trace, pasos de reproducción, forma del input, comportamiento esperado, comportamiento real y archivos cambiados recientemente. Luego pide tres hipótesis antes de editar.
La observación captura hechos. La hipótesis explica qué podría estar mal. La reproducción mínima convierte esa hipótesis en una prueba o script que falla. La corrección debe ser estrecha y la prueba de regresión debe quedarse en el repositorio.
{
"name": "claude-debug-lab",
"private": true,
"type": "module",
"scripts": {
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.6.0",
"vitest": "^3.0.0"
}
}
Ejemplo 1: convertir un map undefined en regla
El primer bug es común: el código llama map sobre datos que todavía no llegaron. El objetivo no es solo silenciar el crash. La regla de dominio es que un payload ausente produce lista vacía, nombres en blanco se eliminan y nombres válidos se recortan.
export type User = {
id: string;
name?: string | null;
};
export function normalizeUsers(users: User[] | null | undefined): string[] {
if (!Array.isArray(users)) return [];
return users
.filter((user): user is User & { name: string } => typeof user.name === "string")
.map((user) => user.name.trim())
.filter((name) => name.length > 0);
}
import { describe, expect, it } from "vitest";
import { normalizeUsers } from "../src/normalize-users.js";
describe("normalizeUsers", () => {
it("returns an empty list when the API payload is missing", () => {
expect(normalizeUsers(undefined)).toEqual([]);
expect(normalizeUsers(null)).toEqual([]);
});
it("keeps only usable display names", () => {
expect(
normalizeUsers([
{ id: "u1", name: " Masa " },
{ id: "u2", name: "" },
{ id: "u3", name: null },
]),
).toEqual(["Masa"]);
});
});
Usa este prompt:
Vemos Cannot read properties of undefined (reading 'map').
normalizeUsers debe tolerar payloads API ausentes y eliminar display names en blanco.
Añade primero un caso Vitest que falle y luego haz el arreglo mínimo.
Ejecuta npm test y npm run typecheck al final.
La trampa es terminar con users ?? []. Puede evitar el crash, pero deja sin definir nombres null, nombres vacíos y payloads que no son arrays. Dale a Claude Code la regla de producto, no solo la excepción.
Ejemplo 2: retry async sin ocultar el último error
Los bugs de retry async suelen parecer intermitentes. Una buena prueba revisa llamadas, espera, éxito eventual y fallo total. Si se oculta el último error, la respuesta al incidente pierde la pista del servicio que falló.
type RetryOptions = {
times: number;
delayMs: number;
sleep?: (ms: number) => Promise<void>;
};
const defaultSleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
export async function retry<T>(
task: () => Promise<T>,
{ times, delayMs, sleep = defaultSleep }: RetryOptions,
): Promise<T> {
let lastError: unknown;
for (let attempt = 1; attempt <= times; attempt += 1) {
try {
return await task();
} catch (error) {
lastError = error;
if (attempt < times) await sleep(delayMs);
}
}
throw lastError instanceof Error ? lastError : new Error("Retry failed");
}
Al pedir las pruebas, menciona aislamiento de mocks, no hacer refactor amplio y preservar el último error. Los logs temporales sirven para investigar, pero deben salir del patch final salvo que el producto necesite logging estructurado.
Ejemplo 3: reproducir límites de fecha antes de tocar la UI
El tercer caso es un bug de fechas. En pantalla parece que la exportación de marzo pierde pedidos del 31 de marzo, pero la causa suele estar en la comparación entre el inicio del mes seleccionado y el inicio del mes siguiente.
export type Order = {
id: string;
createdAt: string;
total: number;
};
export function exportMonthlyOrderIds(orders: Order[], month: string): string[] {
const [yearText, monthText] = month.split("-");
const year = Number(yearText);
const monthIndex = Number(monthText) - 1;
const start = new Date(Date.UTC(year, monthIndex, 1));
const end = new Date(Date.UTC(year, monthIndex + 1, 1));
return orders
.filter((order) => {
const createdAt = new Date(order.createdAt);
return createdAt >= start && createdAt < end;
})
.map((order) => order.id);
}
import { describe, expect, it } from "vitest";
import { exportMonthlyOrderIds } from "../src/export-orders.js";
describe("exportMonthlyOrderIds", () => {
it("includes orders from the first day through the last moment of the month in UTC", () => {
const orders = [
{ id: "feb-end", createdAt: "2026-02-28T23:59:59.999Z", total: 1000 },
{ id: "mar-start", createdAt: "2026-03-01T00:00:00.000Z", total: 2000 },
{ id: "mar-end", createdAt: "2026-03-31T23:59:59.999Z", total: 3000 },
{ id: "apr-start", createdAt: "2026-04-01T00:00:00.000Z", total: 4000 },
];
expect(exportMonthlyOrderIds(orders, "2026-03")).toEqual(["mar-start", "mar-end"]);
});
});
Indica a Claude Code que no dependa de la zona horaria local. La regla es incluir el inicio del mes elegido y excluir el inicio del mes siguiente. Así el agente tiene un objetivo verificable.
Prompt de debugging copiable
Objetivo:
Encontrar causa, añadir prueba de regresión y arreglar con el diff útil más pequeño.
Observaciones:
- Error completo:
- Pasos para reproducir:
- Resultado esperado:
- Resultado real:
- Archivos cambiados recientemente:
Restricciones:
- Lista tres hipótesis antes de editar.
- Evita refactors amplios.
- No escondas errores de tipo con any.
- Elimina logs temporales al final.
- Ejecuta npm test y npm run typecheck al final.
Reporte:
- Causa raíz
- Archivos cambiados
- Prueba de regresión añadida
- Riesgos restantes
Esto convierte a Claude Code en compañero de investigación. El reviewer ve causa, evidencia y riesgo antes de leer todo el diff. En equipos, copia estos campos al review gate para que los diffs de IA y humanos tengan el mismo estándar.
Debugging sin romper la ruta de ingresos
En sitios públicos, debugging también debe revisar CTA. Un arreglo de contenido puede empujar el formulario PDF gratis, cambiar un enlace Gumroad o hacer menos visible la consulta. Cuanto más popular sea el artículo, más importante es incluir revenue routing en la superficie de debugging.
Usa una regla pequeña para fijar la división:
const debuggingRoutes = {
first_error: "free_pdf",
repeated_bugfixes: "prompt_templates",
setup_or_ci_blocked: "setup_guide",
team_process_blocked: "consultation",
};
export function chooseDebuggingCta(intent) {
return debuggingRoutes[intent] ?? "free_pdf";
}
console.log(chooseDebuggingCta("repeated_bugfixes"));
El free cheatsheet encaja con principiantes y comparaciones. 50 Prompt Templates encaja con debugging y review repetidos. Setup Guide encaja con permisos, CI/CD y setup. La consulta encaja con rollout de equipo y decisiones operativas.
Qué se verificó en este artículo
Esta reescritura conserva texto UTF-8 limpio, bloques ejecutables, enlaces internos, enlaces externos y ruta a PDF gratis, Gumroad y consulta. Las próximas métricas son registros PDF, clics a Prompt Templates, clics a Setup Guide y visitas Training desde este slug.
Nota de verificación tras publicar
Al publicar un artículo de debugging, no termines solo con la corrección del código. Abre la URL pública y revisa h1, canonical, inicio del cuerpo, hero image, formulario de PDF gratis, enlaces Gumroad y consulta. En un artículo popular, mejorar el workflow de debugging puede cambiar directamente la acción siguiente del lector. Si la explicación mejora pero el registro PDF, el producto Gumroad o la consulta fallan, la ruta de ingresos queda incompleta.
En versiones localizadas, los ejemplos de código pueden compartirse, pero explicación y CTA deben estar en el idioma objetivo. El lector debe entender observación, reproducción, fix y test sin cambiar de idioma. En la revisión con capturas, mira inicio del cuerpo, zona del prompt y zona del CTA.
Las próximas métricas son registros PDF, clics a Prompt Templates, clics a Setup Guide y visitas a Training desde este slug. Los lectores de debugging tienen dolor activo, así que el movimiento posterior importa más que las PV brutas.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.