En écrire moins mais pas pour risquer plus


Cet article est une traduction a posteriori d'un billet d'abord publié en anglais, parfois des mois en arrière, mais que je tiens à traduire.

La factorisation a quelque chose de fascinant en ce qu’elle permet d’en écrire toujours moins pour le même résultat sinon mieux :

less is more

Autre façon de parler d’efficacité. Cependant, ce n’est pas toujours l’approche idéale et je vais expliquer pourquoi.

Permalien pour la section Restez DRY, Évitez de DIE Restez DRY, Évitez de DIE

DIE signifie “Duplication Is Evil” et DRY “do not repeat yourself”. L’idée est donc la même : réduire les répétitions inutiles et les redondances dans le code.

Les devs vont donc abstraire la logique dans des fonctions ou des helpers qu’ils vont ensuite appliquer chaque fois qu’ils en auront besoin.

Cela permet de centraliser les modifications et le modèle peut être assez puissant mais j’ai vu tellement de contre-exemples impliquant des devs de tous niveaux.

Permalien pour la section Attention aux wrappers sans intérêt Attention aux wrappers sans intérêt

Wrapper ou entourer les fonctions natives est une pratique inutile la plupart du temps. Par exemple, en PHP :

function getArrayParts($array, $start = 0, $end = 1)
{
    return array_slice($array, $start, $end);
}

Il s’agit ci-dessus d’un alias qui reprend array_slice. À l’époque où j’ai vu ce code, le but affiché était d’avoir un wrapper personnalisé qu’on pourrait faire évoluer par la suite.

Étrange mais surtout dangereux, ce que la suite des événements a confirmé avec des bugs introduits silencieusement.

Le problème c’est le contexte d’utilisation de la fonction qui peut beaucoup varier même dans une base code réduite.

Bonjour les effets de bord ! Très pénible à déboguer, qui plus est, étant donné qu’une modification ne génèrera pas forcément d’erreur fatale mais potentiellement des erreurs d’affichage suivant le contexte, plus dures à repérer.

Permalien pour la section Évitez le mode ninja Évitez le mode ninja

La lisibilité générale du code peut être largement impactée par votre factorisation.

Exemples en PHP et JavaScript :

$result = ( CONDITION ) ? CONDITION2 ? 'value1' : 'value2' : 'value3';
let num = 7, str = num + "", num = +s;

Moins de code mais compliqué à lire. Cet écueil est d’autant plus difficile à éviter que le dev pourra utiliser ce genre de syntaxe pour paraître malin.

Malheureusement, c’est juste une illusion et je n’ai jamais vu ce genre de besoin en entreprise. Au contraire, ça peut même faire peur aux équipes soucieuses de la maintenance.

Permalien pour la section Ne suranalysez pas votre projet Ne suranalysez pas votre projet

Il faut bien sûr prendre en compte les évolutions potentielles et les usages de l’app qu’on va coder, mais tout excès dans la phase d’analyze peut devenir nocif.

Par exemple :

pour l’instant, pas besoin mais ça servira plus tard et on aura pas à le coder à ce moment-là.

Non. Codez-le justement au moment où c’est nécessaire. Bien penser son modèle de projet et le code en termes d’évolution est la bonne voie mais si vous prenez deux fois le temps pour produire une base code type open-source qui doit prendre en compte tous les univers parallèles, c’est vite l’impasse.

À moins que vous soyiez sur un projet d’envergure internationale type WordPress, vous risquez d’y laisser de précieuses réserves d’énergie et d’argent.

Permalien pour la section Le coût caché de l’abstraction Le coût caché de l’abstraction

Tous les codes n’ont pas vocation à être factorisé à l’extrême pour respecter la règle DRY à la lettre. Certaines fonctions peuvent paraître similaires et pourtant leur contexte d’utilisation les éloigne fortement.

Ignorer cet aspect peut amener à coder des helpers avec une liste de paramètres à rallonge qui finissent par rendre le code quasi illisible :

static function renderTemplate($template, $is_debug = false, $is_single = true, $theme = "default", $has_bottom = true) {
    // some code
} 

Ici pour couvrir tous les cas, on oblige le dev qui va employer le helper à remplir une flopée de true et false pour au final juste rendre un template.

C’est moins pratique à l’utilisation et plus sujet aux erreurs. Chaque fois qu’on va vouloir faire évoluer le helper, le risque d’introduire des effets de bord voire des régressions est décuplé.

C’est moins facile à maintenir !

Permalien pour la section Le paradoxe de l’abstraction Le paradoxe de l’abstraction

Ne vous y méprenez pas. Le but de l’article n’est pas de vous inciter à éliminer toute forme d’abstraction ou de complexité dans le code.

Ne partez simplement pas dans l’extrême inverse qui est tout aussi risqué.

Plus l’abstraction s’avère utile et plus on va vouloir la mettre à toutes les sauces, et plu on aura de chances, paradoxalement, de provoquer des erreurs.

Plus il y a de code qui dépend de la même abstraction, autrement dit d’un code à un autre endroit, et plus la maintenance de ce bout de code devient complexe.

Permalien pour la section Comment résoudre ce paradoxe ? Comment résoudre ce paradoxe ?

“Duplication Is Evil” mais une abstraction inappropriée est tout aussi mauvaise pour le projet. Il faut éviter d’utiliser la même interface à tout prix pour des usages trop différents.

Le signal d’alarme est souvent la présence de beaucoup de blocs de code imbriqués dans les fonctions pour couvrir une large gamme de cas (ex : switch cases à rallonge).

Le mieux est d’en débattre en équipe à travers des revues de code ou des séances de pair programming.

Permalien pour la section Le rasoir d’Occam pour conclure Le rasoir d’Occam pour conclure

L’approche la plus simple d’un problème est bien souvent la meilleure nous enseigne la parabole du rasoir d’Occam.

Au lieu de chercher à produire la base code la plus extensible possible, conservez-là simple et lisible, surtout dans les premières itérations.

KISS et YAGNI sont des exemples d’approches similaires à ce principe.

Cette page est aussi disponible dans d'autres langues :