Leçons — câblage de @kanmaber/auth-backend dans jobPipeline¶
Date : 2026-07-03
Ce document capitalise sur ce qui a été appris pendant le câblage réel du
package @kanmaber/auth-backend (voir 20260703_1434_procedure-cablage-auth-package.md
pour la procédure détaillée). Deux problèmes réels ont été trouvés et
corrigés — les leçons ci-dessous sont génériques, valables au-delà de ce
package, pour tout package partagé sous Shared/packages/.
Leçon 1 — Une valeur hardcodée utilisée comme clé étrangère est une bombe à retardement silencieuse¶
Ce qui s'est passé : InscrireUtilisateurUseCase (dans le package)
hardcodait idRole: 'role_observateur', valeur utilisée directement
comme profilId (FK) par le repository Prisma — sans lookup, sans
validation, sans documentation nulle part (ni README, ni script de seed
livré, ni commentaire). Résultat : la première inscription réelle a
crashé avec une violation de contrainte FK Prisma brute, un message
technique opaque sans rapport avec la cause réelle.
Pourquoi c'est dangereux : ça a passé le typecheck (c'est une string
valide), ça a passé les smoke tests (qui ne font qu'assembler des objets
DI, jamais une vraie requête DB), et ça n'a été détecté qu'au premier test
curl réel de bout en bout — après tout le reste du câblage.
Correction appliquée : remplacer le hardcode par une résolution
dynamique via une clé métier stable et unique (ProfilUtilisateur.code),
avec une erreur métier explicite (PROFIL_PAR_DEFAUT_INTROUVABLE) si le
seed est absent — au lieu de laisser remonter l'erreur Prisma brute.
Règle à retenir : dans un package partagé, toute valeur qui référence
une donnée censée exister côté consommateur (seed, config par défaut,
rôle par défaut...) doit être :
1. résolue dynamiquement par une clé métier lisible (code), jamais un id
technique arbitraire (cuid, uuid) hardcodé ;
2. accompagnée d'une erreur claire si la ressource est absente, pas d'un
crash technique opaque ;
3. documentée (README ou script de seed livré avec le package) si le
consommateur doit préparer des données en amont.
Leçon 2 — npm install sur une dépendance file:...tgz peut silencieusement garder l'ancien code¶
Ce qui s'est passé : après avoir corrigé le package, reconstruit
(npm run build) et regénéré le tarball (npm pack), un
rm -rf node_modules/@kanmaber/auth-backend && npm install dans
jobPipeline a bien réécrit le fichier (nouveau timestamp), mais avec
l'ancien contenu. Aucune erreur, aucun avertissement.
Cause : package-lock.json retient un hash d'intégrité (integrity)
calculé lors de la première installation. Tant que le chemin et le
numéro de version de la dépendance file: restent identiques, npm
fait confiance à son cache local (~/.npm/_cacache) au lieu de relire
réellement les octets du .tgz sur le disque — même si ce fichier a
physiquement changé entre-temps.
Solutions envisagées :
- Extraction manuelle du .tgz dans node_modules (tar -xzf ...
--strip-components=1) — fonctionne, mais manuel et facile à oublier à
chaque itération future.
- Pointer la dépendance vers le dossier source plutôt que le .tgz**
(file:../../Shared/packages/auth-backend) — **rejeté** : npm crée
alors un symlink, et Node résout les imports internes du package (ex.@prisma/client) via lenode_modules**du dossier du package**, pas
celui de jobPipeline. Or cenode_modulescontient un client Prisma
généré depuis le schéma *fragment* du package, différent du schéma
*fusionné* réel de jobPipeline (champstelephone/titreabsents,updatedAtsans@updatedAt) — un bug de désynchronisation silencieux
et bien pire que le problème de cache initial.
- **Solution retenue : bumper la version du package à chaque
modification** (1.0.0→1.0.1, etc.) avantnpm pack, et mettre à
jour la référencefile:...tgzcôté consommateur en conséquence. Un
nouveau numéro de version force npm à traiter le contenu comme neuf,
sans passer par le cache. Vérifié : après bump,npm install` seul (sans
extraction manuelle) a correctement repris le nouveau code.
Règle à retenir : pour un package interne distribué en .tgz local
(file: + tarball, pas un vrai registre npm) :
- toujours bumper la version avant npm pack, même en dev interne —
ne jamais republier un .tgz avec le même numéro de version ;
supprimer l'ancien .tgz pour éviter toute ambiguïté ;
- ne pas pointer vers le dossier source tant que le package a son propre
node_modules avec des dépendances qui pourraient diverger de celles
du consommateur (notamment @prisma/client, généré depuis un schéma) ;
- après tout rebuild d'un package file:, vérifier explicitement (grep
sur une chaîne caractéristique du changement) que le contenu installé
dans node_modules du consommateur correspond bien à la nouvelle
version — ne jamais supposer que npm install a fonctionné.
Leçon 3 (rappel) — le typecheck et les smoke tests ne prouvent pas qu'une fonctionnalité marche¶
Les deux bugs ci-dessus (Leçon 1 et l'absence de champs Zod requis
confirmationMotDePasse/accepteCGU dans le payload d'inscription) sont
passés inaperçus jusqu'au premier vrai test curl de bout en bout. Ni
tsc --noEmit, ni le script smoke-test-auth-package.mjs (qui ne fait
qu'instancier les objets DI) ne les auraient révélés. Confirme la
pratique déjà en vigueur : tester le comportement réel (requête HTTP
bout en bout) avant de considérer un câblage terminé, pas seulement la
compilation.
Checklist à réutiliser pour tout futur package Shared/packages/*¶
- [ ] Toute valeur de référence hardcodée (rôle par défaut, config...) est résolue dynamiquement par une clé métier, pas un id technique fixe.
- [ ] Le package documente (README ou script de seed) toute donnée qu'il suppose exister côté consommateur.
- [ ] Le numéro de version est bumpé à chaque modification avant
npm pack. - [ ] L'ancien
.tgzest supprimé après un bump, pour éviter toute confusion. - [ ] Après
npm installd'un packagefile:...tgzmis à jour, on vérifie par grep que le contenu installé correspond bien à la nouvelle version. - [ ] Un test end-to-end réel (curl/navigateur) valide le câblage — pas seulement le typecheck ou un smoke test d'assemblage DI.