Découvrir le Clean Code en Software Craftsmanship, Les Bonnes Pratiques en Java

Par KamangaJul 17, 202414 mins de lecture

Découvrir le Clean Code en Software Craftsmanship : Les Bonnes Pratiques en Java

Le coût caché d'un code mal écrit

Mission chez BNP Paribas CIB, fin 2018. Un module de pricing critique, écrit dans l'urgence trois ans plus tôt par une équipe depuis dispersée. Le code fonctionnait, c'était même un de ses problèmes : personne n'osait y toucher. La moindre évolution réglementaire prenait six semaines de chiffrage et deux mois d'exécution, l'essentiel passé à reverse-engineerer des méthodes de 400 lignes aux variables tmp, data2, result_final.

Même schéma chez Agirc-Arrco quelques mois plus tard. Le code livré dans la précipitation lors d'un sprint serré devient le code que personne ne veut maintenir l'année suivante. C'est précisément le problème que résout le Clean Code : produire du code qui reste modifiable six mois ou trois ans après, par soi-même comme par un autre développeur.

Cet article rassemble les principes formalisés par Robert C. Martin dans son ouvrage de référence, avec des exemples Java applicables au quotidien, même quand la pression de livraison est forte.


Les principes fondamentaux du Clean Code

Trois principes structurent la lecture d'un code propre. Ils ne sont pas magiques : appliqués isolément, leur effet est limité. Combinés et répétés sur la durée, ils transforment radicalement la maintenabilité d'une base.

1. Des noms de variables et de fonctions explicites

L'un des premiers pas vers un code propre est de choisir des noms clairs et significatifs pour vos variables, vos fonctions et vos classes. Si quelqu'un peut comprendre ce qu'une variable fait rien qu'en lisant son nom, alors vous êtes sur la bonne voie.

Exemple en Java :

// Mauvais exemple
int a = 5;

// Meilleur exemple
int nombreDeJours = 5;

En nommant la variable nombreDeJours, on rend immédiatement évident ce que représente cette donnée, sans avoir besoin de commentaires ou de devinettes. De plus, évitez les abréviations trop courtes ou ambiguës comme nbr, qui peuvent prêter à confusion.

Question simple à se poser : ce nom passerait-il dans une explication orale à un collègue ? Si la réponse est non, c'est qu'il est à reprendre.

2. La règle des fonctions courtes

Une bonne fonction ne doit faire qu'une seule chose et bien la faire. Si une fonction devient trop longue, il y a de fortes chances qu'elle fasse trop de choses à la fois. Un bon indicateur : si vous pouvez résumer ce que fait votre fonction en une seule phrase claire, elle est probablement assez concise.

Exemple en Java :

// Mauvais exemple - Trop de responsabilités dans une seule fonction
public void processOrder(Order order) {
    // Valider la commande
    if (order.isValid()) {
        // Calculer le total
        double total = order.getAmount() + calculateTax(order);

        // Traiter le paiement
        processPayment(order, total);

        // Envoyer une confirmation
        sendConfirmationEmail(order);
    } else {
        System.out.println("Commande invalide");
    }
}

// Meilleur exemple - Chaque fonction a une responsabilité unique
public void processOrder(Order order) {
    if (order.isValid()) {
        double total = calculateTotal(order);
        processPayment(order, total);
        sendConfirmation(order);
    } else {
        handleInvalidOrder();
    }
}

private double calculateTotal(Order order) {
    return order.getAmount() + calculateTax(order);
}

private void processPayment(Order order, double total) {
    // Code pour traiter le paiement
}

private void sendConfirmation(Order order) {
    // Code pour envoyer l'email de confirmation
}

private void handleInvalidOrder() {
    System.out.println("Commande invalide");
}

Dans le second exemple, chaque étape du processus est divisée en fonctions plus petites et spécifiques. La fonction processOrder ne fait plus qu'orchestrer les étapes, tandis que les détails comme le calcul du total ou l'envoi de l'email de confirmation sont délégués à des fonctions plus courtes et plus ciblées.

Un commentaire qui décrit ce que fait une fonction est presque toujours le signe que la fonction fait trop de choses. Le commentaire devient le nom d'une nouvelle méthode extraite.


Vous voulez écrire du code que vous comprendrez encore dans six mois ?

Nommer juste, garder une fonction courte, écarter les responsabilités qui s'accumulent : ces réflexes ne s'acquièrent pas en lisant un article, ils se travaillent sur votre vrai code. En mentoring 1:1, je relis vos fonctions et vos classes avec vous, je vous montre où ça dérape et comment reprendre la main. Vous repartez avec des gestes que vous appliquez dès la PR suivante.

3. Comment structurer le code pour le rendre lisible

