Skip to main content

1. Le Nommage : Soyez Expressifs

Le nom d’une variable ou d’une fonction doit dire pourquoi elle existe, ce qu’elle fait et comment on l’utilise.

❌ Mauvais (Peu clair)

// On ne sait pas ce que sont 'p', 'l' ou 'v'
const p = 150; 
const l = items.map(i => i.price).reduce((a, b) => a + b, 0);

if (l > p) {
  const v = l * 0.1;
  const t = l - v;
  console.log(t);
}

✅ Bon (Explicite)

const MINIMUM_ORDER_FOR_DISCOUNT = 150;
const DISCOUNT_RATE_PERCENTAGE = 0.1; // 10%

const orderTotal = cartItems.reduce((total, item) => total + item.price, 0);

if (orderTotal > MINIMUM_ORDER_FOR_DISCOUNT) {
  const discountAmount = orderTotal * DISCOUNT_RATE_PERCENTAGE;
  const finalPriceWithDiscount = orderTotal - discountAmount;
  
  console.log(finalPriceWithDiscount);
}

2. Les Fonctions : Faites une seule chose (SRP)

Une fonction (ou un composant) doit avoir une seule responsabilité. Si tu as besoin du mot “et” pour décrire ce que fait ta fonction, c’est qu’elle doit être découpée.

❌ Mauvais (NestJS - Service “fourre-tout”)

// Un service qui fait trop de choses
async handleUserSignup(data: any) {
  // 1. Logique de validation métier
  if (data.password.length < 8) throw new Error("Trop court");

  // 2. Persistance (Base de données)
  const user = await this.userRepository.save(data);

  // 3. Logique de communication (Email)
  const message = `Bienvenue ${user.firstName}`;
  await this.emailService.send(user.email, "Bienvenue !", message);

  // 4. Logique de marketing (Tracker)
  await this.analytics.track('user_created', user.id);
}

✅ Bon (Responsabilités séparées)

Le Service Principal (Responsabilité : Créer) Il ne sait pas qu’un mail doit être envoyé, il dit juste : “J’ai fini mon travail”.
async createUser(data: CreateUserDto) {
  const user = await this.repo.save(data);
  
  // On émet un événement. C'est tout.
  this.eventEmitter.emit('user.created', user); 
  
  return user;
}
Le Listener d’Email (Responsabilité : Communiquer) Ce code vit dans un autre fichier. Il ne s’occupe que de la mise en forme et de l’envoi.
@OnEvent('user.created')
handleUserCreatedEvent(user: User) {
  const welcomeMessage = this.templateService.buildWelcome(user.name);
  this.emailService.send(user.email, "Bienvenue", welcomeMessage);
}
Le Listener Analytics (Responsabilité : Analyser) Si demain on veut arrêter de tracker les utilisateurs, on supprime juste ce bloc, sans toucher au code de création d’utilisateur.
@OnEvent('user.created')
logUserAnalytics(user: User) {
  this.analytics.track('new_registration', { userId: user.id });
}

3. Le Principe de “Don’t Repeat Yourself” (DRY)

Évite de copier-coller. Si tu vois la même logique deux fois, centralise-la.

❌ Mauvais (React - Duplication)

const UserProfile = () => (
  <div style={{ padding: '10px', border: '1px solid black' }}>Profil Utilisateur</div>
);
const AdminProfile = () => (
  <div style={{ padding: '10px', border: '1px solid black' }}>Profil Admin</div>
);

✅ Bon (Composant réutilisable)

const Card = ({ children }) => (
  <div className="card-styles">{children}</div>
);

const UserProfile = () => <Card>Profil Utilisateur</Card>;

4. Les Commentaires : Le code doit s’expliquer seul

N’utilise les commentaires que pour expliquer le “Pourquoi” (décisions complexes), jamais le “Quoi”. Si le code est complexe, renomme tes variables.

❌ Mauvais

// Vérifie si l'utilisateur est actif et a plus de 18 ans
if (u.status === 'active' && u.age > 18) { ... }

✅ Bon

const isEligibleAdult = user.isActive && user.isAdult;
if (isEligibleAdult) { ... }

5. Les Principes SOLID (Le “S” et le “O”)

