Doit-on abstraire son code en vue d'un code propre ?

Beaucoup de développeurs traversent une phase du code dit “propre”. Moi le premier, j'ai souvent opté pour l'abstraction d'un morceau de code avec le risque d'en compliquer sa lecture mais l'objectif était atteint : pas de duplication et c'est un code de qualité. Je peux aussi faire valoir mes qualités de développeur. Cependant, la question a parfois le mérite de se poser : doit-on tout abstraire ?

Analyser la situation

Lorsqu'on développe, il est impératif d'analyser la situation et le contexte dans lequel on est. Travailler tout seul ou dans une équipe (aussi petite soit-elle) n'a pas le même impact.

Si vous êtes indépendant durant le développement et que vous n'avez pas d'autres développeurs avec vous sur le projet, il est facile d'être à la recherche du code parfait. Vous pourrez le mettre en oeuvre en fonction du temps qui vous est disponible et de la qualité attendue pour ce que vous faites. En revanche, dès lors que vous êtes 2 sur le développement d'une fonctionnalité ou d'un projet quelqu'il soit, les choses vont se compliquer.

Dans notre secteur d'activité, vous tomberez régulièrement sur des passionnés ou des développeurs religieux. Ils sont souvent très bons en tant que développeur mais ils peuvent parfois être tout aussi dangereux pour votre projet. Souvent à la recherche de la perfection, ils peuvent mettre en péril votre équipe et le projet lui-même car ils risquent de privilégier la qualité de ce qu'ils produisent à la finalité. C'est là qu'il est important d'analyser la situation. Produisez-vous pour vous-même et dans ces cas là, faites comme bon vous semble, ou, codez-vous quelque chose dans un but précis ? C'est cette 2ème partie de la question qui va nous intéresser car on oublie trop souvent que l'on ne code pas que pour nous. On développe dans un but de faire fonctionner quelque chose qui sera très certainement vu et revu par d'autres développeurs dans le futur qui n'auront pas les mêmes compétences ni les mêmes technologies à leur disposition (version du langage, de nouvelles problématiques hardware ou software, etc…).

Une bonne duplication vaut mieux qu'une mauvaise abstraction

L'abstraction d'un code, sa factorisation, est complexe. Le cheminement et la conception d'une abstraction ne sont pas donnés à tout le monde. Ce n'est pas un don pour autant mais vous pouvez le voir comme le fait d'avoir de la logique, certains le sont plus ou moins. Que vous soyez le Lionel Messi du code, vous ne lisez pas l'avenir et vous ne pouvez pas avoir connaissance des futures demandes et évolutions d'un projet. Bien entendu, vous avez votre petite idée alors vous pensez qu'il sera possible de tout prévoir.

Le risque de chercher à factoriser son code très tôt est de se retrouver quelques mois ou années plus tard avec une mauvaise abstraction. Or, quand on remet les mains dans une boîte noire c'est souvent très compliqué. Il faut aussi espérer que ce soit bien documenté et testé car toute factorisation implique d'avoir un code stable.

Si vous gardiez votre code dupliqué, il est alors plus facile d'intervenir dessus car visuellement, tout est à votre disposition. L'effet “boîte noire” est moindre, on retrouve régulièrement un code très procédural et il vous est facile d'intervenir entre 2 lignes pour venir effectuer une modification. Mais alors qu'en est-il lorsque vous avez 10 modifications à faire dans 10 endroits différents ?

A quel moment doit-on factoriser ?

Pour les puristes, dès qu'il existe deux codes équivalents, il faut le réduire en une seule fonction commune. Prenons alors cet exemple

Le processus de la ligne 4 et de la ligne 12 sont communs : une addition. On l'écrit 2 fois, dans 2 fichiers différents et c'est une instruction algorithmique. Dès lors, pensez-vous qu'il serait intéressant d'écrire une fonction add(a,b) qui nous retourne l'addition des 2 résultats ?

Vous trouvez ma méthode d'addition plutôt complexe ? Effectivement, pour faire une simple addition, j'ai ajouté une complexité supplémentaire qu'est le “currying” (c'est à dire de créer une fonction à partir d'une autre fonction). On peut donc appeler cette fonction de 2 manières différentes. Beaucoup d'entre nous serons d'accord pour s'accorder que c'est inutile. L'addition est une instruction algorithmique de base qui n'a pas forcément besoin d'être factorisée. Que vous en ayez 1 ou 100 dans votre projet, vous n'avez pas d'intérêt ni l'envie de l'avoir dans une fonction commune.

