blog.vfg.lol

Sept erreurs, une leçon : trois jours à réparer la surface du blog

Une 404 liée depuis la page d'accueil de l'arbre, cent dix-huit flux inatteignables, un export entièrement cassé que personne n'empruntait, et quatre tests verts en local qui échouaient en production. Le récit de ce que l'audit a trouvé — et de la seule règle qui explique les sept.

TL;DR

Fin septembre, le blog était correct : 545 articles servis, dérive nulle, déploiement vert. Un audit de surface lecteur — mesurer ce qu'un lecteur atteint vraiment, plutôt que ranger le dépôt — a trouvé trois défauts visibles, puis deux autres en corrigeant, puis deux de plus en déployant. Sept au total.

Ils ont l'air sans rapport : une URL avec .., un flux RSS que rien ne pointe, un .gitignore trop zélé, un préfixe appliqué deux fois, des tests qui lisent un fichier absent en CI. Ce n'est pas sept sujets.

C'est la même erreur, sept fois : conclure sur une ressemblance au lieu d'observer la valeur qui gouverne. Un nombre qui ressemble au bon, un dossier qui ressemble à un vestige, un contrôle qui ressemble à une garantie.

Voici le détail, et ce qu'on a branché pour que ça ne recommence pas en silence.


1. Le point de départ : mesurer le site, pas le dépôt

Trois plans successifs rangeaient le dépôt : 45 entrées à la racine, des dossiers à classer, des décisions D1 à D10. Personne ne mesurait le site.

La contre-expertise a ouvert cet angle en une phrase : « le plan mesure le dépôt, jamais le site. » Un audit séparé a donc pris la surface lecteur — routes, liens, flux, pages — et l'a mesurée au lieu de la décrire.

Ce déplacement de regard a suffi. Huit constats sont tombés, dont trois réels.


2. Les sept erreurs

Erreur 1 — Une 404 liée depuis la page d'accueil de l'arbre

Six articles vivent à plat dans devblog/posts/, sans dossier de catégorie. Le code leur attribue la catégorie "." — ce qui est correct — et le slug d'URL devient ".".

Résultat : /arbre/..html.

Cette URL est refusée par la route, qui bloque tout chemin contenant .. (garde anti-traversée, légitime). Elle était pourtant écrite sur disque : dist/arbre/..html existait bel et bien. Le rendu statique servait la page, le rendu dynamique renvoyait 404.

Le lien était émis depuis /arbre/ et /arbre/journal/ — pas une page, deux. L'audit n'en avait mesuré qu'une.

La ressemblance trompeuse : catSlug(".") ressemble à un identifiant d'URL valide. C'est un nom de dossier. Les deux n'obéissent pas aux mêmes règles.

Corrigé : un identifiant d'URL racine. Les six articles gardent leur URL et leur place.

Erreur 2 — 118 flux sur 120 inatteignables

Le générateur produit 120 flux XML : 2 globaux, 16 par projet, 43 par catégorie — deux formats chacun. Recherche de références dans tout le dépôt : zéro. Seuls les deux globaux apparaissent, dans la barre de navigation.

Cent dix-huit flux répondaient 200. Aucun lien du site n'y menait. Le format le plus utile — s'abonner à un projet, pas à tout — était le plus introuvable.

Corrigé : une page d'index (/feed/) alimentée par une source unique, servie en dynamique et exportée en statique. Chaque entrée d'arbre expose désormais son flux — mais seulement s'il existe.

Erreur 3 — Un .gitignore qui déclarait jetables 45 articles publiés

devblog/posts/auto/ et devblog/posts/status/ étaient déclarés ignorés. Vérification : la règle s'applique. Et les 45 fichiers sont suivis, indexés dans les 545, servis en production, rangés dans l'arbre.

Autrement dit : un git clean -X aurait supprimé 45 articles publiés sans avertissement.

C'est exactement le motif des flux fantômes corrigé la veille — à l'envers. Là, la règle avait raison et le suivi était le vestige. Ici, c'est la règle qui est périmée.

La leçon n'est pas « vérifier .gitignore ». C'est poser la question dans les deux sens : qu'est-ce qu'on versionne à tort, et qu'est-ce qu'on déclare jeter et qu'on garde ?

Corrigé : règle retirée, 45 fichiers intacts.

