Devoxx'10 : Emergent Design par Neal Ford

La session de neal ford avait lieu jeudi après midi et visait à nous faire partager sa vision du design d’application. tout d’abord il nous invite à aller consulter après la séance, la suite d’articles qu’il a écrit sur developerworks.

1. Définitions

Lorsque l’on conçoit une application, il faut se limiter au design nécessaire et suffisant pour avancer. Plus on attend et plus on dispose d’informations et de contexte permettant de prendre de meilleures décisions. Pour réaliser cela, il faut également trouver un mode de fonctionnement se situant entre l’approche waterfall et le ‘cowboy hacking’ (la correction d’un bug directement en production par exemple et de façon plus générale une approche désordonnée dans le cycle de développement). L’approche waterfall est la transposition d’une méthode pensée pour des réalisations industrielles. Dans le monde industriel, on ne peut pas faire de changements une fois le produit sorti des chaînes de fabrication. Dans notre monde, on peut faire des changements tout au long de la vie du produit. Le design émergent existe tout de même dans le domaine du matériel, neal nous donne l’exemple de l’ipod qui à évolué au fur et à mesure des générations de l’appareil.

le design émergent de l'ipod

Nous devons trouver des abstractions et des designs que l’on peut répartir en 2 catégories: technique(validation, sécurité, transactions…) ou métier (règles métier, fonctionalités partagées…).

C’est au travers de quelques définitions que neal nous explique comment faire évoluer le design de son projet:

design vs architecture

design: ce qui est difficile à changer plus tard
architecture: aussi peu de cette chose que possible (c’est la base sur laquelle on s’appuie)

le design et l'architecture

2. Les freins

On peut voir 2 formes de complexité: inhérente ou accidentelle. La compléxité inhérente est la complexité qui découle directement du problème que notre logiciel doit résoudre. La complexité accidentelle découle du fait que nous concevons des choses plus compliquées qu’elles ne devraient être. Ce la vient également de la façon dont on présente le projet. On nous demande rarement de réaliser ‘la chose la plus simple possible qui fonctionne’ mais on nous présente plutôt les choses comme un projet très compliqué avec des parties non encore définies et qui doit toujours fonctionner dans 10 ans!

La dette technique, c’est la différence qu’il y a entre un bon design et un mauvais design. Lorsque l’on doit développer une nouvelle fonctionalité, cette dette entraîne plus de temps de développement que si on ne l’avait pas. On paye donc les intérêts de cette dette et il faut donc convaincre le pilotage du projet qu’il faudra payer cette dette à un moment ou à un autre. Cela n’est pas génant de créer de la dette technique durant un “coup de bourre”, il faudra cependant traîter celle-ci dans la phase faisant suite à ce rush. Cela prend du temps de gérer cette dette et on devra user de refactoring pour en venir à bout.

la dette technique

Pour mesurer cette dette, on peut notamment utiliser 2 mesures
– la complexité cyclomatique
– le couplage afférent (le nombre de classes utilisant une classe)

Sonar, l’outil de gestion de qualité de code, donne un aperçu visuel de la dette technique, ce qui est intéressant car souvent une image parle mieux qu’un gros paquet de chiffres.
en cherchant des valeurs élevées de chacune de ces mesures dans le code source de struts2 neal a trouvé des problèmes de conception qu’un développeur du projet était justement en train de corriger. Cette complexité a maintenant été extraite dans un framework interne à struts2.

La généricité rampante, autrement appelée sur-ingéniérie, c’est la tendance qu’on a à créer des solutions génériques censées s’adapter à d’hypothétiques évolutions du produit. On a tendance à se dire que plus on ajoute de couches à notre produit plus il sera évolutif. C’est ainsi que l’on pourra obtenir un logiciel qui sait tout faire… sauf ce pour quoi il a été fait. Cela fait partie de la complexité accidentelle dont on a parlé au début.

3. Les facilitateurs

Le développement dirigé par les tests (TDD) a plus à voir avec le design qu’avec les tests. Le design va émerger de ces tests. Le TDD permet d’avoir une compréhension ciblée des intentions, ce qui entraîne de meilleures abstractions et moins de complexité accidentelle. L’exemple donné est celui d’un programme permettant de trouver un nombre parfait. Il s’agit d’un nombre dont la somme des facteurs -tout en excluant ce nombre- est égale à ce nombre (6,28…).

Le refactoring permet à chacun d’être propriétaire du code. En effet, chaque développeur peut faire des modifications, même si ce n’est pas “son code”. Il permet de réparer les broken windows. Cette expression fait référence au fait que si une fenêtre est cassée dans un batîment, alors elle encouragera le vandalisme et on verra le batiment être dégradé rapidement. On pourra aller consulter ce post sur le syndrôme de la décharge publique qui fait référence à ce même phénomène dans notre beau métier.
le refactoring permet également de corriger régulièrement les abstractions obsolètes. Ainsi le code devrait se fortifier avec l’âge.

4. A la recherche de patterns idiomatiques

Les patterns idiomatiques sont les patterns de développement que l’on ne va pas trouver dans le livre du GoF mais qui sont propres à notre application et qui seront révélés par les refactorings en appliquant par exemple le pattern composed method décrit par Kent Beck dans sont livre sur les best practice smalltalk.
Il s’agit de:
– diviser son program en méthodes effectuant une tâche identifiable
– garder toutes les opérations d’une méthode au même niveau d’abstraction
Il en resultera un programme avec plein de petites méthodes, chacune faisant quelques lignes.

L’exemple donné est celui d’une fonction addOrder qui effectue un certain nombre d’actions au sein d’une transaction SQL. On obtient finalement le code suivant bien plus compréhensible que l’original, en utilisant le pattern composed method puis le pattern command. Neal ford se fait également l’avocat de Groovy qui permet grâce aux closures d’exprimer encore plus clairement les choses et d’influer sur le design.
On aborde également la possibilité d’utiliser la métaprogrammation en plaçant un @MaxLength(length = 10) sur un attribut. Cela permet d’indiquer clairement qu’il s’agit d’une contrainte.

Les langages induisent différents styles d’abstraction, néanmoins il n’est pas nécessaire de changer de langage pour changer de style d’abstraction. Les objets représentant la réalité ne sont pas toujours la meilleure abstraction. On donne l’exemple du jeu pacman qui avec peu de resources pour tourner doit trouver une autre abstraction. Au lieu de calculer la distance entre pacman et lui, un fantôme sent l’odeur de pacman pour se diriger. Cette odeur n’est présente que dans une zone proche de pacman et décroit rapidement. Quand il ne peut pas sentir pacman il se déplace aléatoirement.

pacman

5. Conclusion

– You don’t know what you don’t known (il s’agit d’une référence à la citation de Donald Rumsfeld: “there are unknown unknowns”). Il ne faut pas prendre des décisions de design de manière prématurée, mais plutôt attendre que cela soit nécessaire de prendre ces décisions.
– Trouvez des patterns idiomatiques pour construire un design emergent