Affichage des articles dans Génie Logiciel

Conjectures et réfutations

Karl Popper est un philosophe des sciences ayant fait des apports majeurs à l’épistémologie, avec entre autres sa définition de la démarche scientifique par la réfutabilité. Je me suis amusé à appliquer certaines de ses thèses à la mise au point de programmes, en supposant qu’un programme soit une connaissance objective au même titre qu’un théorème ou qu’une théorie scientifique.

Si on applique ces thèses, on peut démontrer que la lecture du code source ne suffit pas, par la seule puissance de la déduction, à prouver que le programme est correct. D’ailleurs, un test ne peut pas prouver la validité d’un programme, au contraire, il ne peut que mettre en évidence une erreur. Popper démontre que le raisonnement inductif n’est pas la méthode rationnelle pour se rapprocher de la vérité, mais que nous procédons par conjectures et réfutations. Tout raisonnement fonctionnerait à partir de théories que l’on essaie de contredire, par l’expérimentation ou par la logique. Pour valider un programme on cherche à le mettre en défaut, ce qui est pourtant intuitivement l’inverse du résultat visé.

Je vois donc la programmation comme l’ajout de conjectures sur un fond initial (langage de programmation employé, librairies, système d’exploitation, autres applications). Fond qui, en passant, est également réfutable selon Popper, contredisant les philosophes affirmant que tout raisonnement rationnel ne se fait qu’à partir d’une base de vérités que l’on augmente par déduction.

En poussant plus loin l’analogie, le test unitaire consisterait donc à tenter de réfuter la conjecture (le programme). Popper affirme également qu’une théorie qui en supplante une autre doit expliquer mieux ou plus de choses que la précédente mais également « survivre » aux mêmes tentatives de réfutation. C’est ce que j’appellerais les tests de non-régression.

Mais alors, si programme = conjecture et test = réfutation, que penser de cette pratique XP : « écrire systématiquement les test unitaires en premier, et ensuite seulement le programme qui va passer les tests unitaires avec succès » (le fameux Test-Driven Development) ?

En termes « popperiens » cela revient à faire des réfutations avant même de conjecturer ! Les « XPeurs » feraient-ils de la programmation d’une façon irrationnelle ?

Disons plutôt que cela signifie que la conjecture est implicite, qu’elle trotte dans la tête du programmeur (ou est discutée au sein du binôme pour ceux qui pratiquent également le « pair-programming ») pendant la programmation des réfutations/tests. Et c’est seulement une fois que toutes les réfutations sont posées que l’on rédige l’équation ou le programme. Connaissant à l’avance les tests, la programmation ressemble plus à mon avis à une sorte de régression au sens statistique (trouver la courbe qui passe par tous les points connus à l’avance).

Cela revient à peser tous les cailloux de la terre et trouver l’équation vraie pour tout un tas de cailloux donné afin de formuler la gravité. Il me semble donc à la fois plus rationnel (et plus intuitif, je l’avoue) de poser la conjecture (le programme) avant d’essayer de le réfuter (par les tests).

La connaissance objective

La connaissance objective

Là où je rejoins les partisans du test extrême c’est que la qualité d’un programme est proportionnelle à la quantité de tests tentant de le réfuter, tout comme la meilleure théorie, toutes choses égales par ailleurs, est celle qui se prête et s’est prêtée au plus de tentatives de réfutation.

Ayant moins de connaissances en philosophie qu’en informatique, il est probable que je résume mal la pensée de Popper, je vous recommande donc chaudement la lecture de l’original, comme par exemple « la connaissance objective » disponible en livre de poche.

Du management…

Un collègue m’a récemment envoyé un papier de Steven Kerr  »On the folly of rewarding A, while hoping for B« . Il montre que très souvent le discours officiel d’une organisation est de vouloir un comportement, tout en mettant en place un système de récompense/punition qui dans les faits pousse les gens à un comportement tout autre, voire opposé. :roll: Par exemple on demande à un joueur de sport collectif d’avoir « l’esprit d’équipe » et pourtant on ne félicite que les actions individuelles. D’ailleurs, on ne résume un match de football que par les noms des buteurs. Ainsi, agacé qu’un joueur fasse trop de passes à l’adversaire, un entraineur sanctionne chaque passe ratée ; au match suivant personne ne fait plus aucune passe et le jeu est bloqué. :pinch:

Exemple parfait de contre-productivité : une clinique veut réduire le nombre de décès. On met donc en place une prime qui doit récompenser le chirurgien qui a moins de X décès dans le mois sur le billard. Que va-t-il se passer ? On peut parier que dans les mois suivants tous les chirurgiens vont toucher la prime… En effet, dès qu’ils s’approchent du quota fatidique, ils repoussent toutes leurs opérations délicates au mois suivant ! Du coup les gens décèdent dans leur lit en attendant l’opération, mais ça n’est pas le problème du médecin. Tout le monde touche sa prime, cependant le nombre de décès global de la clinique augmente…

Cette théorie semble être un grand classique du management (le papier remonte à 1975) mais je n’en avais jamais entendu parler. Et quand je l’ai lu j’ai eu une illumination. Tenez, imaginez que vous vous tenez sur une grande étendue de sable, une sorte de drap rouge à la main ; vous le secouez un peu en vous demandant pourquoi vous avez ça à la main :wassat: , quand vous entendez un bruit de sabots tagadam-tagadam dans votre dos :unsure: et *vlan* :pinch:

… au génie logiciel.

