Monorepo Conventions¶
Monorepo TypeScript : React Native (Expo) pour le front, NestJS pour l'API, pnpm + Turborepo.
Regle absolue¶
Ne JAMAIS refactoriser du code existant sauf demande explicite. Les conventions s'appliquent au nouveau code uniquement. Si un refactoring semble benefique, demander confirmation d'abord.
DO NOT¶
- DO NOT ajouter de commentaires sur du code explicite. Comments uniquement pour regles metier non-evidentes ou contexte historique
- DO NOT paraphraser le code dans les commentaires ("// Fetch data" avant un fetch)
- DO NOT ecrire les commentaires de code en francais → tous les commentaires inline / JSDoc des fonctions, methodes et types restent en anglais (convention du codebase, comme les noms de tests
describe()/it()— exception : les tests d'integration API suivent le naming FRQuand …/Alors …, cf. integration-test-conventions.mdc) - Exceptions ou le francais est autorise : (1) les scripts de migration Flyway (
migration/flyway/*.sql), (2) la JSDoc descriptive d'un cron task (le bloc qui explique a quoi sert le cron, sa frequence, son cap journalier, etc.), (3) le bloc d'explication d'un script one-shot (projects/api/scripts/**). Les commentaires explicatifs DANS le code du cron / du script restent en anglais
DO¶
- Utiliser
_comme separateur de milliers pour les literaux numeriques >= 1_000 - Literaux en centimes —
Cents(...)mais aussi toute valeur cents nue (assertion/payload de test, seed) : separer euros et centimes avec_sur les 2 derniers chiffres —Cents(1_000_00)= 1 000 EUR,total: 550_00= 550 EUR,Cents(1_99)= 1,99 EUR. Le separateur milliers s'applique a la partie entiere en euros uniquement - Toujours verifier les composants/hooks/helpers existants avant d'en creer de nouveaux
- Code auto-documentant > commentaires
- Preferer
Err('error-code' as const)pour preserver les unions litterales d'erreur avant d'introduire des alias de typeResultdedies uniquement a l'inference - TS6:
compilerOptions.typesdefaults to[](no auto@types/*). Globals need an explicittypesentry. Isomorphic UUID:import { v4 } from 'uuid', not globalcrypto,node:crypto, or a sibling workspace package tsconfig.base.jsonusesmoduleResolution: bundler. Do not re-declare it in extenders. API keepscustomConditions: ["node"]for TypeORM 0.3.5.front-appstays on TypeScript 4.9 and does not extend this base.- TS6:
noUncheckedSideEffectImportsdefaults true. Side-effect CSS/polyfill imports needdeclare module '*.css'(or equivalent), not flipping the flag. - Docker
buildafter turbo prune: do not calltsc. The cmd-shim targets a nestednode_modules/typescript/bin/tscthat prune does not install once that version is the hoisted one. Commons: tsdown emits JS+dts. Vite BO apps (noEmit):vite buildonly; typecheck stays intype-check.
DO NOT (cross-cutting)¶
- DO NOT anticiper des besoins futurs — coder pour le sprint actuel. La complexite est une dette (YAGNI/KISS)
- DO NOT extraire un query builder partagé entre getMany et getOne — chaque méthode écrit sa query
- DO NOT re-déclarer une union déjà portée par un validator (
typeof x.T) - DO NOT rendre optionnel un champ du payload Graphile si tous les enqueue le posent — le validator = le contrat d'enqueue
- DO NOT rendre required un champ GET nouveau dans la même PR que l'API si Maestro tape une API stale — PR 1 : API + validator client optional. PR stackée : required, une fois l'API sur develop / e2e
- DO NOT creer d'abstractions inutiles — pas de hook wrapper pour un seul appel, pas de type intermediaire utilise une seule fois, pas de const intermediaire sans valeur ajoutee
- DO NOT inliner un appel de fonction multi-lignes dans
lift().add(...)— extraire un const en amont, le chain reste plat. Un objet literal ou une expression courte peut rester inline - DO NOT utiliser
as unknown as Xpour contourner le typage — trouver une solution typee (className,.web.tsx, type guard) ou corriger le type source - DO NOT ecrire les literaux
Cents(...)avec un_sur la valeur cent totale (Cents(100_000)) — illisible ; utiliserCents(1_000_00)(cf. DO ci-dessus) - DO NOT extraire des helper functions one-liner ou single-use quand un const local ou du code inline reste plus lisible. Exception : ternaires imbriqués (3+ branches) → early returns dans une fonction locale
- DO NOT utiliser
voidpour ignorer une Promise —await(dans une fonctionasync) ou gestion explicite de l'erreur - DO NOT
awaitune Promise dans la condition d'unif(if (!(await x()))) — extraire dans une const d'abord, la condition lit un booléen - DO NOT ajouter un alias npm dans
package.jsonpour un CLI ops (scripts/*.script.ts) — l'invoquer directement avecpnpm exec tsx - DO NOT spread conditionnel pour props optionnelles —
...(x ? { key: x } : {})ajoute du bruit sans gain ; assignerkey: x ? x : undefined(ouundefinedseul) dans l'objet literal — les libs/API ignorentundefined, pas besoin de mini-objet spreadé - DO NOT spread le retour d'un helper dans un payload / params object (
...computeX()). Nommer les champs au call-site : un spread cache le contrat et casse si le helper gagne un champ - DO prefer expliciter les merges de domain objects fermes avec
produce()+??plutot qu'un deep merge generique ou une cascade deif - DO NOT hesiter a challenger un choix qui casse l'architecture — expliquer l'impact et proposer l'alternative
- DO NOT dupliquer les libellés d'un enum admin entre le BO et l'API (Slack, mails) — ils divergent. Colocaliser
as const satisfies Record<Enum, string>à côté de l'enum dans@bricks-common/api-communication-bricksoffice - DO NOT utiliser
satisfiesquandconst x: T = { ... }vérifie la contrainte sans cast.satisfiesseulement si annoter élargirait un littéral dont un caller a encore besoin - DO NOT appeler
.toISOString()pour sérialiser uneDateen JSON —Date#toJSON()appelle déjàtoISOString() - DO NOT traiter un changement de libellé (export XLSX, i18n) comme un touch du legacy — intouchable = tables, kinds WT, routes et logique d'investissement 2022
- DO NOT ouvrir une PR ready-for-review — toujours draft (
gh pr create --draft,draft: true). Jamaisgh pr ready/draft: false; un humain un-draft - DO NOT citer un ticket Linear (
BRI-xxxx) dans un.mdc— les rules décrivent le contrat actuel, pas l'historique de livraison - DO NOT utiliser
.default()sur un champ de body d'endpoint que le client doit envoyer — le rendre obligatoire. Une feature pas encore en prod n'est pas une raison de default silencieux
Cross-cutting¶
match().exhaustive()de ts-pattern pour les branchements complexes (3+ cas). Simpleif/elseou early return reste prefere quand c'est plus lisible. UnResulten erreur :if (!result.ok) return match(error)— succès en dernier. Pas deifimbriqués sur le code d'erreur- Data transforms :
space-liftlift()pour 2+ operations.filter+map→collect()(undefineddroppe l'élément) - Validation :
zodpour les nouveaux validateurs,idonttrustlikethatpour le code existant (migration progressive) - Branded types depuis
@bricks-common/api-communicationpour IDs, montants, dates dayjspour les dates (pas moment). Délai en jours :dayjs(now).diff(at, 'day') >= n, pas de constanteMS_PER_DAYproduce()d'immer pour les mutations immutables d'objets
Validation TypeScript & commit¶
Apres chaque serie de modifications, executer tsc sur le projet concerne et corriger jusqu'a zero erreur. Ne pas considerer une tache terminee tant que tsc echoue sur les fichiers touches.
Avant de committer, re-executer les checks sur les fichiers modifies :
- TypeScript — zero erreur toleree
- Biome :
npx @biomejs/biome check <fichiers>— formatting + lint
Si les checks echouent, corriger et re-verifier. Ne jamais push du code qui ne passe pas tsc ou biome.
Commandes par projet :
- api : cd projects/api && npx tsc -p tsconfig.build.json --noEmit + npx @biomejs/biome check <fichiers>
- front-mobile-app : cd projects/front-mobile-app && npx tsc --noEmit + npx @biomejs/biome check <fichiers>
- app-pdp-financement : cd projects/app-pdp-financement && npx tsc --noEmit + npx @biomejs/biome check <fichiers>
Note : les dependances communes (@bricks-common/*, @bricks-common-front/*) doivent etre buildees avant tsc. Si tsc echoue sur des modules manquants, lancer npx turbo run build --filter=<package> sur les packages concernes.
Testing¶
See Test Conventions — Vitest (API + packages), Jest (front apps), fake timers, mocking, config pattern, tsconfig structure
Auto-apprentissage des rules¶
Quand le dev te corrige sur un pattern, une convention ou un choix technique :
1. Appliquer la correction immediatement
2. Ajouter une ligne DO/DO NOT dans la section appropriee de front-conventions.mdc ou generic-monorepo-rules.mdc
3. Ne pas demander — le faire directement. Une correction du dev = une nouvelle rule partagee
- DO NOT utiliser le non-null assertion operator sur une valeur (
value!) → guard /?./??. Exception : definite assignment sur champs de classe ORM/DTO (prop!: Type) requis parstrictPropertyInitialization(TypeORM hydrate hors constructeur) - DO NOT utiliser le type assertion (
as SomeType) → utiliser un type guard ou restructurer le code pour que TypeScript infere correctement. Pour itérer les clés d'un objet connu :keysOf(obj)(projects/api/src/__new/lib/object/keysOf.ts) — encapsule le castObject.keys→keyof T. PourNonEmptyArray: type guard (arr is NonEmptyArray<T>), pas[arr[0], ...arr.slice(1)](identique àarr)