Le refactoring bien fait : comment l'analyse de programme rend les agents d'IA sécuritaires et fiables

Vous demandez à votre assistant de programmation par IA de faire quelque chose de simple — renommer une fonction ou déplacer un fichier — et voilà que vous êtes soudainement en mode de récupération. Les importations brisent ou les références pointent vers des fichiers qui n'existent plus. Une base de code qui compilait il y a cinq minutes commence à générer des erreurs partout. Ce qui aurait dû être un refactoring de 20 secondes se transforme en une séance de débogage et de nettoyage de 5 minutes.

Pourquoi le refactoring est difficile pour les agents

Le refactoring n'est pas simplement une recherche-remplacement à grande échelle — c'est un problème de parcours de graphe à travers la structure sémantique de votre base de code. Lorsque vous renommez une fonction, les changements se propagent en cascade : chaque site d'appel à travers l'espace de travail, les définitions de type et les interfaces qui y font référence, les instructions d'importation/exportation, les tests et (facultativement) la documentation et les commentaires. Déplacer un fichier déclenche une répercussion encore plus complexe, affectant les chemins d'importation dans chaque fichier dépendant, les fichiers barils (index.ts) et les réexportations, les hypothèses de résolution de module intégrées dans les chemins tsconfig et les configurations de bundler, ainsi que des fichiers de configuration dispersés comme la configuration de Webpack, pour n'en nommer que quelques-uns. Voici l'incompatibilité fondamentale : les grands modèles de langage excellent à générer du code plausible par appariement de motifs, mais le refactoring exige la précision plutôt que la plausibilité. Ce n'est pas une tâche créative — c'est un problème de satisfaction de contraintes qui nécessite une compréhension exacte des relations entre symboles, des sémantiques propres à chaque langage et du graphe de dépendances du projet. Un agent qui « semble correct » mais qui manque une importation dans un module profondément imbriqué n'a pas simplement fait une erreur mineure; il a introduit une défaillance d'exécution qui ne se manifestera qu'en production. C'est pourquoi la génération de texte, peu importe sa sophistication, est un outil peu fiable pour la transformation structurelle du code.

Le problème : quand les agents travaillent plus fort, pas plus intelligemment

De nombreux agents d'IA trébuchent avec le refactoring parce qu'ils traitent les modifications structurelles comme des modifications de texte. Voici quelques modes de défaillance que les développeurs rencontrent constamment :

La demande : « Renomme cette méthode. »

L'échec traditionnel : L'agent a mis à jour la définition de la méthode, mais a manqué les sites d'appel à travers le projet. Même lorsque la requête demandait explicitement de mettre à jour les références, le processus s'est transformé en une boucle lente et sujette aux erreurs : rechercher l'ancien nom et le remplacer. Prenons cette requête : renomme get_loose_identifier dans expression.js pour mieux refléter ce qu'il fait. Renommer ce symbole se propage à quatre fichiers, touchant huit références et trois importations. Le côté gauche de la figure suivante (Approche traditionnelle) montre comment cette opération se déroule sans outil de refactoring dédié : après avoir renommé le symbole dans le premier fichier (expression.js), l'agent recherche dans la base de code get_loose_identifier et met à jour CallExpression.js et AssignmentExpression.js par le biais de multiples appels de grand modèle de langage et invocations d'outils. Malgré cet effort, il manque encore les références restantes.

Comment Kiro gère ceci : Considérez ce que les développeurs feraient manuellement pour accomplir cette tâche dans un IDE. Ils appuieraient sur F2 sur get_loose_identifier, taperaient le nouveau nom et appuieraient sur entrée. L'IDE effectuerait automatiquement le renommage tout en mettant à jour les huit références et les trois importations à travers la base de code. C'est précisément ce que fait un outil de renommage sémantique. Le côté droit de la figure suivante (Nouvelle approche) montre comment Kiro effectue correctement l'ensemble du renommage en une seule invocation d'outil.

Chargement de l'image...Comparaison côte à côte de l'approche traditionnelle et de la nouvelle approche de refactoring montrant les listes de fichiers touchés

La demande : « Corrige les erreurs de lint dans ce fichier »

L'échec traditionnel : L'agent a traité la sortie du linter comme une liste de tâches de modifications de texte. Il a renommé les noms de fonctions dans les signatures de camelCase à snake_case dans un fichier, mais a introduit des erreurs « référence manquante » et « importation manquante » dans d'autres fichiers. Il n'a pas réussi à propager les changements à toutes les utilisations.

