


Corriger la cause profonde dans une grande base de code et garder l’API publique compatible
À utiliser quand une IA modifie une grande base de code en couches. Garde les journaux de build courts, remonte chaque bug jusqu’à sa cause en amont, relit son diff pour repérer les helpers qui masquent un problème et rédige une pull request claire.
Pour utiliser ces règles d'IA
## Garder les journaux de build courts et lancer le formateur et les tests avant chaque commit
- Lors de la compilation, redirigez toute la sortie de build vers le périphérique nul et ne relancez la même commande en mode verbeux qu'en cas d'échec, afin qu'une compilation réussie ne consomme pas de tokens en journaux. Par exemple : `build-command >/dev/null 2>&1 || build-command`.
- Privilégiez les dépendances mises en cache localement plutôt que les téléchargements réseau, et activez un cache de build ou de compilateur lorsqu'il est disponible pour accélérer les recompilations incrémentales.
- Pour les compilations longues, non surveillées ou pilotées par un agent, désactivez dès la configuration les boîtes de dialogue modales d'abandon ou d'assertion, afin que l'exécution ne puisse pas se bloquer en attendant une intervention humaine.
- Exécutez le formateur avant chaque commit, et utilisez son mode de vérification seule pour contrôler sans modifier les fichiers ; un code qui ne respecte pas le style du projet impose des allers-retours de revue évitables.
- Exécutez la suite de tests depuis la racine du dépôt, et ajoutez les tests de non-régression sous forme de fichiers dans le répertoire de tests du projet. En l'absence de GPU ou de matériel spécial, utilisez les modes de test CPU ou interpréteur plutôt que de sauter la couverture.
- Dans les environnements multi-shell, par exemple un shell Unix appelant des outils hôtes, ajoutez la bonne extension d'exécutable pour que le binaire hôte visé s'exécute, et non un binaire destiné à une autre plateforme.
## Corriger la cause en amont au lieu d’ajouter des garde-fous
- Visez une représentation correcte et robuste par construction, même si cela implique un remaniement plus important que le plus petit diff possible.
- Corrigez les causes profondes, pas les symptômes. Quand un bug apparaît dans une phase tardive comme la génération de sortie, la cause se situe généralement en amont : une transformation antérieure, une légalisation, une spécialisation, un abaissement (lowering), ou la représentation des données elle-même. Remontez jusque-là et corrigez à cet endroit.
- Remettez en question chaque changement avant de le conserver : pourquoi est-il nécessaire, quel test échoue sans lui, et s'agit-il de la bonne correction ou le problème signale-t-il que la représentation est défaillante ? Si vous ne pouvez nommer aucun test qui échoue sans ce changement, il ne devrait probablement pas exister.
- Ne masquez rien. Une garde, une vérification de nullité ou un cas particulier qui camoufle une structure malformée cache un bug de représentation, et une garde jamais déclenchée avec une entrée correcte est du code mort. Rendez la représentation correcte pour que les consommateurs en aval restent simples.
- Interrogez la forme de l'entrée. Chaque fois que vous traitez une forme d'entrée particulière, demandez-vous si cette forme est elle-même correcte, ou si c'est plutôt son producteur en amont qu'il faut corriger ; ne la traitez localement que lorsque la forme constitue réellement une entrée valide.
- Préférez une représentation correcte plutôt qu'une modification minimale. Si deux formes de surface devraient être équivalentes, modélisez-les de façon identique, et accédez aux données clé-valeur conceptuellement non ordonnées par clé ou par rôle plutôt que par position ou par index.
- Tenez un journal de travail tout au long de la tâche : le problème avec un exemple motivant, les blocages, comment une correction en entraîne une autre, la correction retenue et pourquoi elle est rigoureuse, ainsi que les alternatives rejetées. Condensez-le dans la description du changement et gardez-le hors du code commité.
## Relire son diff pour repérer les helpers qui masquent une mauvaise conception
- Avant de finaliser un changement non trivial, examinez le diff à la recherche de signes indiquant qu'il compense une mauvaise représentation, et traitez ces motifs comme des signaux d'alerte tant que vous n'avez pas prouvé qu'ils ont bien leur place à ce niveau.
- Des fonctions utilitaires personnalisées d'équivalence sémantique, comme de nouvelles fonctions récursives « sont-équivalents » ou « correspond-à », signifient souvent que deux représentations alternatives ont été autorisées à coexister ; demandez-vous d'abord pourquoi un chemin existant de canonicalisation, de substitution ou d'égalité ne les rend pas déjà identiques.
- Prolifération non auditée de fonctions utilitaires : considérez toute nouvelle fonction utilitaire, de repli ou de type « essai » comme une cible de revue, en particulier si elle n'existe que pour faire passer un seul test en échec ou si elle réimplémente une partie d'un mécanisme existant.
- La reconstruction de la syntaxe à partir du sémantique, c'est-à-dire reconstruire une syntaxe de surface à partir de données sémantiques déjà vérifiées, signifie généralement que le producteur a préservé la mauvaise représentation ; la valeur sémantique vérifiée devrait faire foi.
- La redécouverte de contexte par parcours de graphe, c'est-à-dire parcourir des chaînes d'opérandes, de références ou de propriété pour retrouver des arguments, des clés ou des parents, relève généralement d'une réparation en aval ; construisez plutôt la forme canonique dès le producteur.
- Rustinage côté consommateur : les phases tardives ne devraient pas corriger des formes malformées issues de phases antérieures, et les détails de représentation codés en dur (cas particuliers pour certaines sous-classes, noms magiques, index d'arguments ou ordre des entrées) nécessitent un invariant solide et relèvent généralement d'une limite de construction canonique.
- Traitement silencieux d'une forme impossible : une garde qui renvoie une valeur par défaut pour une forme hors contrat cache des bugs ; utilisez une assertion sur la forme impossible plutôt que de l'accepter silencieusement.
- Commencez la revue par un inventaire des fonctions utilitaires suspectes, listant chaque nouvelle fonction, le mécanisme existant qu'elle recoupe, et le test qui échoue sans elle. Pour chaque changement signalé, identifiez la forme d'entrée exacte et son producteur, déterminez si cette forme est canonique ou une alternative accidentelle, essayez d'abord la correction côté producteur, identifiez la source de vérité existante, et effectuez l'exercice du retour en arrière : retirez le changement, exécutez le plus petit test en échec, et retracez la véritable rupture. Ne conservez pas un changement signalé simplement parce qu'il fait passer les tests.
## Écrire des commentaires avec exemples et garder chaque information à un seul endroit
- Rédigez les commentaires de fonction sous forme de phrases complètes : indiquez d'abord ce que fait la fonction, puis pourquoi elle existe si ce n'est pas évident, et incluez un exemple concret pour tout comportement non trivial plutôt qu'un fragment laconique.
- Utilisez des exemples sous forme de discussion dans les commentaires et les explications. Préférez « Prenons cet exemple : » suivi de code réel, puis décrivez étape par étape ce qui se passe, quelle passe crée la forme, quel invariant le code local préserve, et quel consommateur en dépend, plutôt que des étiquettes abstraites utilisées en guise d'explication.
- Réutilisez avant d'écrire, puis extrayez la logique non triviale dans une fonction utilitaire nommée et documentée. Cherchez d'abord dans les utilitaires partagés ; quand la logique est réellement nouvelle, donnez-lui un nom révélateur de son intention et un commentaire de documentation plutôt que d'enfouir un calcul à plusieurs étapes dans un bloc en ligne.
- Gardez une seule source de vérité et supprimez le code mort après un refactor : mappez ou classifiez une chose donnée à un seul et unique endroit, et lorsqu'un changement rend une branche ou une fonction utilitaire inatteignable, supprimez-la plutôt que de la laisser en place.
- Gardez une seule représentation canonique par valeur et affirmez l'invariant. Une seconde représentation de la même valeur logique brise l'égalité, l'identité et la déduplication ; placez une assertion au site de construction lorsqu'un invariant garantit qu'une forme n'est jamais produite pour certaines entrées.
- Échouez bruyamment face à une entrée hors contrat. Quand une fonction utilitaire n'est valide que pour un ensemble restreint d'entrées, utilisez une assertion sur tout ce qui sort de cet ensemble plutôt que de renvoyer silencieusement une valeur par défaut.
## Rédiger la pull request en cinq parties avec des tests de régression
- Formatez le code, puis qualifiez le changement de non cassant par défaut, ou de cassant lorsqu'il modifie une interface binaire ou un comportement du langage.
- Incluez des tests de non-régression qui auraient échoué avant la correction.
- Rédigez la description selon une structure fixe en cinq parties : motivation (le problème avec un exemple concret et motivant), solution proposée (l'approche retenue et pourquoi elle est rigoureuse), résumé des changements (une liste des fichiers ou zones touchés et ce que fait chacun), concepts et vocabulaire (un court glossaire limité aux termes spécifiques à la base de code ou subtils sur lesquels s'appuie le rapport, pas les notions bien connues), et un compte rendu du processus.
- Dans le compte rendu du processus, expliquez chaque changement avec une raison logique. Pour un problème en cascade, donnez le test qui l'a motivé et une trace de code nommant les fonctions exactes concernées, pas seulement une description. Pour tout changement qui garde ou traite comme cas particulier une forme d'entrée, précisez si cette forme est une entrée valide ou si son producteur aurait dû être corrigé, afin qu'un relecteur puisse confirmer que la correction se situe au bon niveau.
- Écrivez pour un relecteur qui n'a pas votre contexte : partez d'un exemple concret, incluez l'extrait de code pertinent plutôt que de simplement nommer un type ou une fonction, et expliquez les étapes dans l'ordre.
## Ajouter à la fin des enums et des interfaces pour ne pas casser l’API publique ni l’ABI
- Traitez tout changement à un en-tête d'API publique comme devant préserver la compatibilité binaire et source pour les appelants déjà compilés avec une version antérieure.
- Énumérations : n'insérez jamais un nouvel énumérateur au milieu d'une énumération existante, car l'insertion décale toutes les valeurs suivantes et casse silencieusement les appelants qui l'ont stockée ou comparée. Ajoutez les nouveaux énumérateurs juste avant le membre de comptage final ou sentinelle, avec une valeur séquentielle explicite, et pour en retirer un, renommez-le sous une forme marquant sa suppression tout en conservant son entier d'origine, sans jamais réutiliser une valeur retirée.
- Les tables de méthodes virtuelles des interfaces de style COM ont une disposition fixée par l'ordre de déclaration : ne réordonnez donc jamais les méthodes virtuelles, ne changez jamais la signature ou la convention d'appel d'une méthode, n'insérez jamais une nouvelle méthode au milieu (ajoutez-la à la fin, avant l'accolade fermante), et ne supprimez jamais une méthode : remplacez son corps par un stub non implémenté tout en gardant la déclaration.
- Évitez d'étendre sur place une interface publique existante lorsque des clients peuvent l'implémenter ou l'interroger par identifiant ; préférez ajouter une nouvelle interface dérivée ou versionnée avec son propre identifiant, tout en maintenant l'originale pour les appelants existants.
