Polar Code 🎭

Command Palette

Search for a command to run...

02
Pièce N°02

CHAPITRE 1 : PROBLÈMES RÉCURRENTS EN CONCEPTION LOGICIELLE

La pluie frappait la vitre de mon bureau comme des doigts impatients. Sur l'écran, le code étalait ses entrailles. Un système de paiement qui devait être simple. Maintenant, ça ressemblait à un cadavre disséqué. Chaque classe tenait l'autre par la gorge. C'était ça, le premier problème. Le vrai tueur silencieux.

Couplage fort

J'avais commencé propre. Une classe Paiement. Une classe EmailService. Sauf qu'au fil des "petites améliorations", elles s'étaient mariées. Pour le pire.

class Paiement {
    private emailService: EmailService;

    constructor() {
        this.emailService = new EmailService(); // Le piège se referme
    }

    traiter(montant: number) {
        // ... logique métier ...
        this.emailService.envoyerConfirmation('client@mail.com');
    }
}

Paiement connaissait EmailService intimement. Comme un indic qui ne pourrait plus respirer sans son flic attitré. Changiez l'un, l'autre saignait. Vouliez tester Paiement seul ? Impossible. Il arrivait avec tout son attirail. Un paquetage empoisonné.

Manque d'extensibilité

Le client est revenu. "On voudrait des notifications SMS aussi". Le code m'a regardé avec un rictus.

class Paiement {
    private emailService: EmailService;
    private smsService: SmsService; // La tumeur grossit

    constructor() {
        this.emailService = new EmailService();
        this.smsService = new SmsService();
    }

    traiter(montant: number) {
        // ...
        this.emailService.envoyerConfirmation('client@mail.com');
        this.smsService.envoyerSMS('+33123456789');
    }
}

Chaque nouvelle demande signait un nouveau couplage. Le code se calcifiait. L'extensibilité ? Un rêve lointain, noyé dans le café froid et les dépendances concrètes.

Multiplication des responsabilités

Paiement ne faisait plus juste des paiements. C'était devenu un gourou multitâche.

class Paiement {
    // ... propriétés existantes ...

    traiter(montant: number) {
        this.validerMontant(montant);          // 1. Validation
        this.debiterCompte(montant);           // 2. Logique métier
        this.sauvegarderTransaction(montant);  // 3. Persistance
        this.envoyerNotifications();           // 4. Communication
        this.genererRapport();                 // 5. Reporting - Bordel, ça suffit !
    }

    // ... 15 méthodes privées qui font tout et rien ...
}

La classe suait l'effort. 500 lignes. Responsable de la validation, du métier, de la persistance, des notifications. Comme un caïd qui voudrait tout contrôler : le jeu, la prostitution, le blanchiment. Un jour, l'empire s'effondre. Toujours.

Code fragile face au changement

Puis vint la demande. "On change de fournisseur SMS". Une simple modification, pensais-je.

Je changeai SmsService. Trois tests ont échoué. Paiement avait des attentes très spécifiques sur la forme des méthodes. Je corrigeai. Une autre partie du système, RapportManager, utilisait aussi SmsService différemment. Elle a lâché à son tour.

Chaque modification devenait une partie de domino. Touchiez une brique, tout le mur tremblait. La fragilité n'était pas un bug, c'était une fonctionnalité. Celle du cauchemar.

Héritage abusif

Dans un coin du système, une autre horreur mijotait. L'héritage, utilisé comme un couteau suisse mal aiguisé.

class Utilisateur {
    nom: string;
    email: string;
    
    seConnecter() { /* ... */ }
    seDeconnecter() { /* ... */ }
}

class UtilisateurPremium extends Utilisateur {
    niveauAbonnement: string;
    
    // Ça commence propre...
}

class UtilisateurPremiumAvecParrainage extends UtilisateurPremium {
    parrains: Utilisateur[];
    
    // ...et ça dérape.
}

class UtilisateurPremiumAvecParrainageEtHistorique extends UtilisateurPremiumAvecParrainage {
    historiqueAchats: Achat[];
    
    // La hiérarchie devient un monstre.
    // Et si on veut un utilisateur normal avec historique ?
    // On refait tout.
}

L'héritage, c'était la solution facile. "Un UtilisateurPremium EST un Utilisateur". Sauf que la vie logicielle est plus torse. Les variations ne sont pas toujours hiérarchiques. Parfois, elles sont transversales, multiples, contradictoires.

Cette hiérarchie rigide craquait sous le poids des "et" : "avec parrainage ET historique". Chaque nouvelle exigence la faisait ployer un peu plus. Jusqu'à ce qu'un jour, il faille un UtilisateurNormalAvecHistoriqueSansParrainage. L'édifice s'effondrerait.

Le constat

Je me suis reculé de l'écran. La pluie avait cessé. Le silence était pire.

Ce n'étaient pas des bugs. C'étaient des pathologies. Des schémas récurrents de pourriture logicielle. Le couplage qui étrangle. Les responsabilités qui s'empilent comme des cadavres dans un placard. L'héritage qui promet l'ordre et livre le chaos.

Je rallumai une cigarette (virtuelle, le bâtiment était non-fumeur). Les Design Patterns, ceux dont le Gang chuchotait, ils ne venaient pas résoudre des problèmes uniques. Ils venaient avec des réponses à ces pathologies-là. Des réponses qui sentaient le vécu, le sang et le bit.

Le vrai problème n'était pas dans le code. Il était dans la façon de penser. Et changer de façon de penser, ça commence par reconnaître le crime. Par voir les patterns de l'échec avant d'appliquer ceux de la solution.

La nuit était tombée. Sur l'écran, le code patientait, plein de mauvaises habitudes et de couplages fatals. Demain, on commencerait le nettoyage. Mais ce soir, on regardait le problème en face. Dans les yeux.