Professionnellement, j'assiste mes développeurs dans la conception d'une application permettant la location de véhicule.
Il y a quelques temps, notre responsable fonctionnel a décidé que notre application devait dorénavant vérifier qu'il y avait bien à minima 18 ans entre la date de naissance du locataire du véhicule et la date d'obtention du permis de conduire.
S'il n'y avait pas ces 18 années d'écard, l'application devait refuser la souscription du contrat de location.
Évidemment, l'ajout de cette contrainte étant parfaitement logique, personne n'a soulevé la moindre objection, d'autant que le développement à réaliser ne posait absolument aucun problème.
Cependant, à notre grande surprise, lors de la mise en production, nos clients n'ont pas été du tout content.
En effet, pour certains des locataires déjà présent en base de données, la date de naissance et/ou la date d'obtention du permis avait été mal renseigné, et il n'était donc plus possible pour eux de louer à nouveau un véhicule.
Et ensuite, il est très possible d'avoir obtenu son permis avant 18 ans, soit parce que c'était possible à l'époque, soit parce qu'il a été passé dans un autre pays.
Sous des dehors vertueux, cette contrainte était donc en réalité trop restrictive et nous avons donc été finalement obligé de la désactiver.
L'autre exemple concerne atoum, mon framework de tests unitaires simple, moderne et intuitif pour PHP 5.3+.
Hier, j'ai fais en sorte que les méthodes de test ne contenant aucune assertion figurent dans le rapport généré par atoum lorsqu'il est utilisé en ligne de commande.
L'un de mes développeurs avait en effet été pertubé, avec raison, par le fait que cette information ne soit pas remontée, et j'ai donc fait en sorte de résoudre le problème.
Là encore, il n'y avait aucun obstacle technique et la modification semblait parfaitement logique.
Et pourtant, elle a posé un problème à l'un des utilisateurs de atoum, qui utilise Mockery et uniquement Mockery pour réaliser des assertions dans certaines de ses méthodes de test.
Or, atoum n'a aucune connaissance des assertions exécutées par Mockery, et en conséquence, il considére que les méthodes de test dans lesquelles il est utilisé ne contiennent aucune assertion.
En conséquence, le rapport généré est donc pollué par des messages qui n'ont pas lieu d'être.
Je suis donc obligé de revoir ma copie et de trouver une solution pour pouvoir désactiver en fonction des besoins le listage des méthodes de test vides
.
Dans les deux cas, malgré l'expérience des différents acteurs et la sécurité offerte par les tests, les modifications effectuées ont eu des effets très inattendus qui ont rendu obligatoire à minima une nouvelle évolution ou bien carrèment un retour en arrière.
Cependant, dans les deux cas, le code a été mis à la disposition de l'utilisateur final très rapidement et ce dernier a donc pu faire un retour tout aussi rapidement.
Et comme le contexte était encore frais dans les esprits des développeurs, il a été très facile d'effectuer la correction ou de réfléchir à une solution potentielle.
Pour connaître l'impact d'une modification sur les utilisateurs d'un logiciel, il ne faut donc pas jouer à Madame Irma en essayant de les deviner, mais bien mettre le logiciel le plus rapidement possible à la disposition de ses utilisateurs, même s'il ne couvre pas l'ensemble de leur besoin fonctionnel.
Cela permet d'avoir leur feedback très rapidement et donc d'agir dans les meilleurs délais et à moindre coût pour éventuellement ajuster le tir.
Bref, il faut être agile et appliquer le précepte release early, release often !
.
4 réactions
1 De Cyrano - 06/07/2012, 16:57
Je reste dubitatif devant les deux exemples cités.
Le premier est une erreur d'analyse fonctionnelle. Je schématise bien entendu, mais il est difficile de blâmer qui que ce soit dans l'équipe de développement, son rayon restant les décision en matière de code et de technique, pas de choix fonctionnels.
Le second exemple me laisse une impression plus floue. Corrigez moi si je me trompe : si je m'appuie sur ce que j'ai lu ou ce qui m'a été présenté comme « des bonnes pratiques » en matière de programmation, et à fortiori en POO, une classe ne fait qu'une seule chose (ou formulé autrement n'a qu'une seule responsabilité) et le fait bien. Sauf erreur de mes sens abusés, une classe traite des données et me retourne (ou non) une information ou une donnée ou quoique ce soit que je défini avant même de commencer à l'écrire. Ça rejoint d'une certaine manière le premier exemple en ce sens que fonctionnellement, ton système retournait des informations particulières et tu as modifié la structure de ce retour en y ajoutant (même si le raisonnement est logique) un élément supplémentaire.
Quand bien même tu aurais publié plus tôt, ça n'aurait rien changé au problème : En modifiant la sortie, il fallait s'attendre à provoquer des comportements inattendus pour certains utilisateurs alors que ça en aurait enchanté d'autres. À froid comme ça, mon approche dans un cas similaire aurait probablement été d'écrire une extension pour obtenir un résultat complété par les éléments que tu as rajoutés laissant au utilisateurs satisfait la continuité du fonctionnement de l'outil et offrant une alternative à ceux qui auraient voulu un résultat plus complet, et préservant par là-même une rétro-compatibilité. Non ? C'est grave docteur ? Me gourre-je ?
2 De mageekguy - 06/07/2012, 17:12
@Cyrano : Dans le cas de atoum, le fait que je publie la modification au plus tôt a permis de détecter immédiatement le problème.
Si j'avais attendu, et éventuellement fait d'autres développement en me basant sur cette fonctionnalité (dans notre cas, il y a peu de chance mais je généralise à dessein), le problème aurait peut être bien plus épineux à résoudre.
Et quand à la responsabilité unique de la classe, c'est bien ce qui est fait, vu que le rôle de la classe que j'ai modifié est en effet de gérer le refactoring.
Il m'a semblé de plus pertinent de proposer cette nouvelle fonctionnalité par défaut, au vu de son utilité.
Faire une extension, ou n'importe quoi d'autre, et devoir l'activer explicitement pour en profiter m'a semblé très peu probant.
Et même avec ce mécanisme, mon utilisateur aurait été pénalisé le jour ou il l'aurait mis en œuvre.
3 De Cyrano - 06/07/2012, 18:47
Ben quand j'évoque l'unicité de responsabilité d'une classe, dans mon esprit, ça sous-entend grosso-modo pour reprendre l'exemple cité qu'elle retourne une informations sous une forme donnée : en soi, la forme de ce retour est la responsabilité de la classe : en modifiant ce retour, on modifie la responsabilité et c'est bien là que réside le risque pour ceux qui sont déjà utilisateurs et satisfaits de la première version.
Pour rebondir sur les bonnes pratiques que j'ai pu glaner ici où là en matière de POO, je n'ai plus en mémoire la formulation exacte mais ça ressemble à « un code doit être ouvert à l'extension mais fermé à la modification », et au niveau des compatibilité dans le cas de mises à jour pour ceux qui on déjà développé des applications basée sur notre code, ça me semble important.
Maintenant il est vrai qu'Atoum est fort jeune, et qu'en prenant... le taureau par les cornes (je trouve que c'est de circonstance ) très tôt dans le processus de développement, les risques sont très atténués : la même chose dans deux ans et ce sera à n'en pas douter une autre sorte de... heu... de corrida ? :P
4 De mageekguy - 06/07/2012, 20:49
@Cyrano : tu parles de l'un des principes de SOLID, qui est la philosophie suivie pour le développement de atoum.