Conventions de code
Langue
| Contexte | Langue |
|---|---|
| Code, noms de variables, messages d'erreur techniques | Anglais |
| Commentaires de code | Français |
| Messages de commit | Français (cf. Workflows) |
Documentation (cette doc et autres .md) | Français |
Cette double règle vient du fait que le code est lu par des outils anglo-saxons (Stack Overflow, IDEs, IA) tandis que la collaboration humaine se fait en français.
TypeScript
- Mode strict activé partout (
strict: truedans chaquetsconfig.json) - Pas de
any, pas de@ts-ignoreni@ts-expect-error - L'échappatoire au typage est
unknown+ narrowing explicite, ou un type guard - Pas d'inférence cachée : les fonctions exportées ont toujours leur type de retour annoté
Frontend
Styling
- Tokens design dans
packages/ui/src/tokens.ts(importés via@app/ui/tokens) — toujours utiliser ces objets (C,S,T,R,SH,G) plutôt que des valeurs en dur - Grille d'espacement 8 px : utiliser les clés
S[](S[2]=8px,S[4]=16px, etc.) - Tailwind acceptable pour le layout simple (flex, grid, padding) ;
tokens.tspour les valeurs de design - Pas de Tailwind pour
font-size,font-weight,line-height— toujours viaT.size.*,T.weight.* - Inline styles acceptables pour les micro-ajustements ponctuels ; à éviter pour les patterns récurrents (créer un composant)
Organisation
packages/ui/src/ # @app/ui — partagé par les 4 apps
├── components/ui/ # shadcn primitives (à ne pas modifier)
├── components/shared/ # TopHeader, BottomNav, ProtectedRoute…
├── services/api/{domain}.ts # Client HTTP par domaine
├── contexts/ # Providers React (auth, etc.)
└── tokens.ts # Tokens de design
apps/frontend-<role>/src/app/
├── App.tsx # Routes du rôle
└── pages/<role>/{Domain}Page.tsx # Une page = un composant racine
- Pages par rôle :
apps/frontend-learner/src/app/pages/learner/LearnerDashboardPage.tsx - Suffixe
Page.tsxpour les composants racine de route - Alias :
@/→src/de l'app courante ·@app/ui/*→packages/ui/src/* - Primitives de layout : privilégier
<Stack>,<Row>,<Grid>(cf. design system)
Données serveur
- TanStack Query v5 pour tout fetch authentifié — jamais de
useEffect+fetchbrut - Une
queryKeypar requête, structurée en tableau :['admin', 'users', { page, role }] - Mutations : utiliser
useMutation+ invalidation manuelle de la queryKey concernée - Erreurs API : intercepter
ApiError(notre wrapper), afficher un toastsonner
Backwards-compat
Pas de feature flags ni de shims pour de la rétrocompatibilité dans le frontend MVP. Quand une page est refactorée, on supprime l'ancienne, on supprime les imports orphelins, on n'écrit pas de "legacy".
Backend
Modules
Chaque domaine vit dans un module Nest dédié :
apps/api/src/{domain}/
├── {domain}.module.ts # Déclaration des controllers + services
├── {domain}.controller.ts # Endpoints HTTP, @Roles, @UseGuards
├── {domain}.service.ts # Logique métier, appels Prisma
├── dto/ # DTOs Zod importés depuis @app/contracts
└── {domain}.service.spec.ts # Tests Jest
Configuration
@nestjs/configcharge.envau boot- Le fichier
config/env.schema.tsdéfinit le schéma Zod des variables d'environnement attendues — l'app refuse de démarrer si une variable manque ou est invalide - Préfixe global :
app.setGlobalPrefix('api/v1')dansmain.ts
RBAC
- Decorator
@Roles('ADMIN')sur les controllers ou les méthodes — leRolesGuardvérifie la claim JWT - Isolation par propriété : implémentée dans le service (
where: { teacherId: user.sub, ... }), jamais en s'appuyant uniquement sur le rôle - Ne jamais bypasser un guard — si un endpoint doit être public, retire-le des modules protégés ou utilise
@Public()explicitement
Erreurs HTTP
400— payload invalide (validation Zod)401— token absent ou invalide403— rôle ou propriété insuffisants404— ressource introuvable (ou inaccessible — n'expose pas la différence)409— conflit (ex : email déjà pris)500— bug serveur (à logger en priorité)
Toujours retourner via les exceptions Nest (ForbiddenException, NotFoundException, etc.), jamais de res.status(...).
Base de données
Migrations
- Prisma uniquement, pas de SQL manuel
prisma migrate dev --name <description>en local,prisma migrate deployen prod et CI- Revue obligatoire des migrations avant merge — toute opération destructive (drop de colonne, renommage, changement de type) doit être flaguée
- Les migrations vivent dans
apps/api/prisma/migrations/et sont commitées au repo
Soft delete
- Toutes les entités métier ont un champ
deletedAt: DateTime? - "Supprimer" en UI =
UPDATE ... SET deletedAt = NOW(), jamaisDELETE - Les requêtes filtrent par défaut
WHERE deletedAt IS NULL— explicite via la clausewhere
Sécurité SQL
-
Pas de
$queryRawnon paramétré — toujours en template tagué :prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}` -
$executeRawstrictement paramétré avec interpolation Prisma
API
Validation
- Validation input via Zod — schémas définis dans
packages/contracts, pipelineZodValidationPipeglobal - Source unique de vérité : le frontend importe les mêmes types pour ses formulaires
- Pas de validation manuelle dans les controllers — si le payload arrive jusqu'au service, il est valide
Pagination
Format unique pour toutes les listes :
{
"items": [...],
"total": 123,
"page": 1,
"pageSize": 20
}
Paramètres query : ?page=1&pageSize=20. Limite max pageSize à 100 côté backend.
Sécurité
- Zéro secret en git — tout dans
.env(ignoré),.env.examplecommité avec valeurs factices - Hash mot de passe : Argon2id via le package
argon2(jamais bcrypt, jamais SHA brut) - JWT : access 15 min + refresh 7 jours avec rotation et révocation côté DB
- Helmet +
@nestjs/throttleractivés au boot - CORS : whitelist stricte (
http://localhost:5173en dev, domaine prod en prod) - Validation API-side systématique — ne jamais faire confiance au client
Tests
- Backend : Jest + supertest + Testcontainers (Postgres réel)
- Lancer :
npm run test --workspace apps/api - Tests
.spec.tsà côté du fichier testé
- Lancer :
- Frontend : pas de test automatisé en MVP (priorité produit)
- E2E : à mettre en place après stabilisation MVP (Playwright envisagé)
Code propre
- Pas de commentaire qui explique le quoi — le code bien nommé suffit
- Commentaires utiles : le pourquoi non évident, les invariants cachés, les contournements de bug
- Pas de docstring multi-paragraphes sur des fonctions évidentes — une ligne max
- Pas de fichier de planning ou d'analyse intermédiaire dans le repo — ces réflexions vivent dans les issues GitLab ou les ADR
Anti-patterns à éviter
- Une fonction qui prend plus de 5 paramètres positionnels — passer un objet
- Une variable
*Manager,*Helper,*Utilsans plus de précision — chercher le vrai concept métier - Une fonction qui mélange validation, transformation et persistance — séparer
- Un composant React de plus de 300 lignes — découper en sous-composants ou en hooks
- Un
useEffectavec dépendances complexes pour synchroniser deux états — repenser le state shape