cr3575.fr

Étude de cas · le produit

Quinze ans sur le même système, quatre fois reconstruit.

anecdotrip.com est en ligne depuis 2011. Je le conçois, le développe et l’exploite seul : code, base de données, déploiement, hébergement, supervision. Le sujet du site est un prétexte — ce qui m’intéresse est le système, et le fait qu’il tienne.

Générations de stack

Reconstructions successives4 générations
01
anecdotrip, première génération — PHP 5 et MySQLphp · mysql · sql · html-css
2011–2019
02
anecdotrip, deuxième génération — Vue.js et PostgreSQLvuejs · javascript · postgresql · sql · migration-donnees
2019–2021
03
anecdotrip, troisième génération — Next.js et API REST Node/MongoDBnextjs · react · nodejs · express · mongodb · mongoose · docker · nginx · rest · api · architecture
2021–2026
04
anecdotrip, quatrième génération — refonte assistée par IAnextjs · react · typescript · mongodb · mongoose · docker · gitlab-ci · claude-code · copilot · llm · agents · tests · e2e · architecture · developpement
depuis 2026

Audit guidé · vérifiez sans me croire

Le dépôt n’est pas public, donc je ne vous demande pas de me croire sur parole : je vous dis quoi ouvrir, quoi mesurer, et ce que vous devriez trouver. Si le résultat diffère de ce qui est annoncé ici, c’est moi qui ai tort.

Point de contrôle · méthode
01
anecdotrip, quatrième génération — refonte assistée par IAOuvre https://anecdotrip.com, bascule la langue entre français et anglais, tu dois passer d'un sous-domaine fr. à un sous-domaine en. et non d'un simple préfixe d'URL.
Ouvrir
02
Accessibilité et internationalisationCharge https://anecdotrip.com et appuie sur la touche Tab : le premier élément à recevoir le focus doit être un lien d'évitement qui mène directement au contenu principal.
Ouvrir
03
Anglais technique — lecture, écrit, conversation simpleOuvre https://anecdotrip.com puis bascule vers le sous-domaine en. : c'est un site anglais complet, servi comme tel.
Ouvrir
04
Console d'administration et chaîne de publicationOuvre une adresse en /admin sur https://anecdotrip.com sans être connecté : tu dois être redirigé vers la page de connexion, pas recevoir une page d'erreur.
Ouvrir
05
Cartographie interactive et données géographiquesSur https://anecdotrip.com, lis deux anecdotes situées puis ouvre la page Trip : clique un point de la carte, la ligne correspondante de la liste se met en évidence et défile jusqu'à elle.
Ouvrir
06
Chaîne éditoriale de Git vers la base de donnéesOuvre les mentions légales sur https://anecdotrip.com : la date de dernière mise à jour affichée est celle du dernier changement du texte source, pas celle du dernier déploiement.
Ouvrir
07
Système de design et composants réutilisablesCompare une même carte de contenu sur la page d'accueil et sur une page de liste : mêmes marqueurs, mêmes règles de combinaison, même silhouette — elles viennent du même composant, pas de deux copies.
Ouvrir
08
Documenter et transmettre ce qu'on apprendOuvre la page d'éval de ce site : les cas qui échouent y sont affichés avec leur cause, pas retirés du jeu.
Ouvrir
09
Écriture d'interface — un registre unique, deux languesCompare sur https://anecdotrip.com un état vide, une page introuvable et un bouton d'action : même registre de phrase, casse de phrase, en français comme en anglais.
Ouvrir
10
HTML et CSS — usage courantOuvre https://anecdotrip.com sur un téléphone, ou réduis la fenêtre à 390 pixels de large : aucune page ne doit déborder horizontalement.
Ouvrir
11
Linux et exploitation en production — usage courantInterroge https://anecdotrip.com en HTTPS et regarde le certificat et les en-têtes : le site répond depuis un serveur que j'administre, pas depuis une plateforme d'hébergement gérée.
Ouvrir
12
Mode sombre, mobile-first et polices libresOuvre https://anecdotrip.com dans une fenêtre de 390 pixels de large et bascule ton système en thème sombre : la page suit le thème et tient sans défilement horizontal.
Ouvrir
13
Performance web — mesure, attribution, arbitrageAffiche le code source servi d'une page d'anecdote sur https://anecdotrip.com : l'image d'en-tête porte un attribut fetchpriority à high.
Ouvrir
14
React et Next.js — usage courantOuvre une page d'anecdotrip.com avec JavaScript désactivé : le contenu principal doit s'afficher quand même, parce qu'il est rendu côté serveur.
Ouvrir
15
Référencement naturel — rendu serveur, URLs, données structuréesAffiche le code source servi d'une page de région sur https://anecdotrip.com : la liste des villes et leurs liens sont déjà dans le HTML, avant toute exécution de JavaScript.
Ouvrir
16
Développeur indépendant — anecdotrip.comOuvre https://anecdotrip.com, regarde les en-têtes de réponse, tu dois trouver un Content-Security-Policy servi par le site lui-même.
Ouvrir
17
Mesurer avant d'affirmer, et nommer l'environnement de mesureLance un audit Lighthouse sur une page d'anecdotrip.com depuis un vrai navigateur, puis compare avec la même page servie en local : les deux nombres ne se remplacent pas.

