Uber a publié un compte rendu de comment ils ont migré 75 000+ classes de tests et 1,25 million de lignes de code de tests à travers leur monorepo Java de JUnit 4 vers JUnit 5, et le détail principal pour n'importe quelle équipe qui pèse actuellement le refactoring assisté par IA, c'est qu'Uber a explicitement choisi de ne pas utiliser d'IA générative pour la transformation. Leur raisonnement déclaré, cité dans le compte rendu : « L'outillage de transformation déterministe était critique pour la consistance à cette échelle », et les approches basées sur les LLM « produisaient des résultats inconsistants sur les patterns de tests custom ». À la place, l'équipe a bâti sur OpenRewrite, le framework open source de transformation sémantique de code qui opère sur des arbres sémantiques sans perte plutôt que sur du texte brut, avec des recettes custom ciblant les classes de base et les test runners spécifiques à Uber. Ils ont jumelé ça avec une couche de compatibilité d'exécution unifiée (JUnit Platform faisant tourner Vintage et Jupiter en parallèle, pour que les dépôts partiellement migrés continuent de fonctionner), des vérifications de précondition pour bloquer les migrations partielles, et un système d'orchestration interne nommé Shepherd qui répartissait les transformations à travers des milliers de cibles Bazel en parallèle et validait chacune via CI.

La réalité technique derrière le choix est plus intéressante que le cadrage LLM-contre-pas-LLM le suggère. À l'échelle d'Uber, le mode d'échec qui compte le plus est l'inconsistance silencieuse : une transformation qui marche sur 99,5 % des fichiers et massacre silencieusement 0,5 % crée 375 classes de tests cassées, dont chacune doit être diagnostiquée et corrigée à la main. Les recettes OpenRewrite sont déterministes; avec le même AST en entrée et la même recette, t'obtiens la même sortie à chaque exécution, et les transformations sont exprimables en visiteurs composables sur un arbre sémantique typé. La transformation de code basée sur LLM, par contraste, est non-déterministe au niveau du token et galère surtout avec les patterns rares qu'elle n'a pas vu souvent dans les données d'entraînement, ce qui est exactement où vivent les test runners custom et les hiérarchies de classes de base d'Uber. L'article d'InfoQ note que les premières exécutions de Shepherd ont fait surface des échecs de build et de tests qui ont informé les mises à jour de la logique de transformation; c'est la boucle d'itération que tu peux réellement faire tourner avec de l'outillage déterministe parce que les échecs sont reproductibles. Avec un LLM, tu refais le même prompt et tu obtiens une erreur légèrement différente, ce qui est beaucoup plus difficile à diagnostiquer à grande échelle.

L'implication plus large pour le récit des outils de codage IA mérite d'être précise. Uber ne dit pas que les LLM ne peuvent pas faire de transformation de code; ils disent que pour cette classe spécifique de problème (haut volume, mécanique-mais-riche-en-patterns, critique pour la correction), l'outillage déterministe a gagné. Ça correspond à ce que les labos de pointe font eux-mêmes en interne : les réécritures de codebase à grande échelle chez Google, Meta et Microsoft ont depuis des années été faites avec des outils de refactoring déterministes (moteurs de réécriture, jscodeshift, transformations style gofmt, Comby, OpenRewrite), avec les LLM utilisés sélectivement pour la longue traîne des patterns que les recettes déterministes ne peuvent pas exprimer. Le cadrage dans la presse tech de « l'IA remplace le refactoring de code » prend ça à l'envers : à l'échelle, l'assist IA est dans l'écriture des recettes et la gestion des cas limites, pas dans la passe de transformation en gros. L'économie favorise aussi le déterminisme pour une migration unique : écrire une recette est un coût fixe qui s'amortit à travers 75 000 fichiers, alors que faire tourner un LLM sur 75 000 fichiers est un coût variable qui passe à l'échelle linéairement et produit une sortie que tu dois quand même vérifier.

Pour les équipes builder, le point d'action est de penser à tes tâches de refactoring en trois seaux. Premièrement, les transformations de patterns mécaniques avec des règles finies bien définies : renommer une API, mise à jour d'imports, échange d'annotations, migration de version JUnit. Ça appartient à l'outillage AST déterministe, point final, et le compte rendu d'Uber est la plus claire étude de cas récente de ce à quoi ça ressemble à l'échelle. Deuxièmement, les refactorings sémantiques avec des appels de jugement : extraire une abstraction, renommer pour la clarté, restructurer le flot de contrôle. C'est là que les outils de codage assistés par IA gagnent leur croûte, parce que les éditions sont locales, révisables, et la flexibilité du LLM aide là où les recettes rigides cassent. Troisièmement, le travail de correction de bugs ou de fonctionnalités avec du refactoring intégré : c'est le sweet spot des outils de codage agentiques, où le modèle peut lire le contexte autour et s'adapter. L'erreur à éviter c'est utiliser un outil d'un seau pour une job d'un autre. Le choix d'Uber de livrer 1,25 M de lignes de migration mécanique sur OpenRewrite, avec une boucle CI déterministe et une orchestration parallèle, est la bonne réponse pour le seau un, et ça vaut la peine de garder ça en tête la prochaine fois que quelqu'un propose de balancer Claude ou GPT sur un refactor d'un million de lignes.