La lisibilité du code est essentielle pour un Clean Code. Un code bien structuré est celui qui peut être compris facilement par un autre développeur, même sans explication supplémentaire. Pour cela, je vous recommande de suivre une hiérarchie logique et d'éviter les longs blocs de code imbriqués.

Exemple en Java :

// Mauvais exemple
if (isValidUser(user)) {
    if (hasSufficientBalance(user)) {
        processPayment();
    } else {
        // ...
    }
} else {
    // ...
}

// Meilleur exemple
if (!isValidUser(user)) {
    return;
}

if (!hasSufficientBalance(user)) {
    return;
}

processPayment();

Dans le second exemple, la structure est plus claire : chaque condition est traitée séparément, les cas d'erreur sont écartés au début. Le lecteur suit le flux nominal sans avoir à dépiler trois niveaux d'imbrication.


Exemples de Clean Code en Java

Rien ne vaut des exemples concrets pour mieux comprendre comment appliquer les principes du Clean Code. Voici quelques exemples pratiques en Java, basés sur les principes vus précédemment. Ces exemples vous montreront comment améliorer la clarté, la lisibilité et la maintenabilité de votre code.

1. Variables bien nommées

Donner des noms explicites aux variables reste l'une des bases du Clean Code : un code bien nommé se passe de commentaires.

Exemple en Java :

// Mauvais exemple
double v = getVolume();

// Meilleur exemple
double volumeDeLaBoite = getVolume();

Dans le second exemple, le nom volumeDeLaBoite explique clairement ce que représente cette donnée, tandis que v est trop vague. On facilite ainsi la compréhension du code sans avoir besoin de lire toute la logique.

2. Fonctions courtes et focalisées

Une bonne fonction doit être courte et se concentrer sur une seule responsabilité. Cela rend votre code plus lisible et plus facile à tester.

Exemple en Java :

// Mauvais exemple
public void calculerEtEnvoyerFacture(Client client) {
    // Calcul des montants
    double montantHT = client.getPrixHT();
    double taxe = montantHT * 0.2;
    double montantTotal = montantHT + taxe;
    
    // Envoi du mail
    String message = "Facture envoyée";
    envoyerMail(client.getEmail(), message);
}

// Meilleur exemple
public double calculerMontantTotal(Client client) {
    double montantHT = client.getPrixHT();
    double taxe = montantHT * 0.2;
    return montantHT + taxe;
}

public void envoyerFacture(Client client) {
    envoyerMail(client.getEmail(), "Facture envoyée");
}

Dans le second exemple, calcul et envoi d'email sont séparés : chaque fonction se teste indépendamment, et l'intention de chaque bloc devient explicite.

Règle empirique utile : au-delà de vingt lignes, une fonction est presque toujours candidate à être découpée.


3. Bonne gestion des exceptions

La gestion des erreurs et des exceptions est souvent négligée, mais elle joue un rôle clé dans un code propre. Un bon code doit être capable de traiter les erreurs de manière claire et concise, sans masquer les problèmes.

Exemple en Java :

// Mauvais exemple
try {
    processOrder(order);
} catch (Exception e) {
    System.out.println("Erreur lors du traitement de la commande");
}

// Meilleur exemple
try {
    processOrder(order);
} catch (InvalidOrderException e) {
    System.out.println("Commande invalide : " + e.getMessage());
} catch (PaymentFailedException e) {
    System.out.println("Échec du paiement : " + e.getMessage());
}

Dans le second exemple, chaque type d'erreur reçoit son propre traitement : on sait précisément ce qui a échoué et la réaction peut être adaptée au cas.

Le catch (Exception e) générique est un anti-pattern classique : il masque les vraies causes et complique le débogage en production.

Les bonnes pratiques pour écrire du code maintenable

Le Clean Code ne se limite pas à écrire du code lisible ; il doit aussi être facilement maintenable et évolutif. Voici quelques bonnes pratiques pour s'assurer que votre code reste propre même après plusieurs itérations.

1. Le principe de responsabilité unique (Single Responsibility Principle - SRP)

Le principe de responsabilité unique, formalisé par Robert C. Martin dans Clean Code, énonce qu'une classe ou une fonction ne doit avoir qu'une seule raison de changer. Le bénéfice est direct : moins de dépendances, des tests plus simples, une maintenance qui n'engendre pas d'effets de bord.

Exemple en Java :

// Mauvais exemple - Une classe gère trop de responsabilités
public class GestionnaireDeCommande {
    public void creerCommande() {
        // Crée une commande
    }
    
    public void calculerRemises() {
        // Calcule les remises
    }

    public void envoyerEmailConfirmation() {
        // Envoie un email de confirmation
    }
}

// Meilleur exemple - Responsabilités divisées en plusieurs classes
public class CommandeService {
    public void creerCommande() {
        // Crée une commande
    }
}

