La documentation projet est sans doute l’élément dont la durée de vie est la plus longue. Les technologies peuvent aller et venir dans le temps mais la documentation demeure l’héritage du projet. Pourtant nous échouons ou luttons souvent dans la maintenance de cette dernière. Les enjeux sont clairs : réaliser des spécifications compréhensibles à la fois par un utilisateur et par un développeur, à jour – donc fiables, donc faciles à maintenir – et non ambiguës afin de pouvoir vérifier objectivement la couverture du besoin. Cet article traite de différentes pratiques de spécification utilisées sur des projets Agile pour répondre à ces enjeux.
Partons du constat
Les principes et pratiques abordés par cet article ont pour point de départ les constats suivants :
- Les utilisateurs ne savent ce qu’ils veulent qu’après avoir vu une première version du logiciel (Principe d’incertitude du besoin de Humphrey).
- Les besoins changent souvent durant le processus de développement du logiciel (Principe d’incertitude de Hadar Ziv – 1996).
- Spécifier intégralement un système interactif est impossible (Lemme de Peter Wegner – 1995).
Un autre constat important concerne les limites de la communication par écrit (cf. schéma ci contre). L’écrit peut être interprété de différentes façons, en particulier lorsque le sujet traité est complexe. Inutile de rappeler que, de nos jours, la réalisation d’un logiciel est une tâche particulièrement complexe tant fonctionnellement que techniquement. D’autre part, ce mode de communication n’offre pas d’interaction directe permettant de vérifier que le message porté est compris. Enfin, il fait l’objet d’un délais de réponse rarement maîtrisé (mode guichet).
Par ailleurs, sur des projets classiques menés en cascade, il arrive que le rédacteur du cahier des charges ne soit pas la même personne que le rédacteur des spécifications fonctionnelles générales, qui n’est pas la même personne que le rédacteur des spécifications fonctionnelles détaillées, qui n’est pas la même personne que celle qui développe, qui n’est pas la même personne que celle qui teste. Sur des projets longs à fort turn-over, il arrive même que ces différents acteurs ne se rencontrent jamais. Comment garantir une bonne couverture du besoin dans ces cas là ?
Forts de ces constats, il s’agit de renoncer à l’illusion de pouvoir spécifier à l’avance l’intégralité d’une solution logicielle. Et au contraire de spécifier, tester et développer au fil de l’eau. De communiquer le plus possible en face à face et d’utiliser l’écrit pour capitaliser l’essence du comportement applicatif visé.
Principes directeurs
Spécifions ensemble : Acteurs métiers et techniques élaborent ensemble les spécifications. Le métier est indispensable pour communiquer les objectifs et la valeur ajoutée visée à travers la fonctionnalité à réaliser. Les développeurs pour tirer parti de l’infrastructure sous-jacente et de la technologie émergente. Les testeurs pour identifier les cas d’usage aux limites. Les idées de chacun sont ainsi mises à profit pour parvenir à la meilleure solution possible. La responsabilité des spécifications et l’engagement associé sont alors collectifs.
Identifions les tests en amont : Afin de lever les ambiguïtés et pouvoir ainsi vérifier objectivement la couverture du besoin. La notion de “test” s’invite dès l’analyse du besoin et la conception. Le test ne sert plus seulement à vérifier l’absence de bug et la bonne couverture du besoin mais également à guider non seulement les développements en soi mais aussi la conception et l’architecture de la solution logicielle.
Règles d’or de rédaction
Exclure toute connotation technique et détails d’IHM de la spécification écrite. La documentation technique de type architecture (logicielle ou matérielle) trouvera sa place dans un dossier d’architecture si le projet le nécessite ainsi que dans le code source. La procédure technique d’installation est automatisée autant que possible (cf. Livraison Continue). Les détails d’IHM peuvent être portés par des maquettes au fil de fer (type Balzamiq). La charte graphique pourra être portée par la fonctionnalité de référence (cf. paragraphe « Balle Traçante »). Quant aux détails tels que les libellés (les messages d’erreurs et de confirmation de saisie d’un formulaire par exemple) coûtent si peu cher à modifier par la suite qu’on pourrait presque se passer de les « tracer » dans les spécifications. Au pire si certaines personnes ont besoin d’être rassurées, on peut les centraliser dans un document annexe (un fichier « properties » idéalement avec des clef parlantes pour les acteurs métier).
Privilégier le vocabulaire de l’utilisateur, le langage naturel (plutôt que télégraphique), au présent (plutôt qu’au futur) et décrire l’expérience utilisateur.
Pratiques
Spécification par l’exemple
Qui n’a pas eu naturellement recours à un exemple pour faire comprendre le comportement complexe d’une solution logicielle à concevoir ou l’expérience utilisateur associée ? La pratique décrite ici repose sur ce levier.
Terminologie
TDD (Test Driven Development) , A-TDD (Acceptance Driven Development), BDD (Behavior Driven Development) et autres sont toutes des pratiques pertinentes et efficaces. Leur multiplication et leur terminologie ont cependant contribué à semer la confusion auprès de leur utilisateurs. Plus grave encore, elles rendent difficile l’implication cruciale des acteurs métier dans le processus de spécification. Afin de couper court à cette confusion, le terme plus parlant de “Spécification par l’exemple” est proposé pour englober ces pratiques. Remarquons que ce terme est également dépourvu du mot “Agile”.
Processus
La notion de User Story (ou « Story ») utilisée ici se limite à la simple formalisation d’un besoin associé à une fonctionnalité à réaliser (exemple : « Paiement d’une commande d’articles »). L’utilisation étendue de la pratique des User Stories est abordée plus loin.
Le présent processus est piloté par les User Stories. Autrement dit, au sein de l’itération, chaque User Story passe individuellement à travers les étapes mentionnées. Elles n’ont pas à passer ensemble chaque étape à la façon d’un mini processus en cascade (ou V) au sein de l’itération.
Pour précision, la « Story » illustrée par le schéma ci-contre (en haut à droite) est sélectionnée à partir du Product Backlog (centralisant la liste ordonnancée des besoins ou fonctionnalités à réaliser) lors de la planification d’itération. En mode flux (Kanban), on pourra appliquer les même étapes sans notion d’itération.
<
Etape Discuter
Un testeur, développeur et expert métier s’assoient avec le product owner et discutent de la Story, décomposent et distillent progressivement le comportement visé en un ensemble de spécifications simples. La conversation (ou atelier de spécification) pourra dans un premier temps se concentrer sur 3 exemples clefs. En commençant généralement par l’exemple « heureux » selon lequel tout se passe bien. Il est généralement facile à identifier et permet d’éclairer la voie.
Point d’attention : A ce stade, il arrive que le porteur du besoin soit déjà dans la solution, court-circuitant ainsi la valeur ajoutée du développeur qui pourrait proposer une meilleure solution (potentiellement moins coûteuse, plus simple, etc) grâce à une technologie que lui seul connaît. La bonne pratique consiste donc à dériver la fonctionnalité à partir de l’objectif de fond. Quitte à remonter en arrière lorsque le porteur du besoin est déjà dans la solution. Dans ce cas de figure le recours au « Pourquoi ? » peut aider (« Pourquoi réalisons nous cette fonctionnalité ? »). Ou de façon plus diplomate « Quels objectifs devons nous atteindre à travers cette fonctionnalité ? ». Ces objectifs guideront les acteurs.
Pour illustrer ce point d’attention, un exemple nous vient de monde de l’aviation. L’expression de besoin associé à la réalisation de l’avion de combat F-16 consistait notamment à atteindre une vitesse de mach 2-2.25. Le concepteur a alors demandé pourquoi. La réponse fut « pour pouvoir échapper au combat ». Le concepteur n’a jamais atteint mach 2. En revanche il a apporté plusieurs innovations permettant d’échapper au combat. Cet avion a eu un tel succès que 30 ans après sa fabrication il est toujours en production.
Etape « Développer »
Le testeur affine les exemples. Le développeur instrumente les spécifications, créé des fixtures (éléments permettant de créer l’environnement propre à l’exécution des tests associés aux exemples) échouant (on vérifie d’abord que le test alerte correctement en cas d’erreur en passant au « rouge ») et implémente ensuite la fonctionnalité (passage « au vert »). Nous aborderons plus en détail dans le paragraphe suivant la notion de « spécification exécutable » associée.
Etape « Démontrer »
Une fois que tous les tests passent avec succès, la Story peut être démontrée au product owner. Les testeurs pourront également tester manuellement. Le passage au vert des tests permet d’affirmer objectivement que la fonctionnalité est « terminée ».
Les spécifications exécutables
Définitions et principe
On appelle « spécification exécutable », une spécification directement connectée au code source de la solution logicielle spécifiée. Chaque exemple porté par cette spécification est directement reliée à un test fonctionnel automatisé. L’ensemble des spécifications rassemblées forment une « documentation vivante ».
Exemple de spécification
Voici ci dessous un exemple de spécification exécutable réalisée avec l’outil Concordion.
A première vue, nous sommes face à une spécification statique. En réalité il s’agit d’une page web générée à partir d’une spécification exécutable (qui se présente sous la forme d’un fichier HTML) interprétée par un outillage de test automatisé. Les tests réalisés avec succès sont indiqués en vert comme sur l’exemple ci dessous (cf. résultats de la recherche) ou en rouge en cas d’échec.
Usage par l’acteur métier
L’acteur métier participe à l’identification des exemples associés à une fonctionnalité qui alimenteront la spécification exécutable (cf. étape « Discuter » du processus). Ce sera généralement le testeur et/ou le développeur qui se chargeront de la rédiger. Après les développements, il pourra se reposer sur ces spécifications pour s’assurer qu’une nouvelle fonctionnalité est bien « terminée » (tests au « vert »).
Plus tard, lorsqu’il souhaitera apporter un changement à un comportement existant de l’application, il pourra là encore se reposer sur elles pour retrouver les règles sous-jacentes à ce comportement. Il le pourra dans la mesure où ces spécifications sont fiables, car intrinsèquement connectées au code source. Dans le cas de spécifications classiques non fiables, nous devons nous reposer sur le code en faisant appel à un développeur pour retrouver les règles dictant le comportement de l’application (avec tous les désagréments que cela implique).
Usage par le testeur
Nous l’avons vu, le testeur participe à l’élaboration des spécifications et à leur affinage en ajoutant notamment les cas de test aux limites (comme les cas d’erreur par exemple). Ce travail peut se faire en binôme avec un développeur. Idéalement, le testeur aura un minimum de connaissances en HTML (rien de bien méchant) nécessaires à l’appropriation de la syntaxe de test afin de rédiger et affiner lui même les spécifications exécutables. Grâce à ces spécifications, il pourra contribuer à la qualité de la solution le plus en amont possible et vérifier en temps réel le bon comportement ou régression de la solution en fonction des évolutions du code. Les cas particuliers non automatisables feront l’objet de tests manuels.
Usage par le développeur
Le développeur participe à la rédaction des spécifications exécutables. Dans le pire des cas, il fait parti des relecteurs avant le démarrage des développements. Ses compétences pourront lui permettre de compléter les exemples et tests dérivés, ou réorienter la structure des exemples afin de rendre les tests associés automatisables. Il implémentera ensuite les tests automatisés associés à la spécification. Ces tests guideront ses développements. Ils vont l’obliger à respecter une architecture permettant l’automatisation des tests. Dans le cadre d’un modèle MVC, la couche front (ou Vue) ne comportera aucune logique métier sujette aux tests automatisés. Ces derniers se glisseront « sous la peau » de l’application organisée en composants à faible couplage. Les bonnes pratiques d’architectures logicielle deviennent alors incontournables. La logique métier se présentera sous la forme d’une « API » facilement testable.
Le développeur pourra se reposer sur cette « ceinture de sécurité » de tests automatisés quant il s’agira de réaliser des évolutions ou de réusiner des volumes de code plus ou moins importants.
Outillage
Seuls Cucumber et Concordion ont été expérimentés pour la rédaction de cet article. Cucumber a été rapidement mis de côté face à la lourdeur de préparation de l’environnement de travail associé. Concordion bénéficie d’une documentation claire, synthétique et illustrée par l’exemple en parfaite cohérence avec la pratique de spécification associée. Par ailleurs sa prise en main est rapide (moins d’une heure). D’autres outils existent tels que Fitnesse (qui a inspiré Concordion). Tous ceux cités ici sont gratuits.
Bénéfices
- La documentation fonctionnelle est fiable (car nativement en phase avec le code).
- L’analyse d’impact des changements et l’accomplissement de ces derniers sont facilités.
- La compréhension commune des besoins entre acteurs métiers et techniques est assurée.
- Les spécifications sont précises limitant ainsi le « faire et défaire » lié aux ambiguïtés autour du besoin.
- La définition de « terminée » d’une fonctionnalité objective et partagée.
- Les efforts de mise à jour des spécifications et d’exécution de tests manuels répétitifs sont limités.
- La Livraison Continue est rendu possible.
- La qualité est élevée et la relation entre client et fournisseur est ainsi améliorée.
Ne pas oublier le travail ne amont
Il existe de nombreux principes, pratiques et astuces pour faire en sorte que les bénéfices de notre automatisation des tests fonctionnels soient supérieurs à l’investissement lié à leur mise en oeuvre et maintenance. Les maîtriser nécessite un apprentissage.
Ne perdons donc pas de vue les autres pratiques qui contribuent quant à elles à éradiquer le défaut à la source :
Test Driven Developpement qui permet de couvrir très exactement le besoin sans code superflu, de concevoir nativement une solution ouverte aux tests, de réusiner le code afin de rendre ce dernier facilement maintenable.
Programmation en binôme qui permet de limiter les erreurs humaines et de trouver des solutions plus simples, faciles à maintenir (deux cerveaux valent mieux qu’un). Mieux vaut privilégier la programmation en binôme obtenant ainsi un feedback immédiat de la part de son pair (comme un pilote et copilote d’avion qui vérifient réciproquement leurs opérations) que la revue de code dont le feedback intervient après coup.
L’Intégration Continue qui permet de détecter et traiter au plus tôt les éventuelles régressions.
Energie et motivation : Une équipe se donne a fond lorsqu’elle est motivée. Cette motivation peut s’obtenir par une bonne utilisation du temps passé en dehors du boulot et une forte concentration au travail. Rentrer chez soi chaque jour à l’heure, passer du temps avec ses amis et/ou sa famille et avoir des activités permettant d’éviter de penser au travail. Faire de l’exercice, avoir une alimentation saine et bien dormir. Au travail, rester concentré, mettre son téléphone en mode silencieux et désactiver les alertes mail et tchat. Définir ensemble une vision, une « noble cause » (« Pourquoi on fait ce boulot ? ») et des valeurs fondamentales qui fédèrent. Les objectifs quant à eux doivent permettre de libérer la créativité des membres de l’équipe. Cf. article « The Art of Agile Development: Energized Work » et celui sur le Leadership Tribal.
User Stories et Cas d’Utilisation Textuels
Commençons par un exemple de User Story.
Exemple de User Story :
ID : #33
Titre : Paiement par carte bleue
Résumé : En tant que client du site internet FoireAuLivre.com je peux payer ma commande par carte bleue afin de me simplifier la vie.
Tests d’acceptation
Tester avec une carte bleue mastercard : OK
Tester avec une carte sofinco : KO
Tester avec une carte expirée : KO
Tester avec un numéro de carte incorrect : KO
Tester avec un montant supérieur à 100 € : OK
Notons l’absence de connotation technique et de détails d’IHM ainsi que l’utilisation d’un template de phrase permettant de se concentrer sur l’essentiel : « En tant que … je peux … afin de ».
Critères « INVEST »
Indépendante des autres User Story de façon à ce qu’on puisse les développer dans n’importe quel ordre. Evidemment, ce n’est pas toujours possible, mais il faut garder cet objectif à l’esprit pour savoir saisir ou créer les opportunités de rendre indépendante une User Story (des techniques de découpage peuvent aider).
Négociable au sens où elle n’est pas orientée « solution ». Autrement dit l’acteur métier porteur du besoin et le développeur ont toute latitude pour parvenir à la meilleure solution possible selon les ressources du moment.
Valeur : elle doit être porteuse de valeur métier.
Estimable par l’équipe de développement.
Small : une User Story doit être suffisamment petite pour être réalisée au cours d’une seule itération. D’autre part, plus une User Story est petite, plus l’estimation est précise et la planification aisée.
Testable afin de pouvoir vérifier la bonne couverture du besoin. Ces tests doivent être rédigés en amont des développements afin de piloter au mieux ces derniers.
Le recours à un support physique (format bristol A5 par exemple) présente des avantages notables :
Simplicité : en cours d’itération, le développeur souhaitant travailler sur une User Story, prend le carton associé et va voir (idéalement accompagné de son binôme de développement et d’un testeur) l’acteur métier pour discuter avec lui du besoin. Ce dernier se remémore rapidement le sujet en lisant le résumé de 1 à 3 lignes. Sans avoir besoin d’un PC, ils annotent le bristol pour par exemple ajouter sous la forme d’un test supplémentaire une règle de gestion découverte au cours de leur conversation. Le développeur repart avec son bristol qu’il pose à côté de son clavier. Pendant ses développements, en un coup d’oeil, il pourra trouver l’information dont il a besoin pour implémenter ses tests et la fonctionnalité.
Taille limitée : le format A5 est une bonne taille car elle oblige les rédacteurs à se concentrer sur l’essence du besoin. Si il n’y a plus assez de place, c’est peut être un bon indicateur révélant la nécessité de découper la User Story.
Planification plus facile : Pendant la réunion de planification d’itération, c’est plus simple de prioriser les User Story en étalant les cartons sur une table ou sur le sol et en les déplaçant de gauche (priorité haute) à droite (priorité faible). La encore, pas besoin de PC et vidéo-projecteur.
Comme dans le cadre de la spécification par l’exemple, les tests portés par la User Story permettent de piloter les développements par les tests. Laissant peu de place aux ambiguïtés et mauvaises interprétations du besoin. Là aussi, une fonctionnalité n’est pas considérée comme terminée tant que les tests inscrits sur la User Story ne sont pas tous passants. La technique des User Stories peut être utilisée seule selon le contexte du projet. Le processus associé ne sera pas très différent de celui déjà décrit.
Pour aller plus loin, l’ouvrage de référence est « User Stories applied » de Mike Cohn.
Cas d’utilisation
Dans certains contextes exigeants, les User Stories ne peuvent pas être considérées comme des spécifications mais simplement comme un support transitoire et éphémère permettant de planifier plus facilement les itérations et piloter les développements par les tests. Dans ce cas, on peut utiliser les cas d’utilisation textuels en complément pour formaliser davantage les choses et apporter la cohérence d’ensemble. Le cas d’utilisation textuel n’est pas par définition un cas d’utilisation UML. L’UML est généralement considéré comme trop difficile d’accès pour un utilisateur ou le client (parfois même pour les développeurs non initiés).
Là encore, commençons par un exemple :
Titre : Commande d’article(s)
ID : 45
Résumé : Ce cas d’utilisation décrit le processus de commande d’un ou plusieurs articles en partant de la recherche jusqu’à la confirmation du paiement.
Acteur concerné : Internaute
Pré-condition : L’internaute s’est authentifié sur FoireAuLivre.com.
Scénario nominal
- L’internaute lance une recherche d’article.
- Il sélectionne l’article de son choix parmi les résultats affichés et l’ajoute à son caddie.
- L’internaute affiche son caddie, vérifie le montant à payer et choisit de payer sa commande.
- Il opte pour le paiement par carte bleue.
- Il saisit son numéro, la date d’expiration ainsi que le cryptogramme de sa carte bleue.
- Un message affiché sur le site interne lui confirme que le paiement a été accepté.
- L’internaute reçoit un email lui confirmant que sa commande est enregistrée et résumant le contenu de cette dernière.
Scénarii alternatifs
3.a L’internaute répète l’étape 2 ou 1 puis 2.
4.a.1 L’internaute opte pour le paiement par Paypal.
4.a.2 L’internaute saisit ses identifiants Paypal.
4.b.1 L’internaute opte pour le paiement par chèque.
4.b.2 Un message indique à l’internaute l’adresse à laquelle envoyer le chèque ainsi que le numéro de commande à joindre à ce dernier.
6.a Le paiement à échoué, l’internaute en est informé.
Cet exemple est un cas d’utilisation plutôt « haut niveau ». On peut au besoin descendre un peu plus et détailler certaines étapes. Dans ce cas, on souligne généralement dans le cas d’utilisation « haut niveau » le mot clef de l’étape en question illustrant le plus le sous cas d’utilisation lié (exemple : « payer », « recherche »,…). On établit ainsi un lien hiérarchique entre le cas d’utilisation et ses sous cas d’utilisation. On peut ainsi établir une cartographie des cas d’utilisation sous forme d’arborescence afin de faciliter la recherche d’information.
Le périmètre fonctionnel d’une User Story est la plupart du temps inférieur à celui d’un cas d’utilisation « haut niveau ». Autrement dit, un cas d’utilisation peut contenir plusieurs User Story mais l’inverse est rare (à moins de rédiger des cas d’utilisation bas niveau décrivant des mécanismes invisibles pour l’utilisateur).
Pour aller plus loin, l’ouvrage de référence est « Rédiger des cas d’utilisation efficaces ».
Balle traçante
A défaut de trouver une métaphore moins morbide, la pratique suivante reprend les principes de la balle traçante.
Définition Wikipedia : Une munition traçante est un type de munition muni d’un dispositif pyrotechnique émettant de la lumière tout au long de la trajectoire vers la cible. L’ogive étant très visible à l’œil nu, elle permet au tireur de suivre la trajectoire de la balle par rapport à la cible afin d’apporter des corrections à sa visée.
Dans le cadre d’un projet de développement logiciel, la pratique de la « balle traçante » consiste à réaliser de A à Z au démarrage du projet une fonctionnalité clef. On réalise la conception graphique et ergonomique ainsi que les développements jusqu’à obtenir un feedback d’utilisateurs (ou représentants d’utilisateurs) à partir de la fonctionnalité concrète terminée. Nous adaptons ensuite cette fonctionnalité en fonction de ces feedbacks et utilisons cette dernière comme fonctionnalité de référence « éclairante ». C’est donc cette fonctionnalité qui porte les spécifications graphiques et ergonomiques. C’est a partir d’elle que l’on va dériver les autres subtilités graphiques et ergonomique des autres fonctionnalités.
Par la même occasion, cette pratique permet de valider ou affiner l’architecture et les technologies choisies. Par ailleurs, la pratique de la balle traçante peut s’étendre au processus de déploiement. A partir de cette fonctionnalité « terminée », nous pouvons dès maintenant automatiser la procédure de déploiement et l’éprouver sur tous les environnements dont on dispose. Nous réduisons ainsi très tôt les risques liés à l’architecture et au déploiement et réduisons par la même occasion le stress reposant sur l’équipe.
[…] pratiques de spécification utilisées sur des projets Agile pour répondre à ces enjeux. Lire l’article Public visé : […]