Refactor pass (Passage de refactoring)
Autrefois, le refactoring commençait par une intuition. Le code était difficile à travailler : une classe que vous parcouriez pour trouver où mettre la nouvelle fonctionnalité, une fonction que vous gardiez en tête pour modifier une branche, une fonctionnalité qui nécessitait de visiter huit fichiers. La même intuition vous disait quoi laisser tranquille. Un code moche que vous ne touchiez jamais ne faisait pas de mal, alors vous le laissiez tranquille.
Un agent qui écrit du code ne ressent rien de tout cela, pas plus qu'un développeur qui lit surtout ce que l'agent a écrit. Les odeurs s'accumulent donc sans être remarquées. Un agent ajoutera une trentième branche à une fonction sans se plaindre. Et lorsqu'on lui demande de refactorer, il nettoie ce qui semble désordonné, y compris le code que personne ne touche. Cette compétence redonne les deux moitiés de l'intuition, sous la forme de trois choses mesurables :
- Là où vous le sentiriez : la carte de chaleur. La friction n'importe que là où les modifications atterrissent. Dans les études, la taille du fichier et sa fréquence de modification prédisent mieux les problèmes de maintenance que n'importe quelle odeur individuelle.
- Ce que vous sentiriez : les odeurs. Le catalogue établi des odeurs, chacune traduite en ce qu'il faut compter, quand ce n'est pas une odeur, et le répertoire des refactorings nommés que des décennies de pratique ont construits pour elle : references/smells.md.
- Combien vous le sentiriez : le parcours. Prenez la prochaine modification à venir, planifiée ou probable, et listez tout ce qu'elle toucherait.
Ensuite, le verdict combine où et quoi :
| Odeurs | Ne sent pas | |
|---|---|---|
| Change souvent | Refactorer | Dire que c'est bon |
| Change rarement | Laisser ; au maximum une note "Plus tard" | Rien |
La case "Dire que c'est bon" est aussi importante que la case "Refactorer". C'est la moitié de retenue de l'intuition, et le rapport le montre.
Deux tailles de question. Un passage couvre l'ensemble du dépôt : étapes 1 à 7. Une question sur une zone ("devrions-nous refactorer X ?") exécute les mêmes étapes sur cette zone uniquement. Elle saute la checklist sauf s'il y a plusieurs éléments, et sa réponse peut être "non". Dans les deux cas, exécutez d'abord le script de l'étape 1 avec --checklists seul : s'il liste une checklist d'un passage précédent, lisez Après un passage précédent d'abord.
1. Où : la carte de chaleur
python3 <dossier de cette compétence>/scripts/heatmap.py -C <depot> <portee ...>
<dossier de cette compétence> est le dossier qui contient ce SKILL.md. Là où python3 n'est pas la commande, comme souvent sur Windows, utilisez python ou py.
- Portée : les dossiers source et les dossiers de tests, comme
src testsouapp, pas la documentation ni les ressources. Lisez le README du projet, une liste de répertoires et les instructions de l'agent (AGENTS.md,CLAUDE.md) pour décider. Les fichiers de test, identifiés par leur nom (Board.spec.ts,test_x.py,XTest.php...) ou dossier, restent hors du tableau et des paires, et sont listés à part lorsqu'ils changent avec plusieurs fichiers. Ajoutez--test GLOBpour d'autres noms de test,--not-test GLOBpour un fichier qui y ressemble seulement (la carte de chaleur nomme ceux dont elle doute), et--ignore GLOBpour les fichiers générés. - Fenêtre : par défaut les 200 derniers commits.
--sincela définit à la place lorsque l'utilisateur nomme un point de départ ("depuis v2.0", "depuis que nous avons ajouté la facturation", "les trois derniers mois") : un tag, une branche ou un commit, ou une date. Les commits des bots, les reformatages en masse qu'un dépôt liste dans.git-blame-ignore-revset les commits enregistrés dans les checklists précédentes sont automatiquement exclus.--exclude-commitsexclut d'autres commits qui rendraient tout ce qu'ils touchent chaud, comme une importation de dépendance externe. - Trop peu ? Avec moins d'environ 30 commits dans la fenêtre, les comptages ne peuvent pas distinguer les fichiers chauds des froids. Élargissez la fenêtre, ou dites que l'historique est trop court pour pointer quoi que ce soit et jugez uniquement par les odeurs. Un clone superficiel (la carte de chaleur le dit) nécessite
git fetch --unshallow; demandez à l'utilisateur.
--help explique chaque colonne et option.
Lecture
- Un point chaud a beaucoup de commits et est aussi gros, donc la liste les met en premier : triés par commits multipliés par taille. Beaucoup de commits sur un petit fichier est souvent acceptable, comme une configuration éditée pour chaque fonctionnalité. Un gros fichier que personne ne touche est économiquement laissé tranquille.
- Encore chaud ?
recentcompte ses commits dans la moitié la plus récente de la fenêtre (la moitié des commits, pas la moitié du temps), etlastest la date de son dernier commit. Lisez les deux par rapport aux dates de la fenêtre : huit commitsrecent, tous le jour où la moitié la plus récente commence, sont une poussée qui s'est refroidie. Un fichier qui a rétréci a peut-être déjà été traité. relest le nombre de lignes modifiées par rapport à la taille du fichier. Le churn relatif prédit bien mieux les défauts que le churn absolu. Bien au-dessus de 1 signifie que le fichier a été réécrit ou a grandi dans la fenêtre. Lisez-le avec growth : un fichier qui ne fait que grandir, commit après commit, est Autoritaire-Only Growth. Un fichier né dans la fenêtre a une croissance égale à sa taille.- Les paires sont des fichiers qui changent souvent dans les mêmes commits. Un groupe est trois fichiers ou plus qui s'apparient tous entre eux, comptés par les commits qui les ont tous modifiés ; les groupes qui se chevauchent sont listés ensemble comme une zone.
- Les fichiers de différentes couches qui s'apparient fortement, ou un groupe traversant les couches, signifient généralement une fonctionnalité répartie entre elles (Chirurgie par dispersion).
- Vérifiez ce qui lie chaque paire. Une importation, un appel, ou un contrat nommé par quelque chose (une route, un schéma, le fichier de type entre un contrôleur et sa page) est une dépendance visible : une fonctionnalité à travers une frontière. Les fichiers qui changent ensemble sans aucun lien de ce type, comme deux copies d'une règle ou deux listes maintenues en phase manuellement, ont une dépendance cachée, le type de couplage le plus fort.
- Un fichier qui s'apparie avec beaucoup d'autres est un hub que chaque changement traverse.
- Un test listé comme changeant avec plusieurs fichiers en plus des siens est lié à leurs internes et bloquera le refactoring, sauf si ces fichiers sont un groupe que le test suit seulement.
- Si beaucoup de commits étaient trop gros pour être appariés (
--max-files), comme dans un dépôt qui fusionne par squash, augmentez-le ou lisez les paires comme partielles.
- Pourquoi ça change. Lisez les messages de commit des premiers fichiers (
--show) et regroupez-les par fonctionnalité. De nombreuses raisons indépendantes de modifier un seul fichier est un Changement Divergent, et cela se montre ici plus directement que dans n'importe quel comptage.
2. Quoi : les odeurs
- Mesurez les fichiers chauds par rapport au codebase lui-même, dans une seule langue.
python3 <dossier de cette compétence>/scripts/measure.py -C <depot> <portee ...>place chaque fichier dans une bande selon les 70e, 80e et 90e percentiles de sa langue, et--show <fichier>montre chacune de ses fonctions. "Gros" est la bande 80e ou 90e (les bandes appliquent également le seuil bas du catalogue) ; le 70e mérite un examen plus attentif. Là où il est dit que les percentiles penchent sur un fichier, jugez également par les points de référence dans Mesure, qui explique aussi comment compter le reste (branches, paramètres, cohésion), ou utiliser le linter du projet. - Lisez vous-même les deux ou trois fichiers centraux. Les chiffres pointent ; la lecture décide.
- Examinez les autres zones chaudes. Si vous pouvez exécuter des sous-agents, donnez à deux ou trois d'entre eux une zone chacun (une couche ou un dossier, afin que chacun lise une zone entière une fois) et exécutez-les en parallèle. Briefez chacun à partir de references/review-brief.md. Sans sous-agents, examinez les zones vous-même, une à la fois, en vous basant sur le même brief.
- Nommez chaque odeur avec son nom établi (Fonction Longue, Chirurgie par Dispersion, Commutations Répétées…), et vérifiez-la par rapport à l'entrée "Pas une odeur quand" avant qu'elle compte. Le nom est l'index de son répertoire.
- Cherchez l'idée manquante. Les odeurs voyagent en grappes, et une grappe pointe généralement vers une chose que le code utilise tout le temps mais n'a jamais nommé : un état, la définition d'un type, une source unique pour un paramètre. Voir Les odeurs voyagent ensemble.
- Vérifiez quels tests atteignent le code chaud : voir Code non testé.
- Les bogues que vous remarquez vont sur la liste comme éléments F, pas corrigés maintenant. Un bogue pas encore reproduit est un soupçon : titrez-le comme tel ("Vérifier : les modifications ignorent la règle du week-end"), avec les preuves dans sa note. Sa première étape est un test qui échoue ; si aucun n'échoue, il est sauté, avec ce que vous avez trouvé.
3. Combien : le parcours
C'est la chose la plus proche de l'ancien sentiment. C'est aussi ce qui rend un élément convaincant.
- Trouvez les prochains changements. Un changement planifié l'emporte sur un changement déduit, donc prenez-les d'abord à partir de ce que l'utilisateur a dit arriver ("avant de commencer le chunk 7"), puis à partir du plan ou de la feuille de route du projet, s'il en a un, puis à partir des messages de commit : les types de changements qui reviennent le plus souvent ("ajouter un nouveau type de rapport", "ajouter un paramètre", "ajouter un écran"). Prenez les deux ou trois types les plus courants, pas un seul.
- Pour chaque type, prenez le dernier commit réel correspondant et listez ce qu'il a touché (
git show --stat). S'il est plus ancien que la fenêtre, utilisez-le quand même et donnez sa date ; le parcours, sur le code d'aujourd'hui, montre ce qui a changé depuis. Si la documentation du projet ou les instructions de l'agent contiennent une recette pour ce changement, comptez ses étapes. Une recette en cinq étapes pour ajouter une chose est un aveu. - Parcourez le prochain changement, sans l'écrire. Listez chaque fichier que vous devriez lire, chaque endroit que vous modifieriez, et chaque copie que vous devriez maintenir en phase.
- Notez le nombre aujourd'hui et le nombre après le refactoring que vous proposeriez : "un nouveau type de rapport touche 7 fichiers et 3 commutateurs aujourd'hui, et toucherait 1 fichier après".
Un fichier froid que des travaux prévus toucheront n'est plus froid : mettez-le dans la ligne supérieure du tableau, puisque le prochain changement paye ce qu'il coûte. Si le prochain changement est bon marché aujourd'hui, l'odeur coûte peu, quoi qu'elle ressemble.
4. Vérifiez chaque constatation
La constatation d'un relecteur est une piste, pas un fait, et il en va de même pour vos propres premières impressions. Avant que quoi que ce soit aille sur la liste, ouvrez les lignes citées, réexécutez les comptages, et supprimez ou corrigez ce qui ne tient pas. Dites dans le rapport ce que vous avez vérifié. Un élément erroné qui envoie l'utilisateur dans un refactoring coûte bien plus que la minute qu'il faut pour le vérifier.
Ensuite, appliquez la retenue. Un relecteur qu'on charge de trouver des problèmes en trouvera toujours, et corriger chaque constatation laisse des couches supplémentaires et des tests pour des cas qui ne peuvent pas arriver. Laissez de côté ce qui est froid, ce que les lignes "Pas une odeur quand" excusent, et ce que le parcours dit être bon marché.
5. Classez
Donnez à chaque élément un ID stable, car l'utilisateur nommera les éléments par celui-ci ("commencer R4"), peut-être dans une session ultérieure. Les noms entre crochets sont les champs de l'élément sur la checklist (étape 6) ; les éléments F, L et D disent le leur dans une note.
- R1, R2… Refactorings, dans l'ordre suggéré. Pour chacun :
- un titre nommant l'idée manquante, comme un résultat ("Nommer les états d'un paiement", pas "Refactorer CheckoutController") (
title) ; - les odeurs par nom, avec des nombres et
fichier:ligne(smells) ; - le type de changement qu'il facilite, de l'étape 3 (
eases), et le coût de ce changement (cost:todayetafter) ; - le plan, comme les étapes du répertoire (
plan) ; - les fichiers, taille (S, M ou L) et risque (Faible, Moyen ou Élevé) (
files,size,risk) ; - comment vérifier que le comportement n'a pas changé (
verify).
- un titre nommant l'idée manquante, comme un résultat ("Nommer les états d'un paiement", pas "Refactorer CheckoutController") (
- F1… Petites corrections trouvées en cours de route, chacune son propre commit.
- L1… Plus tard : odorant mais froid, ou à faire quand le code voisin sera touché la prochaine fois.
- D1… À la discrétion de l'utilisateur : changements dans la façon dont ils travaillent, comme une convention de documentation qui rend chaque changement coûteux. Exposez le compromis, pas une décision.
Classez par rapport bénéfice/risque. Le bénéfice est combien le changement facilite le travail futur : la carte de chaleur et le parcours en sont les preuves. Les priorités des odeurs disent quels types tendent à compter. Le code long et complexe, et les changements répartis sur plusieurs fichiers, comptent le plus. Les odeurs de couplage comme l'Envie de Fonctionnalité et l'Homme du Milieu comptent le moins, et sont corrigées en passant.
Ordonnez la liste de sorte que :
- les bloqueurs viennent en premier : les tests liés aux internes qui feraient que chaque étape ultérieure semble casser quelque chose, et le code qu'aucun test ne couvre, dont l'élément commence par épingler des tests ou dit clairement qu'il ne peut pas être vérifié (étape 7) ;
- les petits éléments qui suppriment un chemin de copier-coller viennent ensuite ;
- les grands mouvements viennent après, car ils nécessitent souvent d'abord une couture (seam) ;
- tout ce que vous ne pouvez pas vérifier vous-même (comment ça sonne, se sent ou se voit) vient en dernier, car l'utilisateur doit le vérifier.
6. Écrivez la checklist et le rapport
Réexécutez la commande de l'étape 1 avec --page .refactor-pass/<YYYY-MM-DD>.html, à la date d'aujourd'hui. Elle crée la checklist à partir de assets/page.html et écrit la carte de chaleur dans son bloc heatmap ; réexécutez-la, elle rafraîchit la carte de chaleur et rien d'autre. Vous écrivez le bloc pass en dessous, dont project, started, base et updated sont déjà remplis :
summary: l'état général en deux ou trois phrases ;items: chaque élément, avec les champs de l'étape 5, unstatus(todo,doing,waitingpour l'utilisateur,doneouskippedavec la raison dans son outcome), etcommitsetoutcome, vides pour un nouvel élément ; un élément reporté conserve sonfrom. Chaque élément commence par son ID et son statut sur une seule ligne,{"id": "R1", "status": "todo",, avec ses autres champs après, comme le commentaire au-dessus des blocs le montre en détail ; pas comme un pretty-printer JSON le disposerait, car les mises à jour de statut de l'étape 7 modifient cette seule ligne ;left_alone: chacun avecwhatetwhy;checks: les vérifications du projet, à partir de ses instructions, ou ce que vous vérifierez manuellement s'il n'en a pas ;earlier: après un passage précédent, sachecklistet sesresults, un par refactoring terminé dessus : sonid,title,easesetcost, le vraichangequi est venu ensuite et ce qu'il aactuallement touché (tous vides si aucun n'est encore venu), et unenote. Sinonnull;log: chacun avec unedateet untext, le plus récent en premier.
Après chaque modification, exécutez python3 <dossier de cette compétence>/scripts/check_page.py <page>, qui détecte une parenthèse cassée, un champ mal orthographié, un ID en double ou un élément qui ne commence pas sur une seule ligne. Si votre environnement peut publier ou prévisualiser des pages HTML, p…
[tronqué pour la traduction]