Décisions et incidents · ce que le produit m’a appris

Chacune de ces règles vient d’un incident daté, pas d’un article de blog. Elles sont dans le corpus, donc l’analyse d’offre peut les citer.

2026

Un contrôle isolé ne peut pas déclencher un bug d'état partagé

Un test de suppression est devenu intermittent dans mon intégration continue le 11 août 2026, en attendant 9 éléments et en en trouvant 5. Je l’ai relancé isolément : cinq exécutions sur cinq au vert, ce qui ne prouvait rien du tout, parce que le mécanisme suspecté — un nettoyage groupé qui s’arme à partir de huit brouillons sur un compte partagé par six fichiers de tests exécutés en parallèle — ne peut structurellement pas se déclencher quand le test tourne seul. J’ai reconstruit l’arithmétique à partir du code, seuil de déclenchement et cible de nettoyage, et elle retombait exactement sur le 5 observé. La règle : avant de lancer un contrôle, se demander si le mécanisme soupçonné peut s’y produire ; s’il ne le peut pas, le vert est acquis d’avance et n’est pas une preuve.

2026

Une spécification écrite sans lire le code est un brouillon

Le 7 août 2026, j’ai rédigé des spécifications de refonte pour quatre écrans à partir de ma seule mémoire du projet, sans rouvrir le code. Le code les a contredites sur les quatre : deux écrans étaient déjà refaits, un composant que je décrivais comme à créer existait déjà, et je m’étais trompé sur le nom et la bibliothèque du composant de carte. Quatre recommandations ont été retirées. Le problème d’une spécification n’est pas d’être vague, c’est d’être confiante et fausse : elle se lit exactement pareil qu’elle ait été vérifiée ou non, donc quelqu’un l’exécute. Depuis, je fais d’abord relire le code par des agents d’exploration, et si le budget ne le permet pas, j’écris « brouillon » dans le fichier lui-même.

2026

Une suite de tests verte ne prouve rien sur la mise en page

Le 5 août 2026, un bloc d’image de mon site est sorti à 533 pixels de large dans une fenêtre de 390 pixels, soit 143 pixels de débordement horizontal sur toutes les pages d’anecdote au format téléphone, et la suite de bout en bout est passée 628 fois sur 628 par-dessus. Playwright vérifie qu’un élément est visible et cliquable, ce qu’il était : aucun de ces 628 tests ne regardait une largeur. Je ne l’ai vu qu’en scriptant la comparaison entre la largeur mesurée de l’élément et celle de la fenêtre. Depuis, quand la géométrie est le livrable, j’écris la mesure et je refuse de traiter une suite verte comme une vérification ; et je vérifie une taille d’écran où la contrainte mord vraiment, parce que dans cet incident le format tablette s’affichait parfaitement.

2026

Un test peut passer à vide — vérifier la forme avant de comparer

Le 11 août 2026, j’ai passé un lot de huit tests fraîchement écrits sur un contrôle destiné à vérifier qu’ils échouaient bien quand ils devaient échouer. L’un des huit passait à vide : il comparait deux valeurs absentes, une égalité de rien avec rien, et affichait donc du vert sans jamais avoir touché le comportement visé. Un test faux positif est pire que pas de test, parce qu’il occupe la place et interdit de chercher ailleurs. La règle que j’en ai tirée est d’affirmer d’abord la forme de la donnée — qu’elle existe, qu’elle a le bon type — avant de comparer sa valeur, et de faire échouer volontairement chaque test neuf au moins une fois.

2026

Une note « ne pas régresser » n'est pas un garde-fou

Le 8 août 2026, j’ai découvert qu’une règle de conception écrite dans ma documentation sous le titre « durement acquis, ne pas régresser » était contredite par les dix formulaires concernés, et l’avait toujours été : la dérive datait de février 2025 et avait survécu à une refonte complète, soit treize mois sans que rien ne la signale. Mesurée, la barre incriminée occupait 246 pixels, 38 % d’une fenêtre de 320 sur 640 — une modification faite au nom de l’accessibilité était elle-même un défaut d’accessibilité. Une prose ne peut pas échouer : elle ne vaut que si quelqu’un ouvre ce fichier avant de toucher au code. Depuis, quand j’écris une règle de ce type, je vérifie le jour même que le code y correspond, et si elle mérite d’être gardée je la déplace dans un composant qui possède le balisage ou dans un test capable de virer au rouge.

