Monday, April 13, 2015

La conception dans le monde agile


Contrairement aux méthodes traditionnelles de développement logiciel (waterfall, en V, RUP, etc), un projet agile préconise l'élimination de la phase de conception en début de projet et la remplace par une conception juste-à-temps intégrée dans les sprints.  Ceci est logique, puisque sans la phase d'analyse de tous les requis du système,il ne serait pas possible de faire la conception d'un système dont les besoins et les contraintes ne sont pas connus
Par contre, en mode agile, l'équipe doit accomplir une story dans un temps limité (timebox) donc le focus est important.  Il faut donc trouver un équilibre entre faire la conception d'une solution qui répond au besoin actuel dans le temps demandé, mais qui pourra également être facilement adapté dans le futur pour répondre aux besoins qui surviendront à ce moment.

Principes de la conception agile
L'atteinte de cet équilibre dans la profondeur de la conception dépend fortement de la taille du système, du niveau d'expérience des membres de l’équipe et du niveau de qualité nécessaire.  Les principes suivants peuvent nous aider à guider nos choix:

Simplicité
En mode agile, il s'agit d'en faire juste assez pour répondre au besoin de façon simple, afin de respecter le temps qui est prévu à cet effet tout en rendant le code et le design facile à comprendre pour les autres.  Chaque décision qui demande du temps supplémentaire pour faire du code plus générique peut se baser sur la croyance de XP (Extreme Programming) qui stipule que "ça ne servira pas de toutes façons" ("You Ain't Gonna Need It Anyway" - YAGNI).  En effet, sans connaître les besoins futurs, il est difficile de prévoir le code qui y répondra.  Le livre Lean Software Development: An Agile Toolkit présente également le principe “Think big, act small, fail fast, learn rapidly”.  Ceci signifie de faire des petites actions concrètes afin de prouver ce qui fonctionne et ce qui ne fonctionne pas, tout en ayant la vue d’ensemble.

Facilité de changement
Le principe Open-Closed de SOLID, qui représente un design où il est facile de changer des comportements sans impacter ce qui est déjà là (Open for extension, but Closed for modification) explique bien l'objectif à atteindre.  Ce principe est très important car il permet d'étendre des fonctionnalités rapidement avec un risque de régression réduit.  Dans cette catégorie, on peut aussi penser aux principes de cohésion et de couplage, qui favorise les changements futurs en créant des classes avec une seule responsabilité, avec peu de dépendances et logiquement regroupées entre elles.  Ceci permet donc des modifications à des endroits bien ciblés et avec peu d'impact sur le code existant.

Testabilité
Lorsqu'il n'est pas possible d'implémenter une nouvelle fonctionnalité sans affecter le code existant, il devient essentiel de faire du réusinage ("refactoring").  Ceci implique de modifier l'architecture actuelle afin de la rendre prête à recevoir des changements pour mieux répondre aux nouveaux besoins, ou pour mettre en commun des fonctionnalités.  À ce moment, la façon la plus efficace de valider que la fonctionnalité existante n'a pas régressée est par les tests unitaires.   Il est impératif d'avoir une couverture de test suffisante, ainsi que d'avoir des bons tests pour éviter de devoir réusiner les tests aux aussi.  De bons tests ont les caractéristiques suivantes:
  • Robustes, c’est-à-dire qu’ils passent toujours et ne dépendent pas de l’environnement
  • Isolés, soit qui ne sont pas dépendants d’autres tests
  • Boîte grise, qui permettent de tester la fonctionnalité de l’extérieur, tout en sachant comment elle est faite à l’intérieur.
Par contre, il n'est pas toujours nécessaire de faire le réusinage au moment où le changement est requis; des fois il peut être plus approprié et plus efficace d'implémenter la solution autrement, comme par exemple:
  • quand les changements peuvent être combinés dans un plus grand réusinage
  • lorsque le changement est trop risqué (de causer des anomalies, ou d’empêcher de compléter la story)
  • il est possible de remplacer le code par une autre “route” créée à côté du code existant, ce qui permet de réduire les effets du changement, puis de transitionner progressivement vers le nouveau code
Dans ces cas, un user story peut être créée afin de documenter le réusinage qui doit être fait, ce qui a pour effet de faire ressortir la dette d'architecture du système.

Architecture cible
Sans la phase de BDUP (Big Design Up-Front) des méthodologies classique, le design d'un système qui utilise une méthodologie agile est dit "émergent", c'est-à-dire qu'il évolue itérativement en fonction des besoins réels, au fur et à mesure que les fonctionnalités sont ajoutées.  Ceci corresponds directement à la philosophie même de Agile.
Par contre, lorsque la complexité du système ou encore le nombre d'équipes qui travaillent dans le même système augmente, ce principe atteint ses limites.  Aucune équipe ne maîtrise le système dans son entièreté, et peut difficilement prévoir les changements qui arriveront dans le futur.  De plus, à cause du focus précis de l'équipe sur le sprint en cours, il y a un risque pour plusieurs équipes de résoudre les mêmes problèmes de façon différentes, ou voir même contradictoires.
Ainsi, arrivé à une certaine échelle, il est important de définir un guide afin d'orienter et de supporter la conception au fur et à mesure de l'évolution du système.  Ce guide est connu sous le nom d'architecture cible, ou architecture intentionnelle.  
L'architecture cible détermine donc certaines technologies, certains patrons de conceptions ou encore d'outils et  frameworks qui vont ajouter des contraintes aux équipes agile lors de l'implémentation des solutions sans les limiter dans leur recherche de solutions.  Cette dernière partie est importante, car il ne faut pas perdre l'avantage que procure le mode agile, avec ses équipes auto-organisées à qui on donne le pouvoir de choisir et de se débrouiller.
L'architecture cible doit être définie dès le début d'un projet afin d'orienter le développement des équipes lors des premiers sprints.  Par contre, comme toute chose agile, elle doit aussi pouvoir évoluer en fonction de l'utilisation et des besoins qu'en font les équipes ainsi que pour les changements au niveau des technologies et des requis de l'entreprise.  Il devient donc nécessaire d'identifier et gérer des user stories d'architecture, qui ont pour but d'adapter l'architecture en fonction des facteurs exprimés précédemment.  La définition des stories et leur implémentation est plus complexe en terme d'architecture qu'il ne l'est en terme de code fonctionnel, parce que les changements sont situés au niveau structurel à la base du système et ils ont des impacts sur tout le système.  En effet, un changement majeur au niveau de la base de données par exemple pourrait briser le concept de "système fonctionnel en tout temps" pendant plusieurs sprints, le temps que tous les modules s'adaptent à la nouvelle architecture.  Ainsi, lorsque possible, ces changements devraient être faits de façon itérative, en implémentant des couches de conversion ou en supportant l'ancien système en parallèle avec le nouveau.
L'important c'est l'équilibre.  En utilisant un bon mélange de simplicité, de design émergent et de tests robustes, l’équipe peut atteindre l’équilibre entre prévoir le futur et le faire maintenant.  L’identification des réusinages à faire et une bonne définition de l’architecture cible va aider l’équipe à atteindre l'équilibre entre encadrer le développement et le limiter.  C'est le défi qu'apporte l'agilité!
Références:

No comments:

Post a Comment