Use Cases (Actualizado: 2/6/2026)

Modernizar código legacy con Claude Code de forma segura

Flujo práctico para modernizar código legacy con Claude Code: pruebas, TypeScript, riesgos, ejemplos ejecutables y verificación.

Modernizar código legacy con Claude Code de forma segura

La modernización falla cuando empieza demasiado grande

El código legacy no es solo código antiguo. Es código cuyo comportamiento cuesta demostrar, que tiene poca cobertura de pruebas, que nadie quiere tocar y que suele romper procesos sensibles como pedidos, facturación, soporte o publicación de contenido. Claude Code ayuda mucho en este contexto, pero no debería usarse como un botón de reescritura masiva.

El patrón seguro es investigar primero, fijar el comportamiento actual con pruebas después y refactorizar en cortes pequeños al final. En este artículo, una prueba de caracterización significa una prueba que documenta cómo se comporta hoy el sistema, incluso si ese comportamiento no es perfecto. El harness es el andamiaje que permite trabajar al agente: pruebas, permisos, comandos, reglas del proyecto y checklist de revisión.

La documentación oficial de Claude Code common workflows organiza tareas como explorar un codebase, refactorizar, trabajar con pruebas, crear PR y usar worktrees. Esa es la mentalidad correcta para modernizar código legacy: Claude Code acelera el trabajo, pero el equipo sigue controlando el riesgo y la aceptación.

Tres casos de uso reales

No priorices por estética. Empieza por las zonas donde una mejora reduce riesgo de negocio.

Caso de usoObjetivoQué puede hacer Claude CodeQué debe revisar una persona
Pedidos, facturación o pagosEvitar errores de dinero y estado del clienteMapear comportamiento, añadir pruebas, encontrar bordesImpuestos, descuentos, redondeo y reglas legales
Migración de JavaScript a TypeScriptHacer más seguros los cambios futurosAñadir tipos, reducir any, corregir errores por lotesCompatibilidad de API pública y build
Callbacks o funciones gigantesMejorar mantenimiento sin cambiar conductaSeparar responsabilidades, proponer nombres, explicar diffsErrores, reintentos, efectos secundarios y logs

En ClaudeCodeLab uso este flujo para scripts de publicación, integración de checkout y transformaciones antiguas de contenido. Lo importante no es que Claude Code escriba rápido, sino que cada cambio sea revisable y tenga una prueba o una nota de verificación manual.

flowchart LR
  A[Explorar] --> B[Fijar conducta con pruebas]
  B --> C[Refactorizar en pasos pequeños]
  C --> D[Añadir tipos y ordenar dependencias]
  D --> E[Revisión humana del riesgo]
  E --> B

Empieza con una auditoría de solo lectura

La primera instrucción debe prohibir ediciones. En ese momento necesitas un mapa del sistema, no un parche producido con contexto incompleto.

Lee @src/legacy y @test.
Todavía no modifiques archivos.

Devuelve esta auditoría:
1. Archivos principales y responsabilidades
2. I/O externo, base de datos, API, escrituras de archivos y efectos secundarios
3. Comportamientos que deben seguir siendo compatibles
4. Pruebas faltantes y ramas de alto riesgo
5. Orden más seguro para cambios pequeños

Si una regla no está clara, escribe "requiere confirmación humana" en vez de adivinar.

La página oficial How Claude Code works explica que Claude Code puede leer archivos, ejecutar comandos y editar código. Esa capacidad es útil, pero en sistemas antiguos conviene que la primera fase esté limitada a comprensión y plan. Un cambio que parece idiomático puede romper un contrato que otro servicio ya consume.

Ejemplo mínimo ejecutable

El ejemplo es pequeño a propósito, pero sigue la misma secuencia que aplico en proyectos reales. Primero crea el proyecto.

mkdir legacy-modernization-demo
cd legacy-modernization-demo
npm init -y
npm install -D vitest typescript @types/node
npm pkg set type="module"
npm pkg set scripts.test="vitest run"
npm pkg set scripts.typecheck="tsc --noEmit"
mkdir -p src/legacy test

El procesador antiguo mezcla validación, cálculo y construcción de respuesta. No es un ejemplo artificialmente terrible; es el tipo de código que funciona y por eso se ha quedado años en producción.

// src/legacy/orderProcessor.js
export function processOrder(order) {
  if (!order || !Array.isArray(order.items) || order.items.length === 0) {
    return { status: "error", message: "items is required" };
  }

  const subtotal = order.items.reduce((sum, item) => {
    return sum + item.price * item.qty;
  }, 0);

  const discount = order.customer?.type === "vip" ? subtotal * 0.1 : 0;

  return {
    status: "confirmed",
    total: subtotal - discount,
    items: order.items,
    discount
  };
}

Ahora fija el comportamiento actual con Vitest. Estas pruebas no intentan rediseñar el dominio; solo protegen lo que no puede cambiar sin una decisión explícita.

// test/orderProcessor.test.ts
import { describe, expect, it } from "vitest";
import { processOrder } from "../src/legacy/orderProcessor.js";

describe("processOrder legacy behavior", () => {
  it("calculates total for a regular customer", () => {
    const result = processOrder({
      items: [
        { id: "A1", qty: 2, price: 1000 },
        { id: "B2", qty: 1, price: 500 }
      ],
      customer: { id: "C1", type: "regular" }
    });

    expect(result).toMatchObject({
      status: "confirmed",
      total: 2500,
      discount: 0
    });
  });

  it("applies a 10 percent VIP discount", () => {
    const result = processOrder({
      items: [{ id: "A1", qty: 1, price: 10000 }],
      customer: { id: "C2", type: "vip" }
    });

    expect(result.status).toBe("confirmed");
    expect(result.total).toBe(9000);
    expect(result.discount).toBe(1000);
  });

  it("returns an error when items are empty", () => {
    const result = processOrder({
      items: [],
      customer: { id: "C3", type: "regular" }
    });

    expect(result.status).toBe("error");
    expect(result.message).toContain("items");
  });
});

