DRY, KISS, YAGNI : Les principes fondamentaux du code propre

Dans le monde du développement logiciel, on parle souvent de Clean Code comme d’un idéal : un code clair, maintenable, évolutif et facile à comprendre. Parmi les principes qui guident cette recherche, trois sont particulièrement puissants et universels : DRY, KISS et YAGNI.

J’ai pu constater leur importance de manière très concrète en travaillant sur plusieurs produits aux tailles et aux qualités très différentes, allant d’applications modestes à des systèmes d’envergure avec une base de code massive et des milliers de fonctionnalités. En tant que développeur puis architecte sur ces projets, j’ai pu comparer les environnements où ces principes étaient rigoureusement appliqués et ceux où ils étaient négligés.

Dans les premiers cas, le code était plus clair, plus cohérent et plus simple à maintenir, ce qui permettait aux équipes de livrer rapidement et sereinement de nouvelles fonctionnalités.

Dans les seconds, le non-respect de ces règles engendrait une dette technique considérable, ralentissait les développements, compliquait la maintenance et provoquait une frustration palpable chez les équipes, qui perdaient un temps précieux à déchiffrer et corriger du code mal conçu. Pour ceux qui se retrouvent dans ce type de situation, je recommande vivement la lecture de Working Effectively with Legacy Code de Michael Feathers, un ouvrage de référence qui explique comment reprendre en main et refactorer efficacement des bases de code existantes.

Ce contraste m’a convaincu que ces principes ne sont pas de simples slogans (ni une lubie de développeurs), mais de véritables leviers de performance et de bien-être pour les équipes de développement. Il en va de même pour d’autres règles de conception comme les principes SOLID, qui complètent parfaitement DRY, KISS et YAGNI et méritent également d’être connus et appliqués.

DRY – Don’t Repeat Yourself (Ne vous répétez pas)

Définition :
Introduit par Andy Hunt et Dave Thomas dans The Pragmatic Programmer, le principe DRY stipule que

Une information, un comportement ou une logique ne doit exister qu’en un seul endroit dans le système.

Pourquoi c’est important :

  • Évite la duplication de code → moins de risques d’incohérences.
  • Réduit l’effort de maintenance → un seul endroit à modifier.
  • Rend le code plus lisible et plus modulaire.

Exemple de violation DRY en Java : dans ce code, on calcul un total dans deux méthodes différentes. Dans la première on le renvoi sans remise, dans la seconde on applique un taux de remise.

Java
public double calculateInvoiceTotal(Invoice invoice) {
    if (invoice == null || invoice.getItems() == null) {
        return 0.0;
    }
    return invoice.getItems().stream()
                  .mapToDouble(item -> item.getPrice() * item.getQuantity())
                  .sum();
}

public double calculateDiscountedTotal(Invoice invoice, double discountRate) {
    if (invoice == null || invoice.getItems() == null) {
        return 0.0;
    }
    double total = invoice.getItems().stream()
                          .mapToDouble(item -> item.getPrice() * item.getQuantity())
                          .sum();
    return total * (1 - discountRate);
}

Refactorisation pour éviter la duplication de code : Au lieu de recalculer le total dans la seconde méthode, on se contente d’appeler la première qui envoi le total, avant d’appliquer un taux de remise.

Java
public double calculateInvoiceTotal(Invoice invoice) {
    if (invoice == null || invoice.getItems() == null) {
        return 0.0;
    }
    return invoice.getItems().stream()
                  .mapToDouble(item -> item.getPrice() * item.getQuantity())
                  .sum();
}

public double calculateDiscountedTotal(Invoice invoice, double discountRate) {
    return calculateInvoiceTotal(invoice) * (1 - discountRate);
}

Avantages de cette approche :

  • Lisibilité accrue : une seule méthode concentre la logique métier, plus facile à comprendre.
  • Moins de risque d’erreurs : toute modification de calcul se fait à un seul endroit.
  • Maintenance facilitée : pas besoin de parcourir le code pour mettre à jour toutes les occurrences.
  • Tests unitaires simplifiés : on peut tester calculateInvoiceTotal indépendamment, et calculateDiscountedTotal n’a besoin que de tests de comportement.
  • Évolutivité : si la méthode de calcul change (ex. TVA, remise supplémentaire), l’impact est maîtrisé.

Pour en savoir plus sur ce principe :


KISS — Keep It Simple, Stupid (Restez simple)

Définition : Le principe KISS stipule que

la solution la plus simple qui fonctionne est souvent la meilleure.

Il met l’accent sur la clarté et la réduction de la complexité accidentelle.

Pourquoi c’est important :

  • Réduit la probabilité d’introduire des bugs.
  • Facilite la compréhension et la relecture du code.
  • Rend le code plus maintenable sur le long terme.
  • Accélère l’onboarding des nouveaux développeurs.

Exemple de violation KISS en Java :

ici, on veut vérifier si un chiffre est pair, ou impair en utilisant une opération binaire et en testant le résultat qui est déjà un boolean

Java
public boolean isEven(int n) {
    boolean isEvenFlag = (n & 1) == 0;
    if (isEvenFlag) {
        return true;
    } else {
        return false;
    }
}

On peut simplifier ce code en évitant les opérateurs binaires et sans faire de test sur le résultat :

Java
public boolean isEven(int n) { 
    return n % 2 == 0; 
}

Pour en savoir plus sur ce principe :

YAGNI — You Aren’t Gonna Need It (Vous n’en aurez pas besoin)

Issu de l’Extreme Programming, le principe YAGNI stipule que

Il ne faut pas implémenter aujourd’hui une fonctionnalité que personne ne demande ou dont l’usage n’est pas confirmé.

Pourquoi c’est important :

  • Évite de perdre du temps et des ressources sur des développements inutiles.
  • Réduit la complexité du code et la dette technique.
  • Facilite la maintenance en limitant le périmètre fonctionnel au strict nécessaire.
  • Permet de se concentrer sur les besoins réels et prioritaires du produit.
Java
public final class ReportGenerator {
    public String generatePdf(Data d) { /* ... */ }
    public String generateHtml(Data d) { /* ... */ }
    public String generateExcel(Data d) { /* pas demandé */ }
}

Ne pas respecter ce principe peut coûter cher. Voici les principaux coûts à considérer :

  • Coût de construction (Cost of Building) : le temps, l’effort et les ressources dépensés pour créer une fonctionnalité (planification, développement, tests). Si elle n’est pas utilisée, c’est un investissement perdu.
  • Coût du retard (Cost of Delay) : l’impact économique ou les opportunités manquées liées au retard d’une fonctionnalité plus prioritaire à cause de développements non essentiels.
  • Coût de portage (Cost of Carry) : la complexité et la charge supplémentaires qu’une fonctionnalité apporte, rendant le reste du code plus difficile à faire évoluer.
  • Coût de réparation (Cost of Repair) : le coût de correction ou d’amélioration ultérieure d’une fonctionnalité inutile ou mal conçue, autrement dit la dette technique.

Pour en savoir plus sur ce principe :

Conclusion

Appliqués ensemble, DRY, KISS et YAGNI offrent un cadre pragmatique :

  • un code simple (KISS),
  • non dupliqué (DRY),
  • focalisé sur le besoin réel (YAGNI).

Formalisez‑les dans vos revues de code et templates de PR pour qu’ils deviennent un réflexe d’équipe.