public class RemiseService {
    public void calculerRemises() {
        // Calcule les remises
    }
}

public class NotificationService {
    public void envoyerEmailConfirmation() {
        // Envoie un email de confirmation
    }
}

Chaque classe a maintenant une responsabilité unique : les classes restent légères, les modifications restent localisées, et l'impact d'un changement ne se propage pas en cascade.

Test pratique : si l'explication de ce que fait une classe nécessite un "et" ("elle fait X et Y"), il y a une responsabilité de trop.

2. Éviter les duplications de code

Le code dupliqué est un piège fréquent qui complique la maintenance. En éliminant les duplications, vous vous assurez qu'un changement à un endroit du code n'entraîne pas une cascade de modifications à différents endroits.

Exemple en Java :

// Mauvais exemple - Duplication de logique
double calculerRemiseClient(double montant) {
    double remise = 0;
    if (montant > 1000) {
        remise = montant * 0.1;
    }
    return remise;
}

double calculerRemisePartenaire(double montant) {
    double remise = 0;
    if (montant > 1000) {
        remise = montant * 0.1;
    }
    return remise;
}

// Meilleur exemple - Refactorisation pour éviter la duplication
double calculerRemise(double montant) {
    double remise = 0;
    if (montant > 1000) {
        remise = montant * 0.1;
    }
    return remise;
}

En unifiant la logique de calcul de la remise dans une seule fonction, vous éliminez la duplication et simplifiez la maintenance. Si la logique doit changer (par exemple, le seuil de 1000), vous n'aurez à modifier qu'une seule fonction.

Deux occurrences de la même logique = à surveiller. Trois occurrences = refactoring obligatoire : la troisième est presque toujours celle qu'on oublie de mettre à jour.

3. Faciliter les tests unitaires

Un code propre est un code testable. Concrètement : des fonctions courtes, des classes cohérentes, aucune dépendance cachée. Les tests unitaires restent alors simples et rapides à écrire.

Exemple en Java :

// Mauvais exemple - Difficulté à tester en raison des dépendances internes
public class CommandeService {
    public void creerCommande() {
        Database db = new Database();
        db.save();
    }
}

// Meilleur exemple - Injection de dépendance pour faciliter les tests
public class CommandeService {
    private Database database;

    public CommandeService(Database database) {
        this.database = database;
    }

    public void creerCommande() {
        database.save();
    }
}

Dans le second exemple, l'injection de dépendance rend la classe plus testable. Au lieu de créer une instance de Database en interne, vous pouvez passer une base de données fictive (mock) lors de vos tests unitaires.

L'injection de dépendances n'est pas qu'un pattern de framework : c'est ce qui rend une classe testable en isolation, sans avoir à monter une base ou un service externe pour la moindre vérification.

Conseils et astuces pour appliquer le Clean Code au quotidien

Maintenant que vous connaissez les bases du Clean Code, voyons comment intégrer ces bonnes pratiques dans votre travail quotidien, même lorsque vous êtes sous pression ou que vous devez travailler sur du code existant. Voici quelques astuces simples mais efficaces pour garder votre code propre, maintenable et lisible à long terme.

1. Réécrire du code existant : le refactoring progressif

Quand vous héritez d'un code legacy mal structuré ou difficile à comprendre, il peut être tentant de tout réécrire. Cependant, ce n'est pas toujours réaliste ou nécessaire. La meilleure approche est souvent le refactoring progressif : au lieu de tout changer d'un coup, vous améliorez petit à petit le code à chaque modification ou ajout de fonctionnalité.

Exemple en Java :

// Code hérité difficile à comprendre
public void traitementCommande(Commande commande) {
    if (commande.isUrgent() && !commande.isValide()) {
        System.out.println("Erreur");
    }
    // Autres opérations
}

// Refactorisation progressive
public void verifierCommandeValide(Commande commande) {
    if (!commande.isValide()) {
        throw new IllegalArgumentException("Commande invalide");
    }
}

Dans cet exemple, au lieu de réécrire toute la méthode traitementCommande, j’ai simplement extrait une partie du code dans une fonction dédiée, ce qui clarifie une portion du processus sans tout chambouler. Ce type de petit refactoring est une manière efficace de rendre le code plus propre à mesure que vous y travaillez.

Pas de "grand refactoring" planifié sur trois sprints : c'est le scénario qui échoue le plus souvent. Le scout rule (laisser le code un peu plus propre qu'on l'a trouvé) marche, lui, sur la durée.

2. Prioriser le Clean Code sous pression

La tentation est forte de sacrifier la qualité quand les délais se resserrent. C'est un mauvais calcul : un code lisible reste plus rapide à modifier, à déboguer et à étendre, y compris dans le sprint en cours, dès qu'il faut revenir sur une fonctionnalité écrite la veille.