Ejecuta npm test. Solo cuando pase, pide a Claude Code que edite.

Lee @src/legacy/orderProcessor.js y @test/orderProcessor.test.ts.
Migra este código a TypeScript manteniendo las pruebas en verde.

Reglas:
- Mantén el nombre público processOrder
- Conserva status, total, discount y message
- Añade tipos primero y separa responsabilidades después
- Ejecuta npm test y npm run typecheck
- Explica qué compatibilidad preservó cada diff

Una estructura TypeScript más revisable

Después de la modernización, separa tipos, validación, cálculo y orquestación. La meta no es crear abstracciones elegantes, sino hacer visible la lógica que afecta dinero o estado de clientes.

// src/orderTypes.ts
export type CustomerType = "regular" | "vip";

export type OrderItem = {
  id: string;
  qty: number;
  price: number;
};

export type OrderInput = {
  items: OrderItem[];
  customer: {
    id: string;
    type: CustomerType;
  };
};

export type OrderResult =
  | {
      status: "confirmed";
      total: number;
      items: OrderItem[];
      discount: number;
    }
  | {
      status: "error";
      message: string;
    };

// src/validators.ts
import type { OrderInput } from "./orderTypes";

export function validateOrder(order: OrderInput | null | undefined): string | null {
  if (!order || !Array.isArray(order.items) || order.items.length === 0) {
    return "items is required";
  }
  return null;
}

// src/calculators.ts
import type { CustomerType, OrderItem } from "./orderTypes";

export function calculateSubtotal(items: OrderItem[]): number {
  return items.reduce((sum, item) => sum + item.price * item.qty, 0);
}

export function calculateDiscount(subtotal: number, customerType: CustomerType): number {
  return customerType === "vip" ? subtotal * 0.1 : 0;
}

// src/orderProcessor.ts
import { calculateDiscount, calculateSubtotal } from "./calculators";
import type { OrderInput, OrderResult } from "./orderTypes";
import { validateOrder } from "./validators";

export function processOrder(order: OrderInput): OrderResult {
  const validationMessage = validateOrder(order);
  if (validationMessage) {
    return { status: "error", message: validationMessage };
  }

  const subtotal = calculateSubtotal(order.items);
  const discount = calculateDiscount(subtotal, order.customer.type);

  return {
    status: "confirmed",
    total: subtotal - discount,
    items: order.items,
    discount
  };
}

Cambia el import del test a ../src/orderProcessor, luego ejecuta npm test y npm run typecheck. Un diff de este tamaño aún se puede revisar con seriedad. Si el mismo PR también mueve carpetas, sube dependencias mayores, cambia formato y renombra conceptos del dominio, la revisión se vuelve débil.

Separa las actualizaciones de dependencias

Otro error frecuente es mezclar refactorización con upgrades mayores. Cuando algo falla, ya no sabes si la causa es TypeScript, una API rota, el bundler o la lógica que acabas de tocar.

Primero pide inventario.

Lee package.json y el lockfile.
No actualices nada todavía.

Devuelve una tabla con:
- paquete
- versión actual
- versión objetivo recomendada
- si es major upgrade
- URL de la guía oficial de migración
- archivos probablemente afectados
- pruebas que deberíamos añadir antes de actualizar

Para operaciones destructivas o amplias, mantén permisos conservadores. La documentación oficial de Claude Code permissions conviene revisarla antes de permitir migraciones, borrados o despliegues. La velocidad del agente no sirve si elimina el punto de aprobación que protegía al sistema.

Errores concretos que veo a menudo

El primer error es refactorizar antes de tener pruebas. Un código más limpio sigue siendo una regresión si cambia un descuento, un redondeo o un mensaje de error.

El segundo es aceptar una sugerencia de Claude Code como regla de negocio. Que una respuesta sea más idiomática no significa que los clientes existentes puedan procesarla.

El tercero es crear un PR enorme. Migración de tipos, separación de lógica, actualizaciones de dependencias, movimientos de archivos y formateo deberían separarse.

El cuarto es “mejorar” demasiado rápido el manejo de errores. En sistemas antiguos, un null, una cadena concreta o un estado HTTP raro puede ser parte del contrato.

El quinto es dejar la documentación para el final. Pide a Claude Code que incluya notas de compatibilidad, pasos de verificación manual y plan de rollback en la descripción del PR.

Revisión, enlaces y CTA

Combina este flujo con la guía de automatización de refactorización, TDD con Claude Code y la generación automática de documentación. Para equipos, documenta zonas prohibidas, comandos de prueba, términos de dominio y reglas de revisión en buenas prácticas de CLAUDE.md.

ClaudeCodeLab ayuda a equipos que quieren introducir Claude Code en productos existentes mediante formación, plantillas de CLAUDE.md y consultoría de modernización. El objetivo no es una reescritura con IA, sino un flujo repetible donde pruebas, permisos y revisión humana trabajan juntos.

Resultado verificado

Probé el flujo creando legacy-modernization-demo: primero el procesador JavaScript antiguo, luego tres pruebas con Vitest, después la migración a TypeScript y finalmente npm test junto con npm run typecheck. La mayor reducción de riesgo vino de separar la auditoría de solo lectura del prompt de edición. Los diffs fueron más pequeños y fue más fácil confirmar que total, descuento VIP y error de pedidos vacíos seguían siendo compatibles.

#Claude Code #código legacy #refactorización #deuda técnica #modernización
Gratis

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.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.