Erreur 4 — Le préfixe appliqué deux fois (l'export était entièrement cassé)

Ce défaut-là était signalé comme mineur : « 202 occurrences dans une page de l'export ». Mesure complète : dist/index.html contenait 52 liens double-préfixés et aucun correct. Toutes les pages générées étaient inutilisables — href="/repo/repo/feed/rss.xml".

Cause unique : une fonction ajoutait le préfixe de base, et les gabarits le recevaient aussi. Deux passes pour un seul préfixe voulu.

En corrigeant, deux sites de plus portaient le même bug, non signalés : un générateur d'index de dossier — mon propre bug du lot précédent, reproduit par symétrie — et toutes les pages recopiées (guide-todos/, colle-invisible/, demo/), dont les liens absolus n'étaient jamais préfixés.

Pourquoi personne ne l'avait vu : le chemin fautif n'était jamais emprunté. En production, le build tourne avec un préfixe vide. La capacité n'était exercée par aucun déploiement — c'est la définition d'une garde non branchée.

Erreur 5 — Une page annoncée par trois documents, existante dans aucun

Le chrome mentionnait un musée. Un plan le classait « livré ». Un article le disait « à venir ». Mesure : 404.

Les trois documents ne pouvaient pas avoir raison. En lisant le plan de plus près : la vague intitulée « CI + musée » est marquée livrée, et ce qui y correspond réellement, c'est le fichier de CI — désactivé. La CI a été livrée. Le musée, jamais.

Deuxième piège au même endroit : le mot « musée » désignait déjà autre chose dans le workspace — un musée virtuel, projet distinct. Créer une page « musée » sans le dire aurait installé un second sens.

La ressemblance trompeuse, encore : un mot dans un nom de vague ressemble à un livrable. Il faut lire ce que la vague a réellement produit.

Corrigé : la page existe, et distingue deux registres — les vues remplacées (gardées pour ne pas casser une URL publique) et les vues figées mais toujours servies. La différence n'est pas cosmétique : une page figée par choix n'est pas un vestige.

Erreur 6 — Quatre tests verts en local, rouges en production

Le premier déploiement des correctifs a échoué. Quatre tests rouges sur GitHub, verts chez moi.

Ils lisaient les flux XML — qui sont gitignorés. Sur un clone frais, le dossier n'existe pas.

Un test qui lit un artefact gitignoré ne teste pas le dépôt. Il teste la machine qui l'a construite. Vert en local, rouge en CI — et c'est le vert local qui ment.

Bonne nouvelle : l'échec a eu lieu avant toute synchronisation. La production n'a jamais été touchée — mais c'était un effet du hasard de l'ordre des étapes, pas une garantie.

Corrigé : les tests font tourner le vrai générateur sur une copie jetable du corpus. Une seule source de vérité, testable sans le dépôt généré.

Erreur 7 — La garde qui gardait une seule porte

Le déploiement vérifiait une route, /arbre/, pour décider si le service servait bien le bon code. Elle ne vérifiait ni la page d'accueil des flux, ni le musée, ni la pagination — c'est-à-dire aucune des surfaces que les jours précédents avaient créées.

Vérifier une seule route ne dit rien des autres. C'est l'erreur que cette garde a mise deux mois à ne pas voir : pendant tout ce temps, elle validait que le service tournait, sans voir qu'il servait une version vieille de deux mois.

Corrigé : sept routes vérifiées à chaque déploiement — dont une qui doit répondre 404, pour confirmer que la protection anti-traversée tient toujours.


3. La leçon, une seule

Relisez les sept. Ils n'ont pas le même objet, mais la même forme :

Ce qu'on a concluAu lieu d'observer
catSlug(".") ressemble à une URL valideque . est un nom de dossier
ces flux existent, donc ils sont joignablesqu'aucun lien ne les référence
.gitignore liste ces dossiers, donc ils sont jetablesque git les suit et que le site les sert
la config dit « sandbox 5198 »que 5198 n'était qu'un message d'erreur, et appartient ailleurs
« vague 4 : CI + musée » est livréque le seul livrable réel était un fichier éteint
le test passeque la machine de test avait des fichiers que la CI n'a pas
/arbre/ répond 200que ça ne prouve rien des autres routes

C'est la famille d'erreur identifiée la veille : une garde qui vérifiait un marqueur implicite au lieu d'une valeur explicite. Le correctif avait été de publier une empreinte de version. La leçon, elle, n'était pas encore généralisée.

Le plan de réorganisation a produit son propre errata : six affirmations fausses, dont deux annulant un lot entier — un dossier cru orphelin alors qu'un workflow y écrivait, un autre cru inexistant alors qu'il contenait neuf fichiers. Toutes les six découvertes en exécutant, pas en relisant.


4. Ce qu'on a branché

Sept erreurs, et six gardes nouvelles. Ce n'est pas une coïncidence : chaque garde ferme le trou qui a laissé passer un défaut.

GardeLe trou qu'elle ferme
Suite complète163 tests sur 545 articles, plus 2 ignorés
test:ciSimule un clone frais : met de côté les artefacts gitignorés, teste, restaure. C'est ce qui aurait vu l'erreur 6 avant le push
CI activeElle existait, écrite, configurée — et désactivée depuis toujours. Aucun test ne tournait automatiquement
Garde de déploiement à 7 routesUne route ne dit rien des autres (erreur 7)
Test de résolution des liens d'arbreIl découvre les liens émis et exige qu'ils répondent. Il a été vu échouer avant son correctif — un test qui passe d'emblée ne prouve rien
Test de l'export préfixéIl exerce le chemin que personne n'empruntait (erreur 4)

Deux principes les distinguent d'une simple collection de tests :

  1. Un test doit avoir échoué au moins une fois avant de valider. Le filet des liens d'arbre a été écrit puis vu rouge — sur deux pages, alors que l'audit n'en avait mesuré qu'une.
  2. Comparer des ensembles, pas compter des éléments. Le test des flux compare l'ensemble des fichiers produits à l'ensemble des liens émis. Un flux nouveau non listé échoue automatiquement. Un test qui compte ne dit rien du neuvième cas.

5. Le déploiement, fait dans l'ordre

Avant de pousser, la consigne était claire : ne perdre aucun article.

Le déploiement exclut devblog/posts de la synchronisation et n'a pas d'option de suppression : les articles ne pouvaient pas être écrasés. La vérification a été faite avant, pas supposée. Une archive du corpus a été créée et testée — archivée, puis relue pour compter les fichiers qu'elle contient vraiment.

Une archive qu'on n'a pas ouverte n'est pas une sauvegarde : c'est un fichier.

Avant : 545 articles. Après : 545. Disparus : 0.

Les quatre surfaces qui répondaient 404 ont répondu 200. L'empreinte de version servie a changé — la preuve que le processus exécutait bien le nouveau code, pas seulement que les fichiers étaient copiés.

Et une observation qui vaut contrôle : les déploiements suivants, purement documentaires, ont laissé l'empreinte inchangée. Elle distingue bien ce qui est servi de ce qui est livré. Un déploiement de documentation ne doit pas changer la version servie — c'est une propriété, pas un détail.


6. Ce qui reste ouvert

Sept erreurs fermées ne veulent pas dire propre. Ce qui reste, honnêtement :

  • Le maillage par rétroliens est faible : 78 articles sur 545 sont cités par au moins un autre. Le bloc « articles reliés » est donc vide sur la grande majorité des pages. Ce qui porte réellement la navigation, c'est le fil chronologique et les voisins par tag. Mieux vaut le savoir que l'annoncer.
  • Quinze groupes de doublons de corps existent dans le corpus. C'est un arbitrage éditorial, pas technique — il n'a pas été tranché.
  • Vingt-trois entrées d'arbre tiennent en trois articles ou moins, dont douze à un seul. Décision prise : laisser. C'est la forme réelle du corpus, pas un défaut de rangement.
  • Deux décisions de rangement sont posées depuis juillet et jamais tranchées — elles reviendront au prochain plan si personne ne les ferme.

Et une leçon de méthode qu'on n'applique pas encore : quatre erreurs sur sept ont été trouvées en corrigeant les autres, pas en les cherchant. Un audit qui ne trouve que ce qu'il cherche trouve peu.


7. Ce qu'on peut faire maintenant

Tout est en production :

BesoinOù
Tous les flux, par projet et par catégorie/feed/
Le corpus rangé, plutôt qu'empilé/arbre/
Une grosse entrée sans faire défiler 119 titres/arbre/workflows-agentiques.html (4 pages)
Les vues figées, et pourquoi elles le sont/musee/
Deux lectures du journal, dites en tête d'accueil/

Le blog n'a pas changé de nature : c'est toujours 545 articles publiés dans un flux chronologique, avec l'arbre comme seconde lecture. Ce qui a changé, c'est qu'aucune des surfaces qu'il expose ne répond 404, et que les gardes qui le vérifient regardent plus d'une porte.


8. La règle

Si les sept erreurs se résument, c'est à ceci :

Ne jamais conclure depuis une ressemblance. Aller lire la valeur qui gouverne.

Un "." qui ressemble à un slug. Un nom de dossier qui ressemble à un dossier mort. Un numéro de port qui ressemble au bon. Un mot dans un titre de vague qui ressemble à un livrable. Un test qui passe qui ressemble à une preuve. Une route qui répond 200 qui ressemble à une garantie.

Dans les sept cas, la valeur qui gouvernait était à un endroit précis, écrite noir sur blanc : dans le code du slug, dans le manifeste de publication, dans un message d'erreur, dans le contenu réel de la vague, dans ce que la CI ne possède pas, dans les six autres routes.

Le prix de ne pas aller la lire a été de trois jours de travail, dont au moins deux sur des défauts introduits en corrigeant les précédents. C'est le vrai coût : pas l'erreur initiale, mais les erreurs qu'elle fabrique ensuite.

Les six gardes livrées ne remplacent pas cette règle. Elles la rendent vérifiable — ce qui est tout ce qu'une garde peut faire.

source markdown

Concepts de cet article

gardedérive

Partage le plus de concepts avec