Bibliothèque de playbooks
Service back-end TypeScript
Les garanties de TypeScript s'arrêtent à la frontière du process, et le code back-end généré se comporte régulièrement comme si ce n'était pas le cas. Ces règles se concentrent sur la couture entre le système de types et la réalité : entrées parsées, rejets traités, I/O bornées, configuration qui échoue au démarrage plutôt que sous charge.
- Écrit pour
- Les équipes qui exploitent des services Node/TypeScript — serveurs d'API, workers, jobs.
- Non couvert
- Aucun avis ici sur le choix du framework, l'arborescence des dossiers ou la structure des tests. Ce sont des sujets sur lesquels il vaut la peine de s'accorder, mais ils relèvent du goût de l'équipe — ces règles portent sur le comportement en cas de défaillance.
11 règles6 bloquantes4 warning1 info
01 Parsez les données externes, ne les castez pas
errorLes données qui entrent dans le process — réponses HTTP, payloads de file, `JSON.parse`, contenus de fichiers — sont parsées avec un schéma. `as`, `as unknown as`, `any` et `@ts-expect-error` sans ticket associé sont refusés à ces frontières. Un cast est une affirmation faite à la compilation sur des données d'exécution : le crash a quand même lieu, simplement plus tard et à un endroit sans contexte.
Bloque : caster à la frontière annule la raison même pour laquelle la codebase est typée.
Portéesrc/**apps/**/src/**
Tagstypescriptvalidationcorrectness
02 Aucune promesse orpheline
errorChaque promesse est attendue, renvoyée, ou explicitement abandonnée avec `void` accompagné d'un `.catch` qui journalise. Un appel asynchrone nu, posé comme une simple instruction, est refusé. Sur les versions modernes de Node, un rejet non traité arrête le process : un appel analytique lancé sans être attendu emporte le handler de requête qui l'a ignoré.
Bloque : le mode de défaillance, c'est la mort du process sous charge de production.
Portéesrc/**apps/**/src/**
Tagstypescriptasyncreliability
03 Chaque appel sortant a un timeout
errorLes appels de client HTTP, les instructions de base de données et les prises de verrou passent un timeout explicite ou un `AbortSignal`. Node ne fixe aucun timeout de socket par défaut : une seule dépendance bloquée retient les connexions et le travail de l'event loop jusqu'à épuisement du pool, et c'est tout le service qui tombe — pas seulement la fonctionnalité qui l'a appelée.
Bloque : un timeout absent transforme le ralentissement d'un partenaire en panne chez vous.
Portéesrc/**apps/**/src/**
Tagsreliabilityhttpresilience
04 Lisez la configuration une fois, au démarrage
errorLes variables d'environnement sont lues et parsées dans le module de configuration, puis importées depuis là. `process.env.X` ailleurs dans le code applicatif est refusé. Une configuration parsée au démarrage fait échouer le déploiement ; une configuration lue paresseusement échoue à la première requête qui atteint cette branche, peut-être des semaines plus tard.
Bloque : c'est ce qui décide si une erreur de configuration est détectée par le déploiement ou par un client.
Portéesrc/**apps/**/src/**
Tagsconfigreliability
05 Les handlers de tâches de fond sont idempotents
errorUn handler de file ou de cron supporte d'être exécuté deux fois sur le même payload — protégez-le par une contrainte d'unicité, une clé d'idempotence ou un upsert. La livraison au moins une fois est la norme de tous les brokers sérieux : un retry qui débite une carte deux fois est un incident programmé, pas un cas limite.
Bloque : des effets de bord dupliqués sur de l'argent ou des notifications sont visibles du client et difficiles à défaire.
Portéesrc/**/jobs/**src/**/workers/**src/**/*worker*src/**/*job*
Tagsreliabilityqueueidempotency
06 L'argent est en entiers ou en décimaux, jamais en flottants
errorLes montants monétaires sont des entiers en unité mineure ou un type décimal, de bout en bout. `parseFloat`, l'arithmétique en `number` sur des prix et les colonnes flottantes sont refusés. Le flottant binaire ne sait pas représenter 0,1 : les totaux dérivent de quelques centimes, et cette dérive finit sur une facture client.
Bloque : une erreur financière est irrécupérable là où ça compte — la confiance.
Portéesrc/**
Tagscorrectnessbilling
07 Levez des erreurs typées, avec des codes stables
warningLevez une classe d'erreur du module d'erreurs partagé, portant un `code` stable. `throw new Error("failed")` et lever une chaîne sont refusés. Les appelants ne peuvent pas se brancher sur de la prose : ils finissent par filtrer sur le texte du message — et une simple relecture éditoriale change alors le comportement en production.
Avertit : cela dégrade la maintenabilité plutôt que de casser le chemin de code actuel.
Portéesrc/**apps/**/src/**
Tagserrorsmaintainability
08 Journalisez via le logger, avec un identifiant de corrélation
warningLe code applicatif journalise via le logger structuré partagé, en passant un objet qui contient l'identifiant de la requête ou du job. `console.log` et `console.error` hors des scripts et du bootstrap sont refusés. Des lignes non structurées ne peuvent être ni filtrées, ni corrélées, ni échantillonnées : elles coûtent cher à stocker et ne rendent rien pendant un incident.
Avertit : coût opérationnel réel, risque fonctionnel nul.
Portéesrc/**apps/**/src/**
Tagsobservabilitylogging
09 Le temps se manipule en instants UTC
warningStockez, transmettez et comparez des valeurs UTC conscientes du fuseau ; ne convertissez vers un fuseau local qu'au moment de l'affichage. Dériver de la logique métier d'une simple chaîne `YYYY-MM-DD`, ou du fuseau local du serveur, est refusé — cela fonctionne jusqu'au jour où la région de déploiement change ou où l'heure bascule.
Avertit : les bugs apparaissent deux fois par an et aux frontières de région, pas le jour du merge.
Portéesrc/**
Tagscorrectnesstime
10 Les nouvelles dépendances arrivent épinglées et cadrées
warningUn paquet ajouté au manifeste est épinglé sur une version exacte ou une plage bornée, va dans `devDependencies` quand seul l'outillage l'utilise, et ne fait pas doublon avec autre chose que le même changement ajoute — deux clients HTTP, deux bibliothèques de dates, deux bibliothèques de validation. Les agents ajoutent un paquet par réflexe ; chaque dépendance ajoutée est une surface de supply chain permanente et la future montée de version de quelqu'un.
Avertit : parfois la dépendance est le bon choix — l'objectif est qu'en ajouter une devienne une décision plutôt qu'un réflexe.
Portéepackage.json**/package.json
Tagssupply-chaindependencies
11 Les fonctions exportées déclarent leur type de retour
infoLes fonctions exportées d'un module ou d'un package annotent leur type de retour au lieu de s'en remettre à l'inférence. Un type de retour inféré signifie qu'un refactor interne peut modifier en silence un contrat public, et que l'erreur apparaîtra dans le build d'un consommateur plutôt que dans cette PR.
Indicatif : cela améliore le signal en revue ; rien ne casse aujourd'hui.
Portéepackages/**/src/**src/**/index.ts
Tagstypescriptapi-design
Avant de tout importer
La sévérité est le levier qui décide si l'équipe garde la gate activée. Seul error bloque un merge — importez d'abord ces règles en warning, regardez ce qui se déclenche réellement sur quelques semaines de vraies pull requests, et promouvez celles qui l'ont mérité. Et réécrivez les globs de portée : les chemins ci-dessus sont des exemples, et une règle ne s'applique qu'aux fichiers correspondant à ses globs.
Les règles entrent dans Normik une par une — via le formulaire du playbook, ou en laissant un agent de code les proposer par MCP à partir d'un repo existant. Il n'y a pas d'import markdown en masse : le bouton de copie ci-dessus sert à votre AGENTS.md, à une PR où l'équipe débat de la liste, et à garder les règles sous les yeux pendant que vous saisissez celles que vous avez retenues.
Chaque règle ici est jugée comme Normik la juge : sur les lignes que la pull request ajoute. Rien dans ce playbook ne dépend de la description de la PR, d'un fichier que le diff ne touche pas, ni de l'historique d'un fichier — ce sont de vrais standards de revue, mais une gate qui n'en voit pas la preuve ne pourrait que rester muette ou deviner.
L'anatomie d'une règle, le comportement des sévérités et des globs, le versionnage et l'export AGENTS.md sont détaillés dans la documentation du playbook.