Comment Kiro gère ceci : Voici un exemple montrant comment un agent bénéficie de l'outil de renommage sémantique même si l'utilisateur ne demande pas directement un renommage. L'utilisateur demande à l'agent de « corriger les erreurs de lint dans text_helpers.py ». Les erreurs de lint indiquent que normalizeText et slugifyTitle dans utils/text_helpers.py doivent passer en snake_case. Un extrait partiel de la base de code est présenté ci-dessous :

Chargement de l'image...corriger les erreurs de lint dans text_helpers.py

Un agent qui traite ces corrections comme des modifications de texte renommera les définitions de fonctions et pourrait corriger les références locales, mais il manquera probablement les importations et les sites d'appel ailleurs, causant des erreurs ImportError/NameError à l'exécution. En utilisant l'outil de renommage sémantique, Kiro met à jour les définitions ainsi que les importations et les appels dans api/routes.py et services/indexer.py, comme illustré dans l'image ci-dessous.

Chargement de l'image...normalize_text mis en évidence en snake_case dans des extraits de code

La demande : « Réorganisons nos composants - déplace Button.tsx de src/components/ vers src/shared/ui/ »

L'échec traditionnel : L'agent a traité la tâche comme une simple opération de fichier. Le fichier a été déplacé avec succès, mais maintenant chaque instruction d'importation pointant vers l'ancien emplacement est brisée. L'agent a ensuite tenté de corriger les importations fichier par fichier avec des opérations de recherche-remplacement, mais a manqué les importations dynamiques : import('../components/Button').

Comment Kiro gère ceci : Voici un exemple concret montrant comment Kiro met automatiquement à jour les chemins d'importation. Le diagramme montre un extrait partiel de la structure du projet et certains des extraits de code dépendants :

Chargement de l'image...diagramme de la structure du projet et extraits de code montrant l'importation ../components/Button

Après avoir déplacé Button.tsx de src/components/ vers src/shared/ui/, Kiro met automatiquement à jour toutes les instructions d'importation impliquant le fichier déplacé.

Chargement de l'image...fichier shared/ui/Button.tsx mis en évidence et mis à jour dans les importations et l'arborescence de fichiers

Avantages clés :

  • Aucune recherche-remplacement manuelle nécessaire, car le serveur de langage intégré gère les modifications.
  • Sensible au langage : comprend la résolution de modules TypeScript/JavaScript.
  • Plus sécuritaire : moins susceptible de briser le code fonctionnel.
  • Gère les cas particuliers : fonctionne avec les alias de chemin, les monorepos et plus encore.

C'est exactement ce qui se produit lorsque vous faites un glisser-déposer d'un fichier dans l'Explorateur de VSCode. L'outil de renommage sémantique en est l'équivalent agentif!

Comment les agents Kiro effectuent le refactoring

Les IDE avaient déjà résolu ce problème avant l'essor de l'IA agentive. Lorsque vous appuyez sur F2 pour renommer un symbole dans VSCode, l'IDE ne devine pas. Il consulte le serveur de langage qui comprend la structure de votre code, calcule une modification à l'échelle de l'espace de travail et l'applique de manière sécuritaire. Les capacités de modification de l'espace de travail de VSCode permettent une recherche-remplacement programmable et sémantique qui comprend la structure de votre code plutôt que de simples motifs de texte. L'agent Kiro ne tente pas de simuler le refactoring uniquement par le raisonnement du grand modèle de langage. Au lieu de cela, l'agent utilise le même mécanisme décrit ci-dessus pour enregistrer deux nouveaux outils de refactoring qui exposent ces capacités d'IDE éprouvées de manière programmatique. Lorsque l'agent doit renommer un symbole ou déplacer un fichier, il reconnaît intelligemment l'intention, sélectionne l'outil de refactoring approprié et l'invoque. L'agent orchestre le flux de travail de refactoring pendant que le serveur de langage de l'IDE aide à valider l'exactitude.

Voyons comment ces outils de refactoring enregistrés par l'agent fonctionnent en coulisses.

Outil de renommage sémantique : le renommage bien fait

