Use Cases (Mis à jour: 02/06/2026)

Moderniser du code legacy avec Claude Code sans casser la production

Workflow pratique pour moderniser du code legacy avec Claude Code: tests, TypeScript, risques, exemples exécutables et vérification.

Moderniser du code legacy avec Claude Code sans casser la production

La modernisation échoue quand elle commence trop large

Un code legacy n’est pas seulement un code ancien. C’est un code dont le comportement est difficile à prouver, avec peu de tests, des règles métier implicites et un risque élevé sur les commandes, la facturation, le support ou la publication. Claude Code est très utile dans ce contexte, mais il ne doit pas devenir un bouton de réécriture massive.

Le workflow sûr commence par l’observation, continue par des tests de caractérisation, puis avance par petites refactorisations. Un test de caractérisation capture le comportement actuel avant de décider si ce comportement est idéal. Le harness est l’échafaudage qui permet à l’agent de travailler: tests, permissions, commandes, règles du projet et checklist de revue.

La documentation officielle Claude Code common workflows couvre l’exploration d’une base de code, le refactoring, les tests, les PR et les sessions parallèles avec worktrees. Cette approche convient très bien au legacy: Claude Code accélère l’exécution, mais l’équipe garde le contrôle du risque et de l’acceptation.

Trois cas d’usage concrets

Ne modernisez pas parce qu’un fichier est laid. Commencez par les endroits où une meilleure maîtrise réduit un vrai risque métier.

Cas d’usageObjectifCe que Claude Code peut faireCe que l’humain doit vérifier
Commandes, facturation, paiementÉviter les erreurs de montant et de statut clientCartographier le comportement, ajouter des tests, trouver les limitesTaxes, remises, arrondis, conformité
Migration JavaScript vers TypeScriptRendre les changements futurs plus sûrsAjouter des types, réduire any, corriger les erreurs par lotsCompatibilité de l’API publique
Callbacks ou fonctions géantesAméliorer la maintenabilité sans changer le comportementSéparer les responsabilités, proposer des noms, expliquer les diffsErreurs, retries, effets secondaires

Chez ClaudeCodeLab, j’utilise le même schéma pour les scripts de publication, le code de checkout et les anciennes transformations de contenu. Le point important n’est pas que Claude Code écrive vite. Le point important est que chaque changement reste assez petit pour être relu et relié à une preuve.

flowchart LR
  A[Explorer] --> B[Figer le comportement par tests]
  B --> C[Refactoriser par petites étapes]
  C --> D[Ajouter des types et traiter les dépendances]
  D --> E[Revue humaine des risques]
  E --> B

Commencer par un audit en lecture seule

Le premier prompt doit interdire les modifications. À ce moment, vous voulez une carte du système, pas un patch produit avec un contexte incomplet.

Lis @src/legacy et @test.
Ne modifie aucun fichier pour l'instant.

Retourne cet audit:
1. Fichiers principaux et responsabilités
2. I/O externes, base de données, API, écritures de fichiers et effets de bord
3. Comportements qui doivent rester compatibles
4. Tests manquants et branches à risque
5. Ordre le plus sûr pour de petits changements

Si une règle est floue, écris "confirmation humaine nécessaire" au lieu de deviner.

La page officielle How Claude Code works explique que Claude Code peut lire les fichiers, exécuter des commandes et modifier le code. C’est puissant, mais dans un vieux système il faut limiter la première phase à la compréhension. Une correction qui semble idiomatique peut changer une réponse dont dépend déjà un client.

Exemple minimal exécutable

L’exemple suivant est volontairement petit, mais il respecte l’ordre utilisé dans un vrai chantier legacy.

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

Voici l’ancien processeur de commandes. Il mélange validation, calcul et construction de réponse. C’est le type de code qui fonctionne depuis longtemps et devient donc intimidant à modifier.

// 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
  };
}

Ajoutez ensuite des tests qui verrouillent le comportement actuel. Ils ne redessinent pas le domaine; ils protègent les contrats existants.

// 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");
  });
});

Exécutez npm test. Quand les tests passent, seulement alors demandez une modification.

Lis @src/legacy/orderProcessor.js et @test/orderProcessor.test.ts.
Migre ce code vers TypeScript en gardant les tests au vert.

Règles:
- Garde le nom public processOrder
- Préserve status, total, discount et message
- Ajoute les types d'abord, puis sépare les responsabilités
- Exécute npm test et npm run typecheck
- Explique quelle compatibilité chaque diff préserve

Une structure TypeScript plus facile à relire

Après modernisation, les types, la validation, les calculs et l’orchestration sont séparés. L’objectif n’est pas une abstraction élégante, mais une logique financière lisible et vérifiable.

// 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
  };
}

Changez l’import du test vers ../src/orderProcessor, puis relancez npm test et npm run typecheck. Une diff de cette taille reste relisible. Si le même PR déplace des dossiers, met à jour des dépendances majeures, change le formatage et renomme des concepts métier, la revue perd en qualité.

Traiter les dépendances séparément

Une erreur courante consiste à mélanger refactoring et mises à jour majeures. Quand les tests cassent, il devient impossible de savoir si la cause vient de TypeScript, d’une API modifiée, du bundler ou de la logique métier.

Demandez d’abord un inventaire.

Lis package.json et le lockfile.
Ne mets rien à jour pour l'instant.

Retourne un tableau avec:
- package
- version actuelle
- version cible recommandée
- présence d'un major upgrade
- URL du guide officiel de migration
- fichiers probablement affectés
- tests à ajouter avant la mise à jour

Pour les opérations larges ou destructrices, gardez un modèle de permissions prudent. La documentation Claude Code permissions est utile avant d’autoriser migrations, suppressions ou déploiements. La vitesse d’un agent ne vaut rien si elle supprime l’étape d’approbation qui protégeait le système.

Pièges concrets

Le premier piège est de refactoriser sans tests. Un code plus propre reste une régression si un arrondi, une remise ou un message d’erreur change.

Le deuxième est de transformer une suggestion de Claude Code en règle métier. Une réponse plus idiomatique n’est pas forcément compatible avec les clients existants.

Le troisième est le PR géant. Migration de types, découpage logique, dépendances, déplacements de fichiers et formatage doivent être séparés quand c’est possible.

Le quatrième est d’améliorer trop vite la gestion d’erreurs. Dans un vieux système, un null, une chaîne précise ou un statut HTTP étrange peut être un contrat.

Le cinquième est de reporter la documentation. Demandez à Claude Code de rédiger les notes de compatibilité, les étapes de vérification manuelle et le plan de rollback dans la description du PR.

Revue, liens et CTA

Combinez ce workflow avec le guide d’automatisation du refactoring, TDD avec Claude Code et le guide de génération de documentation. Pour un usage en équipe, documentez zones interdites, commandes de test, vocabulaire métier et règles de revue dans les bonnes pratiques CLAUDE.md.

ClaudeCodeLab aide les équipes à introduire Claude Code dans des produits existants avec formation, modèles CLAUDE.md et conseil en modernisation. Le but n’est pas une réécriture par IA, mais un workflow répétable où tests, permissions et revue humaine rendent le travail de l’agent acceptable.

Résultat vérifié

J’ai testé ce flux dans legacy-modernization-demo: processeur JavaScript legacy, trois comportements verrouillés avec Vitest, migration vers TypeScript, puis npm test et npm run typecheck. Le gain principal venait de la séparation entre audit en lecture seule et prompt d’édition. Les diffs étaient plus petits, et il était plus facile de vérifier le total, la remise VIP et l’erreur de commande vide.

#Claude Code #code legacy #refactoring #dette technique #modernisation
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.