Voici quelques astuces pour garder votre code propre même sous pression :

  • Utilisez des fonctions courtes et bien nommées. Même en mode rush, séparer les responsabilités limite les erreurs et accélère le débogage.
  • Nommez les variables clairement. Prendre quelques secondes pour choisir un nom explicite peut vous faire gagner des heures plus tard en maintenance.
  • Évitez les solutions "hacky". Même si une solution rapide semble tentante, elle peut introduire des bugs difficiles à repérer plus tard.

Test du "vous dans trois mois" : si la solution hacky ne sera plus comprise par vous-même dans douze semaines, c'est qu'elle ne mérite pas d'être commitée.

3. Collaborer avec d’autres développeurs pour améliorer le code

Le Clean Code n’est pas seulement une affaire personnelle. Il est aussi essentiel d’inciter vos collègues à adopter ces bonnes pratiques. Une revue de code régulière est un excellent moyen d’encourager une culture du Clean Code au sein de votre équipe. Chacun peut ainsi apprendre des autres et partager des conseils sur la façon d’améliorer la qualité du code.

Quelques conseils pour une bonne collaboration :

  • Soyez constructif lors des revues de code. Au lieu de pointer du doigt les erreurs, proposez des solutions et expliquez pourquoi une modification rendrait le code plus clair.
  • Favorisez la documentation du code. Encouragez vos collègues à écrire des noms explicites, à bien structurer les fonctions, et à éviter les duplications.
  • Partagez les succès. Quand un bon refactoring ou une amélioration de la lisibilité est fait, mettez-le en avant lors de vos réunions d’équipe.

Un guide interne avec des exemples concrets tirés de votre propre code vaut mille fois mieux qu'une référence générique copiée d'un blog : il parle le langage du domaine et anticipe les pièges réels du projet.

Le Clean Code, c'est une pratique parmi cent

Cet article détaille une poignée de réflexes : nommer juste, des fonctions courtes, des responsabilités séparées. Ce ne sont que quelques pièces d'un ensemble bien plus large. Le Craft Bundle réunit les 100 pratiques que j'applique pour produire du code propre et maintenable, celles que l'IA ne vous apprendra jamais parce qu'elle ne les a jamais vues tenir trois ans en production.


Questions fréquentes

Clean Code = juste du code joliment formaté ?

Non. Le formatage en fait partie, mais c'est le degré le moins important. Le cœur du Clean Code, c'est la lisibilité structurelle : nommage, taille des fonctions, séparation des responsabilités, gestion claire des erreurs. Une base parfaitement formatée mais aux méthodes de 300 lignes reste un code sale.

Comment appliquer sur du legacy déjà chargé ?

Pas de big bang. À chaque passage sur une zone (bug fix, ajout de feature, exploration), laisser le code un peu plus propre qu'on l'a trouvé. Un nom de variable, une méthode extraite, un test ajouté. Sur six mois, l'effet cumulé est considérable. Sur trois ans, la base est transformée.

Clean Code et délais serrés sont-ils incompatibles ?

Non, c'est même l'inverse au-delà de quelques jours. Le code sale livré sous pression devient le code qui ralentit le sprint suivant. Trois pratiques minimales restent toujours rentables, même en mode rush : nommer correctement, garder les méthodes courtes, refuser les "hacks" qu'on aura honte de relire.

Comment embarquer une équipe résistante ?

Les arguments théoriques ne marchent pas. Ce qui marche : prendre un module particulièrement douloureux, le nettoyer avec un binôme volontaire, mesurer le temps de relecture et le nombre de bugs sur six semaines. Ensuite, introduire les revues de code et le scout rule. La culture s'installe par l'exemple, jamais par décret.


Conclusion

Le Clean Code n'est pas une posture esthétique : c'est ce qui rend une base maintenable à dix-huit mois plutôt qu'illisible à six. La discipline tient en quelques gestes répétés : nommer pour révéler l'intention, garder les méthodes courtes, séparer les responsabilités, écrire du code qu'on relira sans grimacer. Ces gestes paraissent insignifiants à l'unité ; cumulés sur la durée d'un projet, ils font la différence entre une équipe qui livre et une équipe qui s'enlise.

Ressource gratuite : 10 signaux que votre équipe tech est en danger

10 signaux d'alarme pour identifier les problèmes systémiques cachés dans votre équipe avant qu'ils deviennent critiques. Auto-diagnostic inclus : 5 minutes pour savoir où vous en êtes.


Ecris par Kamanga

Expert IT avec 25 ans d'expérience en développement logiciel, diplômé EPITECH et MBA. Spécialisé en software craftsmanship, gestion du changement, stratégie, direction des systèmes d'information, coaching et certifié en agilité.