Cet outil s'appuie directement sur l'API de renommage de symboles de VSCode; la même que vous utilisez lorsque vous appuyez sur F2. Il utilise vscode.prepareRename pour valider que le symbole est renommable (par exemple, qu'il ne s'agit pas d'un mot-clé) et vscode.executeDocumentRenameProvider pour générer une modification de l'espace de travail avec tous les changements nécessaires à travers l'espace de travail. Pour TypeScript, JavaScript, TSX et JSX, les fournisseurs de renommage intégrés de VSCode gèrent tout. Pour Python, Go, Java et autres, l'outil s'appuie sur vos extensions de langage installées et les serveurs de langage qu'elles fournissent.

L'outil de relocalisation intelligente : déplacer des fichiers sans tout briser

Cet outil utilise les capacités de déplacement de fichiers de VSCode pour relocaliser des fichiers tout en mettant automatiquement à jour toutes les références. C'est l'équivalent programmatique du glisser-déposer dans l'explorateur de VSCode, sauf que l'agent peut le faire pour vous. En utilisant vscode.WorkspaceEdit.renameFile et vscode.workspace.applyEdit, l'outil génère des modifications complètes à travers plusieurs fichiers et met à jour les importations touchées.

Chargement de l'image...diagramme montrant la fonction de renommage avant/après en recherchant dans la base de code, en modifiant des fichiers et en bouclant sur des importations brisées, ou de manière similaire à une opération de renommage sémantique

Pourquoi cela est important

La précision plutôt que la créativité : Le refactoring n'a pas besoin d'un grand modèle de langage pour imaginer à quoi le code devrait ressembler. Il a besoin d'outils qui comprennent ce que le code est réellement et qui peuvent le modifier chirurgicalement.

La confiance grâce à une infrastructure éprouvée : Ce ne sont pas des fonctionnalités expérimentales de grand modèle de langage, mais plutôt des intégrations directes avec l'infrastructure de refactoring sur laquelle les développeurs comptent déjà quotidiennement. Si ça fonctionne lorsque vous appuyez sur F2, ça fonctionne lorsque l'agent le fait.

Indépendant du langage : Puisque le gros du travail est effectué par les serveurs de langage, l'approche se généralise à travers les piles technologiques et les langages.

Maintenir la productivité : Un refactoring manuel de 20 secondes ne devrait pas se transformer en une mission de récupération de 5 minutes générée par l'IA. Avec des outils appropriés, l'opération reste rapide et atomique.

La vue d'ensemble

En nous appuyant sur notre philosophie de l'exactitude par construction — le même principe qui a guidé notre intégration des diagnostics IDE — nous étendons cette approche pour couvrir tout le spectre des capacités de refactoring de VSCode. Tout comme nous avons intégré des diagnostics en temps réel pour attraper les erreurs avant qu'elles ne s'accumulent, nous avons maintenant étendu ces capacités déterministes éprouvées de l'IDE à nos nouveaux outils internes de relocalisation intelligente et de renommage sémantique.

Mais les capacités de refactoring ne s'arrêtent pas au renommage et à la relocalisation. Les serveurs de langage de VSCode offrent une riche suite de transformations de code automatisées que les agents devraient exploiter : Extraire une méthode/fonction pour sortir des blocs de code en fonctions réutilisables, Intégrer une variable/fonction pour simplifier le code, Modifier la signature pour mettre à jour les paramètres de méthode à travers tous les sites d'appel, et Convertir en fonction fléchée ou d'autres transformations propres à chaque langage sont des candidats de choix.

En adoptant cette approche, nous pouvons intégrer l'exactitude, la sécurité et la fiabilité dans les fondations sur lesquelles les agents fonctionnent. Le modèle que nous avons établi avec ces outils guidera les nouveaux ajouts à notre boîte à outils : au lieu de demander aux grands modèles de langage de générer des scripts de remplacement de texte fragiles, les agents de programmation intelligents continueront d'exploiter ces opérations d'IDE éprouvées auxquelles les développeurs font déjà confiance. Lorsque l'IDE sait comment bien faire les choses, nous le laissons faire le travail. À mesure que les agents deviennent plus capables, c'est une bonne technique pour aussi rendre leurs résultats plus dignes de confiance.

Prêt à découvrir la différence? Commencez avec Kiro gratuitement et voyez comment il peut transformer votre flux de travail de développement. Rejoignez notre communauté grandissante sur Discord pour partager vos commentaires, poser des questions et vous connecter avec d'autres développeurs qui programment avec l'IA.

Remerciements

Merci à Al Harris pour ses idées techniques et ses précieux commentaires.