J’ai compris beaucoup de choses sur le projet de développement en lisant cela.  Finalement, qu’est-ce que l’on demande aux développeurs ? Comportement ‘A’ : « Produire du code de qualité, maintenable par autrui ». Qu’est-ce que l’on récompense ? Les délais de livraison : comportement ‘B’. Je n’ai jamais vu un développeur se faire sanctionner pour du code mal écrit, non commenté ou conçu de traviole. Par contre, prenez du retard sur le développement, vous allez vous faire enguirlander et vous devrez rester tard tous les soirs pour faire bonne figure. Donc, si vous avez envie d’une vie de famille ou de loisirs : bâclez votre code. :devil: Ne commentez surtout pas, et ne testez que si l’on vous le demande. De toute façon c’est votre successeur qui sera pénalisé par le code non-maintenable ; c’est lui qui ratera ses deadlines :D *niark niak niak niak* (<- rire façon méchant dans James Bond).

Rendre ‘A’ inévitable, récompenser  ‘B’

On va pas chasser le naturel, alors on va biaiser un peu. Continuez à récompenser les délais (‘B’), mais considérez ‘A’ comme acquis. Il faut intégrer le contrôle qualité continu et automatisé à tout environement de développement. Il doit être impossible de réussir un « build » (et donc de livrer) si l’artefact en question ne respecte pas tous les standards en vigueur. J’ajouterai même une revue obligatoire de tout code, du stagiaire au chef de projet, avant de pouvoir placer le post-it dans la colonne « fait » du tableau blanc. Passez par la colonne « en revue », ne touchez pas vingt-mille francs. Enfin pas tout de suite… Soyons fous, récompensons la qualité :alien: .

PS: merci à Majirus pour l’article

Lu dans la doc d’un framework : « un CouteauDeTable est une classe Java standard (ou POJO) qui sert à couper viande ou légume. Il doit sous-classer AbstractCouteau, n’utiliser dans ses signatures que les types Lame et Manche, avoir un constructeur comme ceci et des attributs comme cela. Et son nom doit finir par CouteauImpl. » :eek: C’est-à-dire, tout SAUF un POJO :ermm: !

Ce terme est de plus en plus utilisé comme synonyme de « classe Java », ce qui est une erreur. Alors, qu’est-ce qu’un POJO au juste ? Et bien, ma définition en serait : une classe Java sans contraintes particulières.

En fait, il n’y a pas de POJO sans framework. Un framework peut vous demander de sous-classer telle ou telle classe, respecter telle convention de nommage, limiter ci, imposer cela. A l’opposé (et un peu en réaction aux EJB), on parle de POJO quand le framework justement n’impose rien sur vos classes Java : vous développez old-school, classes et interfaces sont conçues librement.

Exemple de framework pas-POJO-du-tout :

« Sous-classez com.framework.AbstractPrintableObject, avec un constructeur public à un paramètre de type com.framework.PrintEnvironment. Ne définissez que des méthodes publiques non synchronisées qui lèvent comme exceptions des sous-classes de com.framework.PrintException. etc… etc… »

Le même, façon j’aime-les-POJO :

« Passez-lui un POJO, le framework d’impression essaiera de découvrir ses getters publics et de construire une représentation textuelle imprimable. »

Je précise que j’écris ça dans un souci d’illustration, loin de moi l’idée de suggérer une quelconque supériorité d’une approche tout-POJO par rapport au framework de grand-papa qui offre un lot de superclasses abstraites. Par exemple, je trouve l’approche « pur-POJOs (ah oui, plus plein d’annotations) » un peu hypocrite… :whistle:

Si l’on regarde l’ensemble des projets open-source, on trouve en moyenne près d’une ligne sur cinq de commentaires, selon un papier de Dirk Riehle (voir également sur son blog).

On y lit que les projets étudiés restent tous très proches de ce taux. Un peu plus surprenant, on découvre que cette densité de très exactement 18,7% de commentaires est la même quelles que soient la taille du projet et la taille de l’équipe :shock: . L’article propose l’explication que c’est dû à une grande auto-discipline de développeurs, très probablement motivée par l’exposition publique du code source à conjointement au nom de son contributeur.

Autre information de cette étude : ce qui influe sur le taux de commentaires est l’âge du projet. Quand un projet avance en maturité son taux de commentaires baisse (enfin d’un chouïa, à 18%). Je suppose que lorsque l’on maintient un code depuis plusieurs années, certaines choses deviennent tellement comprises, partagées et habituelles qu’elles en deviennent implicites, du coup on aurait l’impression d’écrire des évidences dans les commentaires en détaillant trop…

Ces statistiques soulèvent quelques petites questions…    Par exemple, peut-on considérer ce taux comme idéal ? En effet, c’est le taux auquel des développeurs volontaires et auto-organisés aboutissent à moyen terme, quel que soit le projet. Moins de commentaires c’est pas assez, mais plus de commentaires c’est inutile, voire c’est introduire du bruit. Si l’on admet ce taux comme “idéal”, le taux de commentaires de mes développements s’éloigne-t-il significativement de ce chiffre ? Quelle conclusion en tirer ? Ainsi, vous est-il déjà arrivé comme à moi, de vous dire qu’avant de diffuser tel ou tel de vos composants en open-source, il faudrait faire un petit effort de packaging et de documentation ? :blush:

Question, en guise de conclusion : si vous imposez de préciser dans les commentaires le ou les auteur(s) de toute classe et méthode par exemple (c’est-à-dire en Java : @author partout), est-ce que vous obtiendrez un code globalement mieux commenté et/ou plus lisible ? :twisted: