Je peux accepter qu’un jeu me refuse quelque chose. Une attaque trop tardive. Une esquive mal placée. Une barre d’endurance vide au mauvais moment.
Dans Dark Souls 3, le refus fait partie du jeu. Ce qui le rend supportable, ce n’est pas sa difficulté. C’est le feedback.
Une action ratée produit quelque chose. Une animation. Un son. Une perte de vie. Une mort, parfois. Le jeu ne m’explique pas forcément la solution, mais il me montre que ma décision a eu une conséquence.
Il peut être volontairement obscur sans être silencieux.
Cette différence m’est revenue dessus en travaillant sur la newsletter de ce site.
## Trois pannes, presque aucun signal
Le 28 juillet, j’ai corrigé plusieurs problèmes liés aux emails. Pris séparément, ce sont des bugs assez classiques. Ensemble, ils racontent surtout la même erreur : le logiciel savait qu’un problème existait, mais il ne le disait pas au bon endroit.
Premier cas : quand le SMTP n’était pas configuré, mes fonctions d’envoi ne faisaient rien.
Pas d’exception visible. Pas de log. Pas de mail non plus.
Depuis l’extérieur, les situations suivantes devenaient identiques :
- le serveur SMTP a refusé le message ;
- la configuration est absente ;
- la fonction n’a jamais été appelée ;
- le message est parti mais n’est pas arrivé.
Le seul feedback disponible était celui de l’utilisateur : « je n’ai pas reçu le mail ».
C’est l’équivalent d’appuyer sur un bouton dans un jeu et de ne voir ni attaque, ni animation, ni message. Je ne sais pas si mon entrée a été reçue. Je ne peux même pas commencer à comprendre mon erreur.
J’ai donc rendu les emails non envoyés visibles dans les logs. Pas pour afficher une énorme trace à l’utilisateur. Le signal doit arriver à la personne qui peut agir dessus.
## Une erreur utile doit atteindre le bon joueur
Deuxième problème : une valeur de mon fichier .env contenait un commentaire en fin de ligne. Mon propre parseur gardait ce commentaire dans la valeur. L’hôte SMTP devenait invalide et l’envoi terminait en ENOTFOUND.
La documentation officielle de Node.js sur les fichiers `.env` définit bien # comme le début d’un commentaire hors guillemets. Mon script ne reproduisait pas correctement cette règle.
Cette fois, une erreur existait. Mais sans logs assez clairs autour de l’envoi, elle restait loin de l’action qui m’intéressait : publier un article et envoyer sa notification.
Un bon feedback ne se contente pas d’exister quelque part. Il doit conserver le lien entre l’intention et l’échec.
Dans Dark Souls, je comprends qu’une attaque n’est pas partie parce que mon personnage réagit immédiatement à l’état du jeu. Dans mon serveur, je dois pouvoir relier « notification demandée » à « envoi impossible car l’hôte SMTP est invalide ».
Le niveau de détail n’est pas le même. Le principe, si.
## Le serveur avait raison, l’utilisateur avait perdu
Le troisième bug était plus trompeur.
Après confirmation d’un abonnement, la redirection envoyait vers localhost:3004. Les routes construisaient leur destination depuis req.nextUrl.origin. La documentation de `NextRequest` décrit nextUrl comme une extension de l’API URL. Dans mon déploiement, la requête arrivait à Next.js derrière nginx. L’origine observée par l’application était donc son adresse interne.
Pour le serveur, localhost:3004 était cohérent. Pour la personne qui venait de cliquer dans son email, cette adresse ne désignait évidemment pas mon portfolio.
J’utilisais aussi SITE_ORIGIN pour deux besoins contradictoires :
- appeler le site en interne depuis le VPS ;
- fabriquer des liens publics dans les emails.
J’ai séparé les deux origines.
Ce bug me paraît intéressant parce que chaque couche disposait d’une information valide. C’était leur assemblage qui produisait une mauvaise réponse. Le serveur confirmait correctement l’abonnement, puis donnait une sortie inutilisable au client.
Le feedback était présent. Il mentait sur le chemin à suivre.
## Refuser n’est pas se taire
Je ne veux pas transformer chaque erreur en pavé rouge. Dark Souls 3 ne met pas une documentation au milieu de l’écran après chaque coup raté. Il donne juste assez de matière pour relier une action à un résultat.
C’est ce que je veux retrouver dans mes projets :
- côté utilisateur, un état compréhensible ;
- côté serveur, une erreur exploitable ;
- entre les deux, aucune URL interne ou information technique qui fuit par accident.
La prochaine étape est concrète : tester la construction des liens d’email avec une configuration proche de la production, sans attendre de cliquer sur un vrai message. Et lorsqu’un envoi est volontairement désactivé, le faire apparaître comme un état explicite, jamais comme une fonction qui disparaît dans le silence.