KISS : Keep it simple, stupid : la complexité est superflue (keep it simple) si elle n’apporte rien (stupid). Qui peut aller contre cela ? Tant cette règle paraît être une question de bon sens.
Mais alors pourquoi est-ce que l’évocation de ce principe (KISS) provoque un tel signal d’alarme ?
Nous développeurs, nous aimons les petites règles simples qui nous rappellent les bonnes pratiques. Parmi celles-ci, il y en a une que j’entends souvent : KISS. Cette règle est globalement acceptée par tout le monde. Pourtant, dans certain cas, son utilisation me laisse perplexe. Les explications dans cet article.
Imaginez ce dialogue :
« Alors Gaël, comment vas-tu implémenter ce nouveau feature ?
- « Je vais partir de cette classe qui utilise les fonctionnalités de cette API. Mais il va manquer un certain nombre de contrôles. Il faudrait alors que je rende l’exécution un peu plus comportementale en fonction de ce que l’on demande. Il me semble que je vais faire un petit pattern strategy.
- Wow, tu n’as pas peur que ça complexifie tout ?
- Non je ne pense pas. On va injecter les comportements. Ça les rend testables facilement, et ça simplifie pas mal la maintenance et la lisibilité.
- Je vois le point mais bon, KISS, avec un if c’est réglé. Fait plutôt comme ça s’il te plaît. »
J’ai peut-être un peu grossi le trait mais ce genre de discussion vous est-elle déjà arrivée ? Moi oui, un peu trop souvent même. En fait, dès que j’entends l’évocation de cette règle, je ne peux m’empêcher de penser que les ennuis commencent. Et pourtant, ça ne devrait pas.
KISS : de quoi parle-t-on exactement ?
‘‘Keep it simple, stupid.” La tournure est superbe. À la première évocation, on comprend immédiatement. La complexité est superflue (keep it simple) si elle n’apporte rien (stupid). Qui peut aller contre cela ? Tant cette règle paraît être une question de bon sens.
En effet, la règle KISS est très utilisée et pas seulement en développement informatique. En fait, Wikipédia nous indique qu’il s’agit d’un principe de design utilisé dans la Navy américaine dans les années 60. On trouve des références à cette règle dans de nombreuses disciplines comme l’UX design, la politique, le management etc.
En y réfléchissant, on peut la voir comme une variante du rasoir d’Ockham.
Numqam ponenda est pluralitas sine necessitate.
Guillaume d’Ockham
En d’autres termes, les multiples ne doivent pas être utilisés sans nécessité. Il n’est pas difficile de trouver des commentaires sur ce principe et, personnellement, je ne trouve rien à y redire.
Entre simplicité et complexité
Mais alors pourquoi est-ce que l’évocation de ce principe (KISS) provoque un tel signal d’alarme ?
Pour établir ce bel acronyme, KISS, on a simplifié la formulation et éludé une partie du message. Pour moi effectivement, la sur-simplification nuit directement à l’expression du message, ce qui est un peu ironique quand on prend en compte la teneur du message.
C’est ironique mais c’est également la clé : trop simple, on nuit au but final, et trop complexe, on risque de ne pas répondre correctement à la question à laquelle on souhaite apporter une réponse. Voici une formulation alternative que je préfère :
‘‘Everything should be made as simple as possible, not simpler.”
Albert Einstein
Cette formulation est attribuée à Einstein. Je trouve qu’elle délimite bien la « zone » dans laquelle chercher les solutions. Il faut faire simple mais pas trop car il faut répondre à la demande. Soit dit en passant, dans le cas de systèmes un tant soit peu complexes, il faut aussi faire attention à ne pas dégrader la position des autres demandes.
Voilà ce à quoi devrait nous amener l’application de la règle KISS, c’est-à-dire à un juste équilibre entre simplicité et complexité. Il est dommage que la formulation ne nous l’indique pas mieux.
« Y’a qu’à, faut qu’on »
Parvenir à la conclusion précédente m’a demandé pas mal d’introspection car, voyez-vous, j’ai un gros défaut. J’ai tendance à beaucoup (trop) intellectualiser les choses. Cela m’amène souvent à des solutions un peu trop complexes. C’est certainement pour cette raison que j’ai souvent entendu cette règle KISS. Mais bien souvent, la radicalité de la simplicité était bien au-delà de la complexité que j’avais introduite.
À mon tour alors de vous poser une question : « Combien de fois vous êtes-vous dit : ceci est trop simple ? » En revanche, je suis sûr que vous vous êtes déjà interrogés sur la complexité d’une solution. C’est assez naturel je pense.
En fait, il est assez difficile d’atteindre un bon équilibre entre simplicité et complexité. D’autant plus que la simplicité va souvent de pair avec la rapidité d’exécution. Alors, peut-on proposer des façons de faire qui nous aident dans les pratiques de développement ?
YAGNI
‘‘You ain’t gonna need it.”Ou littéralement « tu n’en auras jamais besoin ». En plus de vous donner un petit côté baroudeur du code, ce principe est particulièrement utile pour éviter le superflu (simple ou complexe). Si un élément n’a pas prouvé son utilité, alors il n’a pas sa place dans le code.
C’est par exemple le cas de l’anticipation des besoins utilisateurs. Je pense qu’il nous est déjà arrivé à tous de nous dire : « Tiens, si j’ajoute ça, le client va… » [remplacer par les idées qui vous passent par la tête dans ces moments-là]. En réalité, rien ne nous assure que cela attirera l’attention du client ni même que cela peut avoir un impact. En fait, si on pense à ajouter des fonctionnalités, on peut simplement les proposer au client.
C’est aussi un bon critère de refactoring quand on simplifie le code. En fait, YAGNI est une formulation simple du rasoir d’Ockham, mais moins dénaturée que KISS à mon avis.
Refactoring
La simplicité est quelque chose de trop complexe à produire pour être produite en première intention. En premier lieu, il faut simplement écrire son code, quelque chose qui fonctionne. À ce moment-là, vous devriez avoir une vision différente du problème. C’est le moment pour commencer à réécrire le code afin de le simplifier et de le rationnaliser. Pendant l’écriture de cet article, on m’a d’ailleurs soufflé cette citation de Saint-Exupéry souvent utilisée dans le design des jeux vidéo :
« Il semble que la perfection soit atteinte non quand il n’y a plus rien à ajouter, mais quand il n’y a plus rien à retrancher. »
Antoine de Saint-Exupéry
J’avoue ne pas avoir grand-chose à ajouter, ni même à retrancher d’ailleurs.
Je pense qu’un des objectifs de l’étape du refactoring repose sur cette simplification. Toutefois, il faut bien prendre garde pendant cette étape à ne pas tout casser. En effet, en modifiant le code, en supprimant du code, on peut rapidement s’y perdre.
Le all inclusive : le TDD
Voici une pratique qui est un peu la synthèse des deux précédents points. Et en plus elle nous permet de sécuriser l’étape du refactoring.
Découvrir le TDD a été pour moi un déclencheur. Voici une bonne introduction à ce qu’est le TDD. Vous pouvez également lire ce très bon article de notre consultant Gaëtan Elouet (et voir sa vidéo pratique).
Une des choses les plus difficiles à faire dans la pratique du TDD, et qui est pourtant centrale, c’est écrire le code minimal pour « verdir » le test. Et dans notre cas, cela va nous garantir de la simplicité. Ceci étant dit, ça va aussi nous faire écrire des choses un peu étranges.
Par exemple, si on regarde la vidéo de Gaëtan, le code minimal qui convertit le chiffre romain I en 1, c’est… return 1. En réalité, il ne convertit rien : ça renvoie à 1 quelle que soit la valeur d’entrée.
Et l’accumulation de ces couches de simplicité va avoir tendance à produire un code un peu « sale ». Mais avant même que tout le code ne soit trop « salit », le TDD introduit des phases de refactoring. Phase qui nous autorise à réarranger le code produit en utilisant par exemple les codes smell (encore une fois l’article de Gaëtan nous présente bien le sujet, mais vous pouvez aussi lire le livre de Martin Fowler sur ce sujet).
La magie de ce procédé est de faire émerger des designs pendant les cycles. C’est rarement ce à quoi on avait pensé au départ. De plus, selon ma propre expérience, les solutions produites correspondent bien à cet équilibre entre simplification et complexité.
Plus globalement, une solution correctement testée tout au long de la pyramide de tests est un bon moyen de conduire à des solutions extrêmement fonctionnelles et équilibrées.
De l’empathie et des bonnes pratiques de développement
Les clients de nos productions sont les utilisateurs finaux mais aussi nos collègues directs, dans le cas de créations de librairies, ou indirects quand on crée des API.
Mon avis est qu’il faut intégrer un maximum de simplicité quand on fournit des interfaces vers l’extérieur. La complexité, si elle existe, ne devrait pas apparaître au-delà de ces limites (API, façade, interface utilisateur). Il faut viser la facilité d’usage en règle générale.
‘‘If you cannot explain it simply, you don’t understand it well enough. “
Albert Einstein
C’est encore une citation d’Einstein et elle est particulièrement juste. Je vois dans cette citation une extension de la règle KISS, dans une forme qui nous pousse à repenser ce que l’on sait à la lumière de ce que l’on produit concrètement.
Normalement, elle s’applique à l’enseignement, mais finalement, quand on y pense, elle s’applique aussi à la conception logicielle. Si les API sont trop complexes, a-t-on réellement compris le besoin ? A-t-on suffisamment pensé au confort de nos utilisateurs ?
Et puisque nos collègues sont aussi des utilisateurs de nos productions, ce principe peut également s’appliquer à l’écriture du code. Bien nommer ses variables, c’est autoriser les autres à comprendre facilement le code. Faire des fonctions courtes, c’est poursuivre ce même but. Les règles du clean code nous aident à atteindre ce but.
L’invocation de la règle KISS en tant qu’argument d’autorité est assez commun. Souvent, derrière se cachent des raisons moins dicibles, comme le temps que l’on va payer pour faire quelque chose qui, à première vue, peut se faire en trafiquant un peu le code. Finalement, ce type de choix se paie en dette technique et en coût de maintenance du code.
Cet argument d’autorité passe d’autant mieux que cette formulation simplifiée du rasoir d’Ockham semble autoriser une utilisation de ce principe plus large qu’elle n’est en réalité. Il faut accepter la complexité qui est intrinsèque aux outils que nous utilisons et la mettre en évidence. Une fois identifiée, on peut alors se concentrer sur le cœur de notre métier, à savoir simplifier leurs utilisations pour en faciliter les usages.
Vos commentaires
Je trouve le parallèle avec le rasoir d’Ockham très pertinent.
Pour ma part, le point crucial pour bien interpréter KISS est de comprendre que la simplicité n’a d’intérêt que d’un point de vue humain. Après tout, la machine se fiche pas mal de la complexité des instructions, tant qu’elle est capable de les exécuter.
De fait, il y a (au moin) 2 perspectives humaines à prendre en compte dans le développement d’un logiciel :
– l’utilisateur (client)
– le producteur (développeur)
KISS du point de vue client = fournir une fonctionnalité simple, qui se contente de répondre au besoin, sans le surcharger de fioritures non demandées.
KISS du point de vue du dév = fournir un code simple, qui montre clairement ce qui est fait, sans le noyer dans une complexité ou inutile.
Or il se trouve que rajouter un « if » dans cette fonction qui fait déjà 200 lignes, ça augmente la complexité. Refactorer la fonction en petit bout, pour déléguer chaque étape à d’autres fonctions bien nommées, c’est plus complexe du point de vue machine (plus d’instructions) mais plus simple du point de vue humain (plus compréhensible).
Je pense que c’est là dessus que se trouve le mauvais usage de KISS : il ne s’agit pas de la perspective machine (complexité du code brut), on parle d’optimisation sinon, mais de la perspective humaine (complexité de compréhension du code). Être simple ne veut pas dire être simpliste.
Je préfère la formulation « Keep it stupidly simple », dans le sens : oui, l’implémentation doit être simple, mais surtout il faire en sorte qu’elle reste simple, et qu’il faille la garder simple. Du coup, faire un if au lieu d’une stratégie ne va pas garder cette simplicité parce qu’un futur développeur va devoir comprendre le if pour savoir ce que fait vraiment la méthode, contrairement à une stratégie bien nommée. Pareil, si on dit « Oui, mais on peut mettre directement un string pour l’URL de l’API au lieu de piocher dans la config », bah oui c’est simple, mais par contre ça ne va pas le rester et ça va être assez inmaintenable.
Exactement 😉