C'est un exemple plutôt extrême mais s'il fallait en retirer quelque chose, je vous conseillerais de repousser le curseur de votre alarme à l'abstraction. 2 instructions identiques ne doivent pas faire obligatoirement l'objet d'une factorisation. Cela va vous prendre du temps et complexifier votre projet alors que certainement, vous n'en aurez plus jamais besoin. Je dirais donc de mettre votre curseur à 3 éléments (et pourquoi pas 4 ou 5 alors ?).

3 éléments identifiables est un signal plus important que 2. Vous augmentez de 50% la présence de la duplication par rapport à 2 et vous avez plus d'éléments dans vos mains vous permettant d'obtenir une vision plus globale de ce qu'il faudrait abstraire. Ceci dit, ce n'est pas une obligation pour le faire.

L'abstraction implique une forte connaissance du métier

Outre le fait d'être dans la capacité d'implémenter une factorisation du code, il est important d'avoir une bonne connaissance du métier que vous êtes en train de traiter. L'avantage du notre est de pouvoir jouer avec énormément de sujets. Un jour vous développez un système pour une assurance, puis le lendemain vous passez sur un sujet de facturation qui implique des connaissances dans la comptabilité. Or, nous vivons dans un système évolutif et changeant. Les lois d'un pays ou les décisions d'une entreprise varient et il n'est pas toujours facile d'en prévoir tous les cas. Cela ne doit pas vous empêcher d'être à la recherche d'une abstraction mais il faut être capable de mesurer l'impact de ce que vous allez faire. Votre développement sera-t-il résilient à ces changements ? Rien n'est moins sûr.

Factoriser grâce à un pattern reconnaissable

Malgré la complexité du sujet, il y a parfois des éléments reconnaissables qui mettent en évidence des patterns. Ces derniers peuvent être un atout majeur lorsqu'ils sont identifiables car ils sont universels. Prenons l'exemple d'un projet où vous avez énormément de règles métiers à gérer, le pattern specification peut alors devenir intéressant. L'avantage d'utiliser un design pattern est, qu'à mon sens, tout bon développeur sera en capacité de le comprendre et de le reprendre. Ils sont modélisés avec un modèle UML et on y trouve généralement suffisamment de ressources (internet ou livre) pour comprendre son utilité.

Viser la qualité du code n’oblige pas à la factorisation

Il faut bien comprendre que la factorisation est un processus complexe qui cherche à vous diriger vers le principe DRY (Don’t repeat yourself) mais qui peut parfois rentrer en contradiction avec KISS (Keep It Stupid Simple). Je ne dis pas qu’il ne faut jamais factoriser bien au contraire, mais avant cela, je vous conseille d’être plus perfectionniste dans la qualité de votre code (ce qui n’oblige pas à l’abstraction) plutôt que de viser le simple fait de ne pas vous répéter.

La réalité est qu’aujourd’hui, on est intéressé par le livrable (et un livrable de qualité). Si vous cherchez à produire une Ferrari que personne ne pourra utiliser en tant que client, ça n’a aucun intérêt. Cependant, la qualité du code passe par certains principes et modes de fonctionnement qui doit vous amener à optimiser ce que vous faites. Parfois, vous n’aurez même pas le choix que de coder avec le cul par manque de temps et de moyens ou tout simplement, car c’est plus intéressant de faire ce choix à ce moment-là.

Pour améliorer la qualité de votre code et éviter des erreurs évidentes, il vous faut procéder à des relectures de code. Cela permet notamment d’inclure plus de personnes sur ce qui est en train d’être développé et d’y apporter plusieurs visions. Il y a bon nombre de choses qui impliquent qu’un code est plus ou moins de qualité. Que ce soit sa testabilité, sa performance, sa complexité algorithmique, sa capacité à réagir à des exceptions, suivre un standard, etc… Tous ces éléments apportent de la matière à un projet et n’ont pas besoin de réagir à une quelconque abstraction.

De fait, factoriser son code est une bonne chose mais qui doit être mesurée. Le faire par pur plaisir peut avoir des répercussions sur un projet ou sur votre équipe en tant que telle. Il ne faut pas pour autant en faire tout un débat interminable lors d’une réunion d’équipes et tomber dans le syndrome du développeur religieux. Commencez par vous poser la question du coût que cela va impliquer, si l’équipe existante est capable de suivre son implémentation et surtout, de son utilité au moment où vous souhaiteriez le faire.