Qu'est-ce que la qualité de code ? Vous vous doutez que l'état de l'art dans notre métier ne permet pas aujourd'hui de donner une réponse claire qui fait l'unanimité !
C'est justement pour échanger sur ces questions que nous nous retrouvons lors des Round-Tables de la communauté Software Craftsmanship Paris, dont voici le compte-rendu de la session de Janvier dernier. Ce post n'est qu'une tentative de rapporter les échanges de façon organisée, sans autre prétention. La discussion peut aussi continuer dans les commentaires !
Un mini sondage pour commencer: tout le monde pense qu'il est facile de reconnaitre du mauvais code, en revanche il semble bien plus difficile de reconnaitre du bon code! En l'absence d'expert UX (user experience), "qualité" se réfère ici à la qualité interne du code, par opposition à la qualité perçue par l'utilisateur.
“Deliver and forget”
Une super définition qui nous a bien plu à tous est l'idée qu'un code de qualité est un code qui une fois livré ne fait plus parler de lui: "Deliver and Forget!". Cela comprend l'absence de retour de bugs et la fonctionnalité satisfaite. Cette définition ne concerne que le court-terme, et doit être complétée par "facile à estimer et à retravailler plus tard pour des évolutions".
Difficile de parler de qualité de code sans rappeler les 4 règles de "simple design" : un design est simple dans la mesure où, par ordre de priorité décroissante, il:
- Passe tous ses tests (et est donc évidemment testé)
- Maximise l'expressivité du code (facile à lire et à comprendre)
- Minimise toute duplication (certains échangent l'ordre des points 2 et 3)
- Contient le moins d'éléments possibles (méthodes, classes, lignes de code)
Le nombre de lignes de code seul n'est pas un bon critère de jugement. Quand il s'agit de comparer un langage et un autre, la concision est souvent discutée comme un avantage, pourtant la verbosité de nos langages usuels a souvent des vrais avantages (voir Why I Like The Verbosity of Java, voir aussi les commentaires)
Si 3 mois après j'arrive à relire mon code alors c'est qu'il est pas trop mal.
Une technique pour améliorer est de s'arrêter à mi-chemin, puis de laisser un partenaire prendre le relais (ping-pong). Ce qui nous amène évidemment au TDD !
Qualité et TDD
Nous sommes pour la plupart convaincu des avantages du TDD. Cependant on entend une remarque intéressante d'un développeur qui travaille au forfait en solo "pas toujours en TDD, avec TDD surtout pour des parties complexes, parce qu'on vend du code, pas des tests !" Justement, pourquoi on ne vendrait pas aussi les tests ?
Cette remarque oblige aussi à mentionner le post de Martin Fowler sur le sujet que croire que la qualité interne fait perdre du temps est le problème: "the true value of internal quality - that it's the enabler to speed" (voir TradableQualityHypothesis par de Martin Fowler).
C'est l'occasion de rappeler que TDD est très utile même pour des fonctionnalités faciles, quand il s'agit d'élaborer l'API du point de vue du client qui la consommera. Un participant précise : "dans mon équipe le pair programming est utile pour tout le monde, même pour les experts ou les seniors".
Un sondage délibérément simpliste: "préférez-vous un code avec un bon design, ou un code bien couvert par des tests?" Une majorité préfère plutôt un code bien testé, car l'essentiel est de pouvoir intervenir dessus avec moins de danger, et que le fait d'être bien testé rend plus probable que le design ne soit pas trop mauvais. Bien entendu on entre rapidement dans un dilemme circulaire, car un bon design devrait aussi permettre l'ajout facile de tests a posteriori.
En réponse à ce dernier point, un participant nous rapporte qu'il a fait l'expérience de ressortir un vieux code d'un design correct, et de tenter a posteriori de le rendre testable, en binôme. Il s'avère que cela demande un effort beaucoup plus important qu'imaginé, "c'est super dur, ça nous a demandé un énorme effort pour atteindre seulement 40% de couverture".
Comment améliorer la qualité du code legacy? La règle du Boy Scout: "laisser le camp plus propre qu'on ne l'a trouvé". Pour le reste c'est un sujet bien trop vaste en lui-même !
Qualité de design
La qualité est aussi au niveau du design, qui n'est pas toujours facile à comprendre: la qualité du code c'est aussi de ne pas avoir besoin de lire des centaines de classes et méthodes pour comprendre la structure de l'application. Il s'agit donc là d'exprimer l'intention de design, ce qui peut nécessiter l'ajout de commentaires (patterns par exemple), même si on rappelle que en général les méthodes et classes doivent être compréhensibles par leurs noms seuls, sans besoin de commentaire supplémentaire.
Utiliser le vocabulaire du client et du métier aide énormément
à réduire l'effort de découverte. On pense alors évidemment à l'Ubiquitous Langage de Domain-Driven Design.
Un bon design implique une bonne séparation de la définition des différentes fonctionnalités constituant l'application. Cette séparation se manifeste par l'absence de mélange de l'implémentation de plusieurs fonctionnalités dans le même module. De même, nous n'avons pas la définition d'une seule fonctionnalité qui est dispersée sur plusieurs modules. Mélange et dispersion donc comme une autre façon de juger la qualité d'un design.
Enfin en matière d'architecture ou de style, "A Rome, faites comme les romains". Une nette majorité des développeurs présents mettent la priorité à l'homogénéité du style et de l'architecture, devant sa qualité propre. Par exemple, si le style de développement sur un projet existant est d'avoir des POJO anémiques et des comportements regroupés dans une couche de services, alors on doit suivre ce style même si on préfèrerait nettement un style plus objet, avec des objets riches de données et de comportements.
Qualité d’abstraction et niveaux d’abstraction
On a tous entendu parler de niveaux d'abstraction, mais c'est quoi vraiment ? En général une abstraction définit une intention, et le niveau en dessous implémente cette intention. Par exemple l'intention de "notifier un utilisateur" peut être implémentée par "envoyer un email". Il est important de ne pas mélanger plusieurs niveaux d'abstractions dans un même scope.
Dans le cas des méthodes, c'est assez simple: si le nom d'une méthode est dans un certain niveau d'abstraction, alors le contenu de la méthode est d'un niveau inférieur. Par exemple la méthode ci-dessous raconte une histoire à un niveau d'abstraction homogène :
void remindMeetup(...){ if(isMember(...) && meetup.isNextDay(...)){ notifyUser(...); } }
Alors que cette autre version mélange intention (notifier si membre et si meetup est demain) avec l'implémentation de la notification (envoyer email) :
void remindMeetup(...){ if(isMember(...) && meetup.isNextDay(...)){ sendEmail(...); } }
Dans le premier cas, notifyUser() n'est qu'un alias sur sendEmail(), spécifiquement pour faire que le code se lise comme une phrase de niveau d'abstraction homogène. Si chaque méthode correspond à un mot, le statement qui appelle ces méthodes correspond alors à une phrase qui doit se lire de façon naturelle et fluide.
Sur cet exemple, on peut critiquer que cette méthode alias est un cas de spéculation (prévoir le cas où on souhaiterait changer de mode de notification), et est donc une complexité inutile tout de suite, auquel cas on crie YAGNI ! D'un autre coté on peut aussi considérer que l'utilisateur veut réellement notifier, et que ce mot correspond au vrai niveau d'intention de l'utilisateur. On rappelle aussi qu'il faut se méfier des spécifications orientées "solutions" (envoyer un email) au lieu de décrire le problème (juste notifier).
En matière d'architecture ou de design, regarder les noms de classes suffit en principe, le contenu des classes est trop détaillé; les noms de classes forment aussi un niveau d'abstraction, le plus haut visible dans le code.
Qualité bas niveau
Violations Sonar, PMD, Checkstyle: certaines équipes ne sont pas assez matures pour respecter les conventions de codage et d'hygiène minimale. A ce propos : "si on commence à penser en terme de copier-coller, c'est déjà qu'il y a un problème !" A ce niveau par contre on est facilement unanimes dans nos opinions !
Notes
La liste approximative des participants est sur la page Meetup du groupe.
Un grand merci à Arolla, sponsor salle et galettes de cette soirée !
Quelques liens pour en savoir plus:
https://fr.wikipedia.org/wiki/Software_craftsmanship
http://manifesto.softwarecraftsmanship.org/
http://www.meetup.com/fr/paris-software-craftsmanship/
http://www.amazon.com/Software-Craftsmanship-The-New-Imperative/dp/0201733862
Directeur Technique d'Arolla