Le problème de la revue

Le conseil standard pour le code généré par AI, c’est : « relisez-le soigneusement ».

Ce conseil est juste et inutile à grande échelle.

Un développeur qui relit une sortie AI attrape les problèmes quand il est alerte, connaît bien le domaine et n’est pas sous pression temporelle. Dans toutes les autres conditions — c’est-à-dire la plupart des conditions — des choses passent entre les mailles du filet.

Une AI qui relit des problèmes générés par AI est encore moins fiable. Vous demandez à un système probabiliste de vérifier la sortie d’un autre système probabiliste. Les modes d’échec sont corrélés.

La seule approche qui passe à l’échelle, c’est l’enforcement déterministe. Des règles vérifiées à chaque commit, que la fatigue ou l’optimisme ne peuvent pas contourner, et qui cassent le build lorsqu’elles sont violées.

Ce que veut dire « déterministe »

Déterministe signifie que la vérification produit le même résultat à chaque fois, peu importe qui l’exécute ou quand.

  • Une règle de linter est déterministe.
  • Une vérification de types est déterministe.
  • Une restriction d’import aux boundaries est déterministe.
  • Un contract test est déterministe.
  • La capacité d’attention d’un relecteur humain ne l’est pas.
  • L’évaluation d’un LLM ne l’est pas.

Cette distinction compte encore plus dans les codebases générées par AI parce que le volume de code généré dépasse ce qu’une équipe peut relire manuellement. Vous ne pouvez pas faire passer la revue à l’échelle linéairement avec la vitesse de génération. Vous pouvez faire passer les deterministic checks à l’échelle presque trivialement.

La pile de garde-fous

Pour les codebases générées par AI, la pile de garde-fous comporte quatre couches :

Couche 1 : le système de types

Le système de types est le premier garde-fou. Des types stricts attrapent une classe d’erreurs qu’aucun processus de revue — humain ou AI — n’attrape de manière cohérente :

  • Violations de null
  • Incompatibilités d’interface
  • Gestion de cas manquante
  • Mauvais types d’arguments

Si le projet utilise TypeScript, strict: true est non négociable. S’il utilise un langage sans système de types solide, ajoutez-en un via des outils ou choisissez un autre langage.

Couche 2 : les règles d’architecture

Les règles d’architecture font respecter la discipline des boundaries :

  • Aucun écran n’importe les internals d’un autre écran.
  • Aucune logique de domaine n’importe directement l’infrastructure.
  • Aucun module ne contourne le composition root pour accéder aux dépendances.
  • Aucun SDK fournisseur n’apparaît en dehors de son module d’adaptation.

Des outils comme Semgrep, ArchUnit, dependency-cruiser ou des règles ESLint personnalisées peuvent appliquer cela statiquement. La clé, c’est qu’ils tournent en CI et cassent le build. Un avertissement que les développeurs peuvent ignorer n’est pas un garde-fou.

Couche 3 : les contract tests

Les contract tests vérifient que les modules satisfont leurs interfaces sans exécuter le système complet :

  • L’adaptateur d’auth satisfait l’interface d’auth.
  • L’adaptateur d’analytics satisfait l’interface d’analytics.
  • L’adaptateur de storage satisfait l’interface de storage.

Ils s’exécutent vite, testent les boundaries d’intégration et attrapent le mode d’échec précis où du code généré par AI satisfait la signature de type mais viole le contract comportemental.

Couche 4 : les vérifications d’intégration déterministes

Au niveau de l’intégration, les vérifications contrôlent des propriétés globales du système :

  • Le composition root résout toutes les dépendances sans erreurs runtime.
  • Le graphe de dépendances ne contient aucun cycle.
  • Toutes les variables d’environnement requises sont déclarées.
  • La configuration est valide au build time, pas seulement au runtime.

Semgrep pour le code généré par AI

Semgrep mérite une mention particulière parce qu’il excelle à exprimer des règles d’architecture sous forme de code :

  • Pattern-matching sur la structure AST, pas sur des chaînes de caractères.
  • Règles personnalisées par projet, pas seulement du linting générique.
  • Suffisamment rapide pour s’exécuter à chaque commit.
  • Suffisamment expressif pour encoder des violations de boundaries.

Une équipe qui utilise intensivement la génération AI devrait maintenir un jeu de règles Semgrep qui encode ses boundaries d’architecture. Quand l’AI génère du code qui viole une boundary, le build échoue avant le merge. Aucune attention humaine requise.