Pour les juniors, focus sur ces deux-là en priorité :
PrincipeDéfinition simple
Single ResponsibilityUn fichier = Une mission.
Open/ClosedTon code doit être ouvert à l’extension mais fermé à la modification.

S : Single Responsibility Principle (SRP)

“Une classe ou une fonction ne devrait avoir qu’une seule raison de changer.” Si ton fichier s’occupe à la fois de calculer un prix, de formater une date et d’envoyer une notification, il a trop de raisons de changer. Si le format de la date change, tu risques de casser le calcul du prix par erreur. ❌ Le mauvais élève (La classe “Dieu”) Ici, la classe Invoice (Facture) fait tout.
class Invoice {
  calculateTotal(items) { /* ... */ }
  generateHTML() { /* ... */ } // ❌ Trop de responsabilités : l'affichage
  saveToDatabase() { /* ... */ } // ❌ Trop de responsabilités : le stockage
}
✅ Le bon élève (Découpage) On sépare la logique métier, la présentation et la persistance.
  • InvoiceCalculator : Ne connaît que les chiffres.
  • InvoicePrinter : Ne connaît que le HTML/PDF.
  • InvoiceRepository : Ne connaît que la base de données.

O : Open/Closed Principle (OCP)

“Ouvert à l’extension, fermé à la modification.” C’est le principe le plus difficile à comprendre au début. Cela signifie que tu devrais pouvoir ajouter une nouvelle fonctionnalité à ton application sans modifier le code existant. On ajoute du nouveau code au lieu de changer l’ancien. ❌ Mauvais : Le switch infini Imaginons un système qui calcule les frais de port selon le pays. Chaque fois qu’on ajoute un pays, on doit modifier la fonction existante.
// Si on veut ajouter "Espagne", on DOIT modifier cette fonction
function getShippingCost(country: string) {
  if (country === 'France') return 5;
  if (country === 'Belgium') return 7;
  if (country === 'USA') return 15;
  // ... C'est le début d'un enfer de maintenance
}
✅ Bon : L’approche par polymorphisme On crée une structure (Interface) que l’on peut étendre. On ne touche plus au moteur de calcul, on lui donne juste de nouvelles règles.
// 1. On définit une règle générale
interface ShippingStrategy {
  calculate(): number;
}

// 2. On crée des extensions pour chaque cas
class FranceShipping implements ShippingStrategy {
  calculate() { return 5; }
}

class USAShipping implements ShippingStrategy {
  calculate() { return 15; }
}

// 3. Le code principal est "fermé" : il ne change jamais, 
// peu importe le nombre de pays ajoutés.
function displayCost(strategy: ShippingStrategy) {
  console.log(strategy.calculate());
}

Pourquoi est-ce vital pour un Junior ?

  1. S (Single Responsibility) : Rend ton code testable. Il est facile de tester une petite fonction qui fait une seule chose. C’est impossible si elle fait 200 lignes.
  2. O (Open/Closed) : Rend ton code stable. Si tu n’as pas besoin de modifier une fonction qui marche déjà pour ajouter une option, tu ne risques pas de créer de nouveaux bugs (régressions) dans l’existant.
En résumé : Le S nous aide à savoir mettre le code, le O nous aide à savoir comment l’organiser pour qu’il puisse grandir sans casser.

6. Structure et Clean Architecture (NestJS)

En NestJS, respectez la hiérarchie pour ne pas transformer les contrôleurs en usines à gaz.
Règle d’or :
  • Controller : Reçoit la requête, valide les données (DTO), renvoie la réponse. Pas de logique métier.
  • Service : Contient toute la logique métier.
  • Repository/Entity : Gère l’accès aux données.

Exemple de structure propre :

src/
 └── users/
     ├── dto/              # Validation des entrées
     ├── entities/         # Schéma de base de données
     ├── users.controller.ts
     ├── users.service.ts
     └── users.module.ts

Résumé pour l’équipe

  1. Lisibilité > Brièveté : On s’en fiche que le code soit court, on veut qu’il soit clair.
  2. Petit, c’est mieux : Des fonctions de moins de 20 lignes, des composants de moins de 100 lignes.
  3. Supprime le code mort : Si une fonction n’est plus utilisée, on l’efface (Git est là pour s’en souvenir).