2026

Mesurer avant d'affirmer, et nommer l'environnement de mesure

Le 24 juillet 2026, la même erreur s’est répétée quatre fois dans un après-midi : j’avais mesuré un correctif d’affichage en local, conclu qu’il n’y avait plus de scintillement blanc, et l’écrit tel quel dans un journal de décisions. En production, où le téléchargement du bundle domine, le scintillement durait 400 à 500 millisecondes et se voyait à l’œil nu sur le site en ligne. J’en ai tiré deux règles que j’applique depuis : aucune conclusion écrite avant d’avoir mesuré, et une mesure n’est valable que dans l’environnement où elle a été prise, donc ce nom accompagne toujours le chiffre. La causalité s’établit avec un contrôle — reconstruire l’état d’avant et relancer — pas avec un ordre chronologique du type « ça marchait avant ma modification ».

2026

Un échec que personne ne regarde dure indéfiniment

Le 29 juillet 2026, une synchronisation de contenu a échoué à son premier déploiement sur une erreur de poignée de main TLS. En remontant, j’ai découvert que le même défaut tuait mes deux tâches planifiées nocturnes depuis des mois : les scripts autonomes construisaient leur URL de connexion sans le certificat d’autorité privée que l’application, elle, ajoutait à l’exécution. L’échec était écrit en clair dans les journaux du conteneur tout ce temps ; rien ne les lisait. J’ai corrigé en centralisant la construction de l’URL dans une fonction unique qui refuse de se connecter en production si le certificat manque, plutôt que de retomber silencieusement sur une connexion non vérifiée. La leçon qui vaut au-delà de ce cas : un système qui échoue en silence est un système que personne ne surveille, et une alerte sur l’échec vaut mieux qu’un journal parfait.

2026

Ce qui est présent à la construction peut être figé dans l'image

Le 13 juillet 2026, 280 tests de bout en bout sont tombés d’un coup dans mon intégration continue. La cause : une variable d’environnement destinée à être lue à l’exécution était présente pendant la construction de l’image, et l’outil de compilation l’avait remplacée par sa valeur littérale jusque dans le code serveur — la lecture à l’exécution n’existait plus. Il suffisait qu’elle soit déclarée comme argument de construction Docker, sans même être exportée, pour que ça se produise, et la renommer plus loin dans le fichier n’y changeait rien. La conséquence était invisible en local et se manifestait par des cookies posés sur le mauvais domaine, donc par des connexions qui ne tenaient pas. J’en retiens qu’il faut savoir, pour chaque valeur de configuration, à quel moment elle est lue, et qu’un environnement de construction n’est pas neutre.

2026

Préférer le correctif le plus simple qui marche

Pour gérer les index de ma base de données, j’avais proposé un script de synchronisation, un manifeste, un test de dérive en intégration continue, une étape de déploiement et une modification du Dockerfile. La bonne réponse était : mettre à jour le fichier de modèle et taper une commande de création d’index à la main sur la production. La création automatique d’index est active en développement, en intégration et en tests, et désactivée uniquement en production, qui est une base unique et durable à laquelle j’ai un accès direct — la machinerie répondait à un problème que je n’avais pas. Depuis, je trace où une chose est réellement gérée dans chaque environnement avant de proposer un outil, et pour une action manuelle occasionnelle, une procédure documentée bat une automatisation.

2026

Un constat est une hypothèse, pas encore un défaut

Pendant la passe d’audit de mon site en juin et juillet 2026, la leçon dominante a été qu’un constat d’audit n’est pas un défaut confirmé : le comportement existant peut être intentionnel. Un exemple précis : une vérification de rôle relisait délibérément la base plutôt que la session, parce que la session est mise en cache cinq minutes ; « factoriser » cette duplication aurait rouvert une fenêtre d’élévation de privilèges de cinq minutes. Un autre : une règle interne interdisait un type volontairement permissif, alors que la plupart des usages relevés étaient légitimes — c’était la règle qui était fausse, pas le code. Depuis, avant de corriger, je cherche si le code actuel est délibéré pour une raison réelle, et quand il l’est, j’annote plutôt que je ne refactorise.

Ce qui n’est pas ici

Le détail de l’audit de sécurité et la cartographie des garde-fous d’authentification ne sont pas publiés. Le site est en production avec de vrais comptes : publier le plan des défenses d’un système vivant serait une faute, quelle que soit la valeur de démonstration. Je décris la méthode, jamais les emplacements — et c’est aussi une réponse à la question « sait-il ce qu’on ne met pas en ligne ».