Le sujet n’est pas d’attraper des bugs dans la sortie AI. Le sujet est de rendre les violations structurelles impossibles à merger, quelle que soit la manière dont elles ont été produites.

Ce que la revue de code AI attrape réellement

Les outils de revue de code AI sont utiles pour :

  • La cohérence de style
  • Les trous dans la documentation
  • Les erreurs de logique évidentes
  • La suggestion d’approches alternatives

Les outils de revue de code AI sont peu fiables pour :

  • Les violations de boundaries d’architecture
  • Le couplage subtil introduit entre modules
  • Les violations de contracts comportementaux
  • Les propriétés de sécurité qui exigent un raisonnement à l’échelle du système entier

Le mode d’échec n’est pas que la revue AI rate des choses de temps en temps. Le mode d’échec, c’est qu’elle rate des choses de façon imprévisible, et que vous ne pouvez pas savoir quand elle a raté quelque chose. C’est pour cela qu’elle ne peut pas remplacer l’enforcement déterministe — seulement le compléter.

L’économie

Les garde-fous déterministes coûtent peu à exécuter et coûtent cher à construire au départ.

Écrire les règles d’architecture prend quelques jours. Maintenir la configuration Semgrep demande une attention continue. Mettre en place des contract tests exige de définir d’abord les interfaces.

Mais une fois qu’ils existent, ils tournent à chaque commit pour un coût marginal presque nul. Ils ne se fatiguent pas. Ils ne sautent pas des vérifications le vendredi après-midi. Ils ne cèdent ni à l’ancienneté ni à la pression sociale.

Pour les codebases générées par AI, où le volume de code est élevé et la vitesse de génération rapide, ce profil économique est décisif. L’alternative — faire grandir la revue humaine au même rythme que la vitesse de génération AI — n’est pas viable.

Le lien avec l’architecture AI-native

J’ai écrit sur cette dynamique plus large dans Stanford CS146S a raison sur l’AI coding. Le sujet manquant, c’est l’architecture. Les garde-fous déterministes sont le mécanisme d’enforcement qui rend réelle une architecture remplaçable.

Sans garde-fous, les boundaries d’architecture restent aspirationnelles. Avec eux, les boundaries deviennent structurelles. La différence entre « on essaie de garder les modules isolés » et « le build échoue si l’isolation des modules est violée » est la différence entre une architecture qui survit à une itération à vitesse AI et une architecture qui s’effondre sous cette vitesse.

Le développeur logiciel moderne n’a pas seulement besoin d’aisance avec les outils AI. Il a besoin d’une pile de garde-fous qui rende le code généré par AI structurellement sûr à mettre en prod rapidement.

FAQ

Quel est le meilleur outil pour faire respecter des règles d’architecture sur du code généré par AI ?

Semgrep est l’option la plus flexible pour des règles d’architecture personnalisées. Il prend en charge le pattern-matching sur la structure AST, s’exécute vite en CI et permet aux équipes d’encoder des boundaries propres à leur projet. Pour les projets JavaScript/TypeScript, dependency-cruiser et des règles ESLint personnalisées sont aussi efficaces.

Est-ce que la revue de code AI peut remplacer la revue de code humaine ?

Non. La revue de code AI complète la revue humaine pour le style et la documentation, mais elle est peu fiable pour l’enforcement des boundaries d’architecture et des propriétés de sécurité. Les vérifications déterministes (système de types, linters, règles d’architecture, contract tests) sont le seul remplacement qui passe à l’échelle pour une revue dépendante de l’attention.

Comment mettre en place des garde-fous sans ralentir l’équipe ?

Commencez par le système de types (mode strict, sans exceptions). Ajoutez des règles d’architecture pour les trois boundaries les plus risquées. Ajoutez des contract tests pour les intégrations externes. Chaque couche prend une journée à mettre en place et s’exécute en quelques secondes. Le ralentissement vient des violations, pas des vérifications elles-mêmes.

Quelle est la différence entre un garde-fou et un linter ?

Un linter suggère des améliorations. Un garde-fou casse le build. La distinction, c’est l’enforcement. Dans les codebases générées par AI, les suggestions sont ignorées à grande échelle parce que le volume est trop élevé pour une attention manuelle cohérente. Seuls les échecs de build garantissent la conformité.