Commentaires et Documentation TypeScript
Principe fondamental : Expliquer le "pourquoi", pas le "quoi"
> Règle d'or : Les commentaires doivent expliquer la raison d'une décision technique, d'une règle métier ou d'un contournement. Le code bien écrit doit être auto-descriptif pour expliquer ce qu'il fait.
Récapitulatif des principes
✅ Commentaires de valeur
- Contexte métier : règles, contraintes, réglementation
- Décisions techniques : pourquoi cette approche plutôt qu'une autre
- Contraintes de sécurité : standards, audits, certifications
- Optimisations : benchmarks, trade-offs, limitations mesurées
- Évolutions temporelles : migration, dépréciation, roadmap
❌ Commentaires à éviter
- Répétition du code : ce qui est déjà évident avec TypeScript
- Instructions : comment utiliser (→ documentation séparée)
- Descriptions fonctionnelles : ce que fait le code (→ noms explicites)
- Commentaires obsolètes : non maintenus, contradictoires
🎯 Questions à se poser avant d'écrire un commentaire
- Pourquoi cette solution plutôt qu'une autre ?
- D'où viennent ces valeurs/seuils/règles ?
- Quels risques ou contraintes doivent être connus ?
- Quelle évolution est prévue pour cette partie ?
- Un développeur arrivant dans 6 mois comprendrait-il le contexte ?
> Principe ultime : Si votre commentaire répond à "Qu'est-ce que fait ce code ?", il est probablement inutile. S'il répond à "Pourquoi le code fait-il cela de cette manière ?", il est probablement précieux.
Types de commentaires
Commentaires de ligne
- Utiliser
//pour les commentaires courts - Ajouter un espace après
// - Focus sur le contexte et les raisons, pas sur l'implémentation
// ✅ Bon usage : expliquer le contexte et les contraintes
function calculatePrice(basePrice: number, taxRate: number): number {
// Conformité réglementaire française : TVA obligatoire selon art. 256 CGI
const taxAmount = basePrice * taxRate;
// Éviter les erreurs d'arrondi des calculs monétaires (IEEE 754)
return Math.ceil((basePrice + taxAmount) * 100) / 100;
}
// ✅ Bon usage : justifier une approche non-évidente
function processLargeDataset(data: any[]): Promise<void> {
// Traitement par lots pour éviter d'épuiser la mémoire sur des jeux > 10k éléments
const batchSize = 1000;
// setTimeout(0) pour libérer le thread principal entre chaque lot
return new Promise(resolve => {
// Implementation avec setTimeout...
});
}
// ❌ Mauvais : répéter ce que fait le code
function calculatePrice(basePrice: number, taxRate: number): number {
// Calculer le montant de la taxe
const taxAmount = basePrice * taxRate;
// Retourner le prix de base plus la taxe
return basePrice + taxAmount;
}
// ❌ Mauvais : commentaires évidents
function getUserById(id: string): User {
// Appeler la fonction findUser avec l'id
return this.userRepository.findUser(id);
}
Commentaires de bloc
- Utiliser
/* */pour les commentaires multi-lignes - Réserver pour des explications de contexte complexe, d'architectures ou de contraintes métier importantes
/*
* Configuration d'authentification JWT optimisée pour notre contexte
*
* Choix techniques motivés par :
* - Tokens courts (15min) : réduire la fenêtre d'exposition en cas de compromission
* - Refresh tokens longs (7j) : équilibrer sécurité/UX pour nos utilisateurs métier
* - Stockage en mémoire : éviter la persistance locale sensible (RGPD/ANSSI)
*
* SÉCURITÉ : Clés via variables d'environnement uniquement (jamais en dur)
* Longueur minimale 256 bits conforme aux recommandations ANSSI 2024
*/
export const jwtConfig = {
secret: process.env.JWT_SECRET,
expiresIn: '15m',
refreshExpiresIn: '7d'
};
/*
* IMPORTANT : Pattern temporaire de migration vers le nouveau système d'auth
*
* Ce bloc gère la coexistence entre :
* - L'ancien système de sessions (legacy-auth-service)
* - Le nouveau système JWT (auth-service-v2)
*
* Migration prévue : Q2 2025
* À supprimer après validation complète en production
* @see ticket #AUTH-2024-001
*/
Documentation JSDoc
Objectif de la documentation JSDoc
La documentation JSDoc doit se concentrer sur :
- Le comportement attendu et les effets de bord
- Les contraintes métier et les validations appliquées
- Les cas d'erreur et leur gestion
- Les exemples d'usage dans le contexte métier
- Les prérequis et dépendances
Documentation des fonctions
- Documenter toutes les fonctions publiques
- Expliquer le comportement, les contraintes métier et les cas d'erreur
/**
* Valide et normalise une adresse email selon nos standards métier
*
* COMPORTEMENT :
* - Normalisation automatique : suppression espaces, conversion minuscules
* - Validation basique ou stricte selon le contexte d'usage
* - Mode strict : applique RFC 5322 pour l'interopérabilité B2B
*
* CONTRAINTES MÉTIER :
* - Emails internes : validation basique suffisante
* - Emails partenaires/clients : validation stricte obligatoire
* - Conformité RGPD : pas de normalisation sans consentement
*
* @param email - L'adresse email à valider
* @param strict - Validation stricte pour contexte B2B/partenaires
* @returns L'email normalisé et validé
*
* @throws {ValidationError} Format invalide selon les critères choisis
* @throws {TypeError} Type d'entrée incorrect (doit être string)
*
* @example
* ```typescript
* // Usage interne (équipe, back-office)
* const internalEmail = validateEmail(' [email protected] ');
* // Résultat: '[email protected]'
*
* // Usage B2B/partenaires (interopérabilité)
* const businessEmail = validateEmail('[email protected]', true);
* // Applique validation stricte RFC 5322
* ```
*
* @since 1.2.0
* @see {@link https://tools.ietf.org/html/rfc5322} Spécification RFC 5322
*/
export function validateEmail(email: string, strict: boolean = false): string {
if (typeof email !== 'string') {
throw new TypeError('Email must be a string');
}
const normalizedEmail = email.trim().toLowerCase();
const basicRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const strictRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](a-zA-Z0-9- 0 61 a-zA-Z0-9.md)?(?:\.[a-zA-Z0-9](a-zA-Z0-9- 0 61 a-zA-Z0-9.md)?)*$/;
const regex = strict ? strictRegex : basicRegex;
if (!regex.test(normalizedEmail)) {
throw new ValidationError(`Invalid email format: ${email}`);
}
return normalizedEmail;
}
Documentation des classes
/**
* Service de gestion des utilisateurs avec sécurité renforcée
*
* RESPONSABILITÉS :
* - Centralisation des opérations utilisateur (CRUD, auth, rôles)
* - Application des politiques de sécurité (tentatives, verrouillage)
* - Audit et traçabilité des actions sensibles
*
* CONTRAINTES SÉCURITÉ :
* - Max 5 tentatives de connexion (protection force brute)
* - Verrouillage temporaire 15min (équilibre sécurité/UX)
* - Audit obligatoire des actions admin (conformité)
*
* CONTEXTE ARCHITECTURE :
* - Pattern Repository : découplage persistance/métier
* - Notifications asynchrones : éviter les timeouts UX
* - Logging optionnel : faciliter debug sans impact performance
*
* @example
* ```typescript
* // Initialisation avec dépendances injectées
* const userService = new UserService(userRepository, emailService, logger);
*
* // Création avec validation métier automatique
* const user = await userService.createUser({
* email: '[email protected]',
* firstName: 'John',
* lastName: 'Doe'
* });
*
* // Authentification avec protection anti-force brute
* const authResult = await userService.authenticate('[email protected]', 'password');
* ```
*
* @since 1.0.0
* @author Équipe Sécurité & Développement
*/
export class UserService {
// Politique de sécurité : protection contre les attaques par force brute
private readonly maxLoginAttempts = 5;
private readonly lockoutDuration = 15 * 60 * 1000; // 15 minutes
/**
* Initialise le service avec injection de dépendances
*
* CHOIX ARCHITECTURE :
* - Repository pattern : facilite les tests et la maintenance
* - Service email découplé : permet le remplacement (SMTP/SES/SendGrid)
* - Logger optionnel : évite la dépendance forte pour tous les contextes
*
* @param userRepository - Accès données utilisateur (abstraction persistance)
* @param emailService - Service notifications (découplé du transport)
* @param logger - Audit et debug (optionnel pour flexibilité)
*/
constructor(
private readonly userRepository: UserRepository,
private readonly emailService: EmailService,
private readonly logger?: Logger
) {}
/**
* Crée un nouveau compte avec validation métier complète
*
* PROCESSUS MÉTIER :
* 1. Validation données + unicité email
* 2. Hachage sécurisé mot de passe (bcrypt, 12 rounds)
* 3. Audit création (traçabilité RGPD)
* 4. Notification async (éviter timeout UX)
*
* @param userData - Données utilisateur validées côté client
* @returns Promise avec utilisateur créé (SANS mot de passe pour sécurité)
*
* @throws {ValidationError} Données invalides ou contraintes métier
* @throws {ConflictError} Email déjà utilisé (unicité)
* @throws {DatabaseError} Problème technique persistance
*/
async createUser(userData: CreateUserRequest): Promise<UserResponse> {
// Implementation avec audit et validation...
}
}
Documentation des interfaces et types
/**
* Représente un utilisateur dans le système
*
* @interface User
* @since 1.0.0
*/
export interface User {
readonly id: string;
readonly email: string;
firstName: string;
lastName: string;
/**
* Rôle de l'utilisateur dans le système
* @default 'user'
*/
role: UserRole;
/**
* Statut actuel du compte
* @default 'active'
*/
status: UserStatus;
readonly createdAt: Date;
readonly updatedAt: Date;
/**
* Date de dernière connexion
* @nullable Null si l'utilisateur ne s'est jamais connecté
*/
lastLoginAt: Date | null;
}
/**
* Données requises pour créer un nouvel utilisateur
*
* @interface CreateUserRequest
* @since 1.0.0
*/
export interface CreateUserRequest {
/** Adresse email (doit être unique) */
email: string;
/** Prénom (minimum 2 caractères) */
firstName: string;
/** Nom de famille (minimum 2 caractères) */
lastName: string;
/**
* Mot de passe (minimum 8 caractères avec majuscule, minuscule et chiffre)
* @minLength 8
*/
password: string;
/**
* Rôle à assigner à l'utilisateur
* @default 'user'
* @optional
*/
role?: UserRole;
}
/**
* Rôles disponibles dans le système
*
* @typedef UserRole
* @since 1.0.0
*/
export type UserRole =
| 'admin' // Accès complet au système
| 'moderator' // Gestion des contenus et utilisateurs
| 'user' // Utilisateur standard
| 'guest'; // Accès limité en lecture seule
/**
* Configuration pour la pagination des résultats
*
* @interface PaginationOptions
* @template T - Type des éléments paginés
*/
export interface PaginationOptions {
/**
* Numéro de page (commence à 1)
* @minimum 1
* @default 1
*/
page?: number;
/**
* Nombre d'éléments par page
* @minimum 1
* @maximum 100
* @default 20
*/
limit?: number;
/**
* Champ sur lequel trier
* @default 'createdAt'
*/
sortBy?: string;
/**
* Ordre de tri
* @default 'desc'
*/
sortOrder?: 'asc' | 'desc';
}
Commentaires de code métier et décisions techniques
Explications d'algorithmes et choix techniques
Documenter les décisions non-évidentes et leurs justifications :
/**
* Validation de carte bancaire par algorithme de Luhn
*
* POURQUOI CET ALGORITHME :
* - Standard industrie : détecte 100% des erreurs de saisie simples
* - Détection ~90% des transpositions de chiffres adjacents
* - Léger et sans dépendance : évite les libs externes pour cette validation critique
*
* CONTEXTE MÉTIER :
* - Validation côté client : améliore UX avant soumission
* - Validation côté serveur : sécurité (jamais faire confiance au client)
* - Compatible tous types cartes : Visa, MasterCard, AMEX, etc.
*/
function validateCreditCard(cardNumber: string): boolean {
// Normalisation : retirer formatage utilisateur (espaces, tirets)
const cleanNumber = cardNumber.replace(/[\s-]/g, '');
// Validation préalable : uniquement des chiffres acceptés
if (!/^\d+$/.test(cleanNumber)) {
return false;
}
let sum = 0;
let isEven = false;
// Algorithme Luhn : parcours de droite à gauche (sens naturel cartes)
for (let i = cleanNumber.length - 1; i >= 0; i--) {
let digit = parseInt(cleanNumber[i], 10);
if (isEven) {
digit *= 2;
// Optimisation : soustraire 9 équivaut à additionner les chiffres du résultat
// Ex: 16 → 1+6=7 ou 16-9=7
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
// Validation finale : somme divisible par 10
return sum % 10 === 0;
}
/**
* Cache intelligent avec stratégie LRU et éviction proactive
*
* POURQUOI CETTE APPROCHE :
* - LRU : préserve les données les plus utiles (principe de localité)
* - Éviction proactive : évite les pics mémoire sur gros volumes
* - TTL variable : adapte la durée selon la criticité des données
*
* CONTEXTE PERFORMANCE :
* - API tierces lentes : cache réduit latence de 800ms à 50ms
* - Coût réseau : économise 95% des appels répétitifs
* - Mémoire limitée : éviction empêche les fuites sur longues sessions
*/
class SmartCache<T> {
private cache = new Map<string, { data: T; timestamp: number; ttl: number }>();
private accessOrder: string[] = [];
private readonly maxSize: number;
constructor(maxSize: number = 1000) {
// Taille par défaut basée sur profiling : équilibre mémoire/performance
this.maxSize = maxSize;
}
set(key: string, value: T, ttl: number = 300000): void {
// TTL par défaut 5min : équilibre fraîcheur/performance pour données métier
const now = Date.now();
// Éviction préventive si limite atteinte
if (this.cache.size >= this.maxSize) {
this.evictLRU();
}
this.cache.set(key, { data: value, timestamp: now, ttl });
this.updateAccessOrder(key);
}
private evictLRU(): void {
// Retirer l'élément le moins récemment utilisé
const lruKey = this.accessOrder.shift();
if (lruKey) {
this.cache.delete(lruKey);
}
}
}
Documentation des règles métier
Expliquer les contraintes business et leur origine :
export class PricingService {
/**
* Moteur de calcul tarifaire conforme aux règles commerciales 2024
*
* CONTEXTE BUSINESS :
* - Fidélisation client : remises progressives stimulent le réachat
* - Optimisation logistique : remises quantité réduisent coûts manutention
* - Conformité fiscale : TVA variable selon pays de livraison (UE)
* - Stratégie commerciale : port gratuit favorise panier moyen
*
* RÈGLES MÉTIER (ordre d'application obligatoire) :
* 1. Fidélité : 5% >1k€, 10% >5k€ (historique annuel glissant)
* 2. Quantité : 3% ≥5 articles, 7% ≥10 (même commande)
* 3. TVA : France 20%, Allemagne 19%, Belgique 21% (directive UE)
* 4. Port : gratuit >50€ (incitation panier), sinon 4.90€ forfait
*
* @param basePrice - Prix unitaire catalogue HT
* @param quantity - Quantité commandée (validation >0 côté appelant)
* @param customerTier - Niveau fidélité (calcul externe service CRM)
* @param country - Code pays livraison (ISO 3166-1 alpha-2)
* @returns Détail complet calcul pour affichage client et audit
*/
calculateFinalPrice(
basePrice: number,
quantity: number,
customerTier: CustomerTier,
country: CountryCode
): PriceCalculation {
let currentPrice = basePrice * quantity;
const discounts: Discount[] = [];
// RÈGLE 1 : Remise fidélité (basée sur chiffre affaires annuel)
const loyaltyDiscount = this.calculateLoyaltyDiscount(currentPrice, customerTier);
if (loyaltyDiscount > 0) {
discounts.push({
type: 'loyalty',
amount: loyaltyDiscount,
percentage: loyaltyDiscount / currentPrice * 100,
// Audit : traçabilité obligatoire pour contrôle commercial
reason: `Fidélité ${customerTier} - CA annuel qualifiant`
});
currentPrice -= loyaltyDiscount;
}
// RÈGLE 2 : Remise quantité (optimisation logistique)
const quantityDiscount = this.calculateQuantityDiscount(currentPrice, quantity);
if (quantityDiscount > 0) {
discounts.push({
type: 'quantity',
amount: quantityDiscount,
percentage: quantityDiscount / currentPrice * 100,
reason: `Volume ${quantity} articles - réduction coûts logistiques`
});
currentPrice -= quantityDiscount;
}
// RÈGLE 3 : TVA selon réglementation européenne
const vatRate = this.getVatRate(country);
const vatAmount = currentPrice * vatRate;
currentPrice += vatAmount;
// RÈGLE 4 : Politique commerciale port gratuit
const shippingCost = currentPrice >= 50 ? 0 : 4.90;
return {
basePrice: basePrice * quantity,
discounts,
subtotal: currentPrice - vatAmount,
vatRate,
vatAmount,
shippingCost,
finalPrice: currentPrice + shippingCost,
// Métadonnées pour audit et support client
calculationTimestamp: new Date(),
appliedRules: ['loyalty', 'quantity', 'vat', 'shipping']
};
}
private getVatRate(country: CountryCode): number {
// Taux officiels UE 2024 - mise à jour annuelle obligatoire
const vatRates = {
'FR': 0.20, // France : 20% (taux normal)
'DE': 0.19, // Allemagne : 19% (Mehrwertsteuer)
'BE': 0.21, // Belgique : 21% (BTW/TVA)
'ES': 0.21, // Espagne : 21% (IVA)
'IT': 0.22 // Italie : 22% (IVA)
};
// Défaut France si pays non supporté (politique commerciale)
return vatRates[country] ?? vatRates['FR'];
}
}
TODO et commentaires temporaires
Gestion structurée des TODOs
Les TODOs doivent expliquer pourquoi quelque chose n'est pas fait, pas quoi faire :
export class PaymentService {
async processPayment(payment: PaymentRequest): Promise<PaymentResult> {
// TODO: Validation carte bancaire manquante
// POURQUOI : Service de validation Stripe indisponible en environnement dev
// IMPACT : Risque de fraudes non détectées en production
// PLAN : Migration vers service interne validation prévue Q1 2025
// @author: [email protected]
// @date: 2024-01-15
// @priority: HIGH
// @issue: #1234
// FIXME: Timeouts non gérés avec conséquences critiques
// POURQUOI : SDK fournisseur ne respecte pas timeout configuré
// IMPACT : Sessions utilisateur bloquées, pics charge serveur
// REPRODUCTION : Service paiement >30s → thread bloqué
// CONTOURNEMENT : Circuit breaker temporaire implémenté
// @see: https://github.com/company/app/issues/5678
// HACK: Workaround bug fournisseur payment-sdk v2.0.x
// POURQUOI : Bug arrondi monétaire dans la lib (ticket #PAY-2024-001)
// RISQUE : Écarts centimes sur gros volumes
// TEMPORAIRE : Suppression prévue avec payment-sdk v2.1.0 (mars 2025)
// @remove-after: 2025-03-01
if (payment.provider === 'legacy-provider') {
// Forcer arrondi pour éviter erreurs calcul IEEE 754
payment.amount = Math.round(payment.amount * 100) / 100;
}
// NOTE: Conformité réglementaire France uniquement
// POURQUOI : Loi anti-blanchiment française (seuil 1000€)
// LIMITATION : Internationalisation nécessitera refonte
// ÉVOLUTION : Voir ticket architecture #9999 pour design multi-pays
if (payment.currency === 'EUR' && payment.amount > 1000) {
// Déclaration TRACFIN obligatoire (Code monétaire financier art. L561-15)
await this.reportLargeTransaction(payment);
}
return this.executePayment(payment);
}
}
Commentaires de dépréciation
Expliquer les raisons du changement et les impacts :
/**
* @deprecated Remplacé par `validateEmailV2` pour des raisons de sécurité
*
* POURQUOI LA DÉPRÉCIATION :
* - Validation insuffisante : bypass possible avec emails malformés
* - Pas de normalisation : incohérences base de données
* - Performance : regex non optimisée (x10 plus lente)
*
* MIGRATION OBLIGATOIRE :
* - Version actuelle maintenue jusqu'à 3.0.0 uniquement
* - Migration automatique disponible : `npx migrate-email-validation`
* - Support équipe : [email protected]
*
* @see {@link validateEmailV2} Nouvelle implémentation sécurisée
* @since 1.0.0
* @deprecatedSince 2.5.0
* @removeIn 3.0.0
*/
export function validateEmail(email: string): boolean {
console.warn(
'SÉCURITÉ: validateEmail() deprecated due to security vulnerabilities. ' +
'Use validateEmailV2() immediately. Support ends in v3.0.0'
);
return validateEmailV2(email).isValid;
}
/**
* Validation email sécurisée avec protection contre les vulnérabilités connues
*
* AMÉLIORATIONS SÉCURITÉ :
* - Protection injections regex (ReDoS)
* - Validation caractères spéciaux Unicode
* - Normalisation préventive (punycode, homoglyphes)
* - Détection patterns malveillants connus
*
* @param email - Adresse email à valider (any string input accepted)
* @returns Objet détaillé avec statut, erreurs et email normalisé
*
* @since 2.5.0
*/
export function validateEmailV2(email: string): EmailValidationResult {
// Implémentation sécurisée avec protection ReDoS...
}
Documentation d'API
Documentation des endpoints (si applicable)
/**
* Contrôleur pour la gestion des utilisateurs
*
* @swagger
* tags:
* - name: Users
* description: Opérations de gestion des utilisateurs
*/
export class UserController {
/**
* Récupère la liste des utilisateurs avec pagination
*
* @swagger
* /api/users:
* get:
* summary: Liste des utilisateurs
* tags: [Users]
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* minimum: 1
* default: 1
* description: Numéro de page
* - in: query
* name: limit
* schema:
* type: integer
* minimum: 1
* maximum: 100
* default: 20
* description: Nombre d'utilisateurs par page
* responses:
* 200:
* description: Liste des utilisateurs récupérée avec succès
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/PaginatedUserResponse'
* 400:
* description: Paramètres de requête invalides
* 401:
* description: Non authentifié
* 403:
* description: Permissions insuffisantes
*/
@Get('/users')
@Auth(['admin', 'moderator'])
async getUsers(
@Query() pagination: PaginationQuery
): Promise<PaginatedResponse<User>> {
return this.userService.getUsers(pagination);
}
}
Bonnes pratiques : Focus sur le "pourquoi"
1. Quand commenter et pourquoi
// ✅ Excellent : expliquer les contraintes et choix techniques
function retryWithBackoff<T>(
operation: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
// Backoff exponentiel pour préserver les services en difficulté
// Évite l'effet "thundering herd" lors des redémarrages
// Coefficient 2^n respecte les SLA partenaires (délai max acceptable)
const baseDelay = 1000;
// 3 tentatives max : équilibre entre resilience et temps d'attente UX
return this.executeWithBackoff(operation, maxRetries, baseDelay);
}
// ✅ Excellent : expliquer les contraintes métier et leur origine
function calculateShippingCost(weight: number, distance: number): number {
// Grille tarifaire basée contrat La Poste 2024 (référence COM-2024-001)
// Révision annuelle obligatoire selon clause 12.3
// Seuils métier validés par direction commerciale
// Seuil 500g : optimisation rentabilité/compétitivité analysée Q3 2023
const weightSurcharge = weight > 500 ? (weight - 500) * 0.005 : 0;
// Distance 500km : frontière économique transport express vs standard
const distanceSurcharge = distance > 500 ? baseCost * 0.20 : 0;
return baseCost + weightSurcharge + distanceSurcharge;
}
// ✅ Bon : contexte de sécurité critique
function hashPassword(password: string): Promise<string> {
// SÉCURITÉ : 12 rounds bcrypt = 250ms délai (recommandation OWASP 2024)
// Équilibre sécurité/UX validé par audit sécurité (SEC-2024-003)
const saltRounds = 12;
return bcrypt.hash(password, saltRounds);
}
// ❌ Mauvais : répéter ce que fait le code
function addTwoNumbers(a: number, b: number): number {
// Additionner a et b et retourner le résultat
return a + b;
}
// ❌ Mauvais : commentaire évident sans valeur
function getUserById(id: string): User {
// Récupérer l'utilisateur par son ID
return this.userRepository.findById(id);
}
2. Maintenir la cohérence entre code et commentaires
// ❌ Dangereux : commentaire obsolète induit en erreur
function processUser(user: User): void {
// OBSOLÈTE : Envoi email de bienvenue
// Note: Email désactivé temporairement
// LE CODE A CHANGÉ mais pas le commentaire !
// Plus d'envoi d'email → commentaire mensonger
logUserCreation(user);
updateUserStatistics();
sendSlackNotification(user); // Nouveau comportement non documenté
}
// ✅ Correct : documentation à jour avec le contexte
function processUser(user: User): void {
// Audit obligatoire : traçabilité création utilisateur (RGPD art.30)
logUserCreation(user);
// Métriques temps réel pour tableau de bord commercial
updateUserStatistics();
// Migration email → Slack : meilleure réactivité équipe (décision Q4 2024)
sendSlackNotification(user);
// TODO: Réintégrer email quand nouveau service SMTP opérationnel
// Migration prévue Q1 2025 (projet MAIL-2025-001)
// @see: ticket #1234
}
// ✅ Excellent : évolution documentée avec historique
function processUser(user: User): void {
// Traçabilité obligatoire (RGPD + audit interne)
logUserCreation(user);
updateUserStatistics();
// ÉVOLUTION NOTIFICATIONS 2024 :
// v1.0 : Email SMTP (problèmes de délivrabilité)
// v2.0 : Slack uniquement (réactivité équipe)
// v3.0 : Email + Slack prévus Q1 2025
await this.notificationService.notify(user, ['slack']);
}
3. Éviter la redondance avec TypeScript
TypeScript étant un langage fortement typé, concentrez-vous sur les informations non-évidentes :
// ❌ Redondant : TypeScript dit déjà tout cela
interface User {
id: string; // Identifiant de l'utilisateur
name: string; // Nom de l'utilisateur
email: string; // Adresse email de l'utilisateur
age: number; // Âge de l'utilisateur en années
}
// ✅ Valeur ajoutée : contraintes métier et contexte
interface User {
/** UUID v4 généré automatiquement à la création */
readonly id: string;
/** Nom complet affiché publiquement (prénom + nom famille) */
name: string;
/**
* Email unique dans le système - utilisé pour authentification
* Validation : RFC 5322 + blacklist domaines temporaires
*/
email: string;
/**
* Âge calculé à partir date naissance (mis à jour quotidiennement)
* Null si utilisateur refuse de fournir cette information (RGPD)
*/
age: number | null;
}
// ✅ Excellent : explique les choix de conception
interface CacheConfig {
/**
* TTL optimisé selon criticité des données :
* - Données statiques : 24h (référentiels)
* - Données utilisateur : 1h (profils, préférences)
* - Données temps réel : 5min (notifications, statuts)
*/
ttlSeconds: number;
/**
* Taille basée sur profiling production :
* 1000 entrées = ~50MB RAM sur données moyennes métier
* Éviction LRU empêche les fuites mémoire longues sessions
*/
maxEntries: number;
/**
* Préfixe pour éviter collisions entre environnements
* Format : {env}-{service}-{version} ex: "prod-user-v2"
*/
keyPrefix: string;
}
4. Commentaires de sécurité critiques
Les commentaires de sécurité doivent expliquer les choix et leurs justifications :
export class AuthService {
async hashPassword(password: string): Promise<string> {
// SÉCURITÉ : Paramétrage bcrypt selon standards industrie 2024
// 12 rounds = ~250ms sur serveur standard (benchmark interne)
// OWASP recommande 10-12, ANSSI préconise 12+ pour secteur bancaire
// Augmentation future : +1 round tous les 2 ans (roadmap sécurité)
const saltRounds = 12;
return bcrypt.hash(password, saltRounds);
}
validatePasswordStrength(password: string): PasswordValidation {
// SÉCURITÉ : Critères renforcés selon ANSSI/OWASP 2024
// Contexte : Application manipulant données financières
// Base réglementaire : ISO 27001 + PCI DSS niveau 1
const criteria = {
// 12 caractères min : protection contre attaques par dictionnaire
minLength: 12,
// 3+ types caractères : résistance attaques statistiques
requiredCharTypes: 3, // [a-z], [A-Z], [0-9], [spéciaux]
// Blacklist commune : évite mots de passe prévisibles
bannedPatterns: ['123456', 'password', 'qwerty', 'azerty'],
// Anti-pattern : évite répétitions (aaa..., 111...)
maxRepeatingChars: 2
};
return {
isValid: this.checkComplexity(password, criteria),
errors: this.getPasswordErrors(password, criteria),
// Score numérique pour indicateur UX progressif
strengthScore: this.calculatePasswordScore(password)
};
}
async validateToken(token: string): Promise<TokenValidation> {
// SÉCURITÉ : Validation multi-niveaux contre attaques courantes
try {
// Étape 1 : Validation signature (protection falsification)
const decoded = jwt.verify(token, this.secretKey);
// Étape 2 : Vérification blacklist (tokens compromis/révoqués)
if (await this.tokenBlacklist.has(decoded.jti)) {
throw new SecurityError('Token révoqué pour raisons de sécurité');
}
// Étape 3 : Contrôle usage anormal (brute force/bot)
await this.rateLimiter.checkTokenUsage(decoded.sub);
return { valid: true, payload: decoded };
} catch (error) {
// Audit sécurité : log tentatives d'accès malveillantes
this.securityLogger.logSuspiciousActivity({
event: 'invalid_token_attempt',
token: this.hashToken(token), // Hash pour RGPD
timestamp: new Date(),
ipAddress: this.getClientIP()
});
throw error;
}
}
}
### 5. Utilisation des directives TypeScript spécialisées
TypeScript fournit des directives spécialisées pour des cas particuliers :
```typescript
// Suppression d'erreurs justifiée et documentée
function legacyApiCall(data: any): void {
// @ts-ignore - Bypass temporaire type checking
// POURQUOI : Legacy API ne fournit pas types corrects
// RISQUE ACCEPTÉ : Validation manuelle côté appelant
// PLAN : Migration vers nouvelle API typée prévue Q2 2025
// @remove-after: nouvelle API déployée
legacyService.process(data.unknownProperty);
}
// Validation d'erreurs attendues en mode test
function testErrorHandling(): void {
// @ts-expect-error - Erreur volontaire pour test
// Test robustesse : vérifier gestion paramètre invalide
processPayment('invalid-amount-type');
expect(lastError).toContain('Amount must be numeric');
}
// Désactivation contrôle fichier entier (migration)
// @ts-nocheck
// MIGRATION EN COURS : Ancien fichier JS en cours de conversion TS
// Phase 1 : Import sans erreur (actuel)
// Phase 2 : Typing progressif (Q1 2025)
// Phase 3 : Validation complète (Q2 2025)
// @see: ticket MIGRATION-2024-015
// Activation contrôle fichier JS spécifique
// @ts-check
// Validation TypeScript sur ce fichier JS legacy
// CONTEXTE : Code critique nécessitant vérifications avant migration TS
6. Commentaires et performance
Documenter les choix d'optimisation et leurs trade-offs :
class DataProcessor {
// Cache en mémoire : trade-off mémoire/latence réseau
private cache = new Map<string, ProcessedData>();
async processLargeDataset(dataset: RawData[]): Promise<ProcessedData[]> {
// Traitement par chunks : évite épuisement mémoire sur gros volumes
// BENCHMARKS (environnement prod) :
// - 1000 items/chunk : équilibre optimal mémoire/performance
// - Chunks plus petits : overhead traitement (+40% temps)
// - Chunks plus gros : risque OOM sur datasets >100k items
const chunkSize = 1000;
// Web Workers pour parallélisation : exploit multi-core serveur
// POURQUOI : CPU-bound operations bénéficient du parallélisme
// LIMITE : 4 workers max (observé 8 cores = diminishing returns au-delà)
const workerPool = new WorkerPool(Math.min(4, os.cpus().length));
const results: ProcessedData[] = [];
for (let i = 0; i < dataset.length; i += chunkSize) {
const chunk = dataset.slice(i, i + chunkSize);
// Promise.all : parallélisation inter-chunks
// Risque assumé : pic temporaire utilisation CPU
const chunkResults = await Promise.all(
chunk.map(item => workerPool.process(item))
);
results.push(...chunkResults);
// Yield periodique : libère event loop pour autres tâches
// Fréquence optimisée : équilibre responsivité/performance
if (i % (chunkSize * 5) === 0) {
await new Promise(resolve => setImmediate(resolve));
}
}
return results;
}
}