Notes sur la construction de systèmes qui marchent et d'autres choses... peut-être ?

La découverte d'abord

· 5 min

Le moyen le plus rapide de repérer du code écrit par quelqu'un qui a débarqué hier, c'est de lire sa gestion d'erreurs. La logique est correcte. Le nommage est sensé. Et ça ne ressemble à aucun autre fichier du repo. Son petit dialecte à lui : un wrapper de réponse que personne d'autre n'utilise, un type d'erreur maison là où tout le reste renvoie celui qui est partagé, de la validation inline là où le reste de la base de code a une couche de garde. Pris isolément, ça passe la review. À côté de ses voisins, ça se lit comme un invité qui a apporté ses propres assiettes.

J'ai livré ce code-là. Du coup j'ai une règle maintenant, et elle est ennuyeuse exprès : pas de code tant que je n'ai pas cherché comment la chose est déjà faite. Chercher, rapporter ce que j'ai trouvé, attendre un feu vert, puis construire. Un mur étanche entre comprendre et construire, sans rien qui fuit d'un côté à l'autre trop tôt.

L'ordre compte plus qu'il n'en a l'air. Quand tu écris d'abord et que tu réconcilies après, la réconciliation n'arrive jamais vraiment. Tu as un diff qui marche, les tests sont au vert, et maintenant coller au style de la maison est une corvée qui se dresse entre toi et le merge. Alors tu sautes l'étape, ou tu la fais à moitié, et le dialecte part en prod. La découverte d'abord, ça inverse le coût. Tu paies la recherche avant d'être attaché à quoi que ce soit, au moment où changer de cap est gratuit parce qu'il n'y a pas encore de cap.

Voici la forme, concrètement.

Découvrir

Grep le fichier que tu t'apprêtes à toucher. Puis grep ses voisins. Tu ne lis pas pour comprendre en général, tu chasses le pattern précis dont tu vas avoir besoin : comment les handlers de ce dossier renvoient un succès, comment ils renvoient une erreur, où vit la validation, ce qui est loggé.

Cite ce que tu trouves. De vrais chemins, de vraies lignes.

handlers/users.go:42   success: c.JSON(200, Envelope{Data: u})
handlers/users.go:55   error:   return apiErr(c, 400, "invalid_email")
handlers/orders.go:31  same envelope, same apiErr helper
handlers/teams.go:28   same again

Quatre fichiers, un pattern, dit trois fois. Ce n'est plus une suggestion. C'est le contrat.

Point de contrôle

Stop. Avant qu'une seule ligne du nouveau handler n'existe, dis trois choses à voix haute. Voici le pattern existant. Voici mon plan, qui s'y conforme. Voici le seul endroit où je dévie, et pourquoi.

La ligne sur la déviation, c'est tout l'intérêt. Copier aveuglément n'est pas le but. Parfois le pattern existant est faux, ou ton cas diffère vraiment, et là tu dévies exprès et tu le dis. La différence entre une déviation délibérée et une déviation inventée, c'est de savoir si quelqu'un était au courant que ça arrivait. L'une est une décision. L'autre est une surprise que quelqu'un découvre dans trois mois.

Ensuite tu attends le feu vert. C'est l'arrêt net, et il est dur parce que le sauter donne l'impression d'être productif. Tu sais déjà quoi écrire. Attendre, ça ressemble à du surplace. Ça n'en est pas. C'est le moment le moins cher pour se faire dire qu'on est sur le point de faire la mauvaise chose.

Implémenter

Maintenant tu construis, et construire est presque barbant, parce que les décisions intéressantes ont déjà eu lieu. Tu copies l'envelope. Tu appelles le même helper apiErr. Ton handler a la même forme que ses quatre voisins, plus l'unique endroit documenté où ce n'est pas le cas.

Le nouveau endpoint se lit comme s'il avait toujours été là.

Pourquoi la version à froid déraille

Écris un handler sans recherche et tu ne produis pas rien. Tu produis quelque chose de plausible. Tu inventes une enveloppe de réponse parce que toute API en a une et que la tienne te semble raisonnable. Tu inventes une forme d'erreur parce que les erreurs ont besoin d'une forme. Rien de tout ça n'est mauvais à la manière dont un bug est mauvais. C'est mauvais à la manière dont un accent étranger est mauvais dans une base de code : ça marche, ça coûte à chaque futur lecteur un moment d'arrêt, et ça autorise discrètement la personne suivante à inventer son propre truc à son tour. Les patterns pourrissent une exception raisonnable à la fois.

La découverte amarre le travail à ce qui existe. Ça ne te rend pas plus intelligent. Ça t'empêche juste d'inventer dans un coin alors que la réponse était déjà posée dans le fichier d'à côté.

Le cas de l'agent

C'est un vieux conseil pour les humains qui rejoignent une base de code. Lis avant d'écrire. Sauf qu'avant c'était facile à ignorer, parce qu'un humain n'écrit pas si vite, et qu'un humain ressent la friction d'un pattern étranger, ce sentiment lancinant du « il y a sûrement déjà une façon de faire ça ici ».

Un agent IA ne ressent pas ça. Il va générer un type d'erreur propre, sûr de lui, cohérent en interne, qui n'existe nulle part ailleurs dans ton repo, en quelques secondes, et il va le faire dans cinq fichiers avant que tu aies fini de lire le premier. L'hallucination n'est pas une erreur de syntaxe que tu peux attraper. C'est un pattern plausible mais étranger, produit plus vite que personne ne peut le remarquer, et plausible est exactement le genre de faux qui se faufile à travers la review.

Du coup l'étape de découverte cesse d'être une politesse et devient un garde-fou. Fais de la recherche et du point de contrôle une phase obligatoire, pas une habitude que tu espères voir tenir. Force l'agent à grep d'abord, à citer ce qu'il a trouvé, à dire où il dévie, et seulement après, à écrire. La même règle qui a toujours été du savoir-vivre pour une nouvelle recrue. L'agent est juste la version de la nouvelle recrue qui tape plus vite que tu ne peux protester.

Chercher d'abord. Construire ensuite. Jamais l'inverse.