Bienvenue sur Développement Agile

Référence sur le développement logiciel Agile. Nous traitons de conception, de programmation, de pratiques de génie logiciel, d'essais et d'autres sujets connexes.

La conception importe plus que la production

Publié par David Beaumier le mardi 16 juin 2015 à 10:17

« It’s not the production system that matters most, but the design system ». Du moins, c’est le point de vue de Toyota lorsque son système de production (le fameux TPS – Toyota Production System) est comparé à son système de conception, le TPDS pour Toyota Product Development System.

C’est sans doute pour cette raison que Toyota a toujours accepté de recevoir les représentants des autres manufacturiers dans ses usines. Toyota savait que son principal avantage compétitif se trouvait plutôt au niveau du système de conception de ses produits. Et oui, même si la conception est une activité hautement créative, le système à l’intérieur duquel cette conception se fait peut, à plusieurs égards, être standardisé.

It’s not enough to do your best; you must know what to do and then do your best.

- W Edward Deming

En développement logiciel, bien des équipes ont tendances à associer leur processus de développement à un système de production. Je crois qu’elles auraient plutôt avantage à faire comme Toyota et scinder en deux leur processus : la conception du logiciel et la production du logiciel. Ne vous méprenez-pas, je ne vous propose pas de revenir au développement en cascade!

Je vous propose plutôt d’utiliser l’analogie de la chaîne de montage pour regrouper les activités de construction (build), de vérification, de déploiement et de mise en production de votre logiciel. Le fameux DevOps, quoi. Le serveur d’intégration continue, avec son build, ses tests et vérifications et par la création du paquetage applicatif, joue en quelque sorte le rôle de l’usine. La partie déploiement, mise en œuvre et opérationnalisation du produit équivaut cycle de distribution, de vente et de service habituellement associé au réseau des concessionnaires.

Les activités en amont, incluant le « codage », sont en réalité des activités de conception. C’est seulement un fois que le serveur récupère le code depuis votre gestionnaire de sources que l’usine se met en branle. Tout ce qui se fait avant sert à définir les spécifications structurelles et comportementales de l’application (qu’il s’agisse de l’interface utilisateur ou de traitements d’affaires). Le code source, les schémas de bases de données et la configuration représent les spécifications requises pour bâtir le produit.

Ce n’est donc pas suffisant de mettre votre usine en marche. Vous devez vous assurer qu’elle produit la bonne chose! Heureusement, nous avons la chance en développement logiciel de ne pas avoir à gérer les contraintes physiques. Nous pouvons construire une seule fois le produit et le cloner aussi souvent que souhaité. Il nous est possible de changer ses spécifications, le bâtir et l’essayer en peu de temps et pour peu de frais. Cette flexibilité devrait donc nous permettre de nous concentrer sur le produit à bâtir plus que sur sa fabrication. Ce n’est pas que l’aspect DevOps ne soit pas important, c’est juste que ce n’est habituellement pas en premier lieu pour votre efficacité à bâtir et opérer qu’un client va adorer votre produit.

Ne perdez pas de vue le premier des principes du manifeste agile: le client sera satisfait par la livraison de fonctionnalités à valeur ajoutée.

Principe1 Manifesteagile

Un cadre de travail agile, tel que Scrum, a l’avantage d’offre un modèle itératif pour la conception de produits. Jumelé à un système de production efficace ça permet de réduire la boucle de rétroaction afin de valider que vous avez conçu les fonctionnalités requises pour que votre client atteigne ses objectifs d’affaires.

Et vous, est-ce que votre équipe se perçoit comme un groupe de conception de produits ou se voit-elle plutôt comme une équipe de fabrication?

Copy Driven Development

Publié par Jean-Francois Gilbert le lundi 8 décembre 2014 à 00:00

Copy Paste

On l'a tous déjà fait : on ne sait pas trop comment utiliser un framework X ou encore on ne se souvient plus tellement comment implémenter un algorithme. Alors on cherche sur Bing (je blague !) et on copie le code dans notre logiciel. À première vue, c'est un gain de productivité fabuleux ! Mais lorsqu'on élève cette pratique au rang de méthode de travail, ça devient carrément du "Copy Driven Development". Et c'est dangereux !

Qu’y a-t-il de mal de mal à copier du code trouvé sur internet ? Quelqu'un d'autre s'est déjà tapé le sale boulot et c'est souvent du code spécialisé dont on ne se sert presque jamais. Je serais tout à fait d'accord si les développeurs prenaient le temps de comprendre ce que le code copié fait et comment ça fonctionne réellement. Notez-bien que je m'inclue dans le lot. Je plaide coupable d'avoir pratiquée le Copy Driven Development à plusieurs reprises. Mais j'essaie de me réhabiliter, votre honneur !

Le problème c'est que quand le code trouvé est copié dans votre projet, il devient VOTRE code. Si ça ne fonctionne pas comme prévu, s'il y a des bugs qui surgissent, serez-vous en mesure de les régler ? Si on le modifie, êtes-vous capable de garantir que ça va continuer de fonctionner comme prévu ? Est-ce que vous avez des tests unitaires qui valident son bon fonctionnement ? 

Je ne dis pas qu'on ne devrait jamais copier de code, mais je crois qu'il est de notre devoir de se l'approprier et d'en comprendre les subtilités. Selon moi, il doit servir d'outil d'apprentissage plutôt que de béquille à notre mémoire défaillante ou notre paresse intellectuelle.

Avoiding Layers of Misdirection

Publié par Jean-Nicolas Viens le jeudi 29 mai 2014 à 09:53

Have mocks led you to add frustrating layers of indirection? You are not alone. This is an argument part of the ongoing debate with DHH (David Heinemeier Hansson), Martin Fowler and Kent Beck.

All three of them seem to be in agreement on one thing: mocks should be avoided. This is what I think brings you from layers of indirection to layers of misdirection.

Skippable summary

You may skip this section, even if you are not familiar with the topic. Refer to these links to continue the discussion!

Here are the main parts (I think), in order :

  • Is TDD Dead? David's blog post following his railsconf keynote. The subsequent articles are interesting as well.
  • Their firstsecond and third hangout sessions.
  • Google, twitter

These people have a lot more experience than I do, but I still don't see mocks the same way they do. Hopefully, this will make some sense. As for everything pertaining to this debate : what works for me might not work for you. But, it doesn't mean you should not try it.

This is obviously a vast debate with various angles of approach. If you want test isolation with proper mocking, Gary Bernhardt nailed it in my opinion. Read his post. If the test speed argument is one that bothers you, I suggest you read Corey Haines' article. If you are still unsure that integrations tests can harm you when not done properly, I could not explain it better than J. B. Rainsberger in this video.

What if mocks had nothing to do with it

The angle I want to tackle is one that has confused me a lot over the years. Sorry, there will be no definite answer at the end of this article, just a suggestion for a further conversation. I hope you chip in!

Over the years, I have seen people hate TDD because they had to do mocks. Doing mocks forced them to create new classes. Many of them got confused in their own design after three or four of these classes.

This phenomenon is also seen when you read someone else's code. If it's all in one place, you can read it in one go. Adding layers of indirection makes it harder to follow. DHH puts it this way :

[If I can look at something that fits within 10 lines of clean code], to me that's endlessly easier to understand than sixty lines that has three additional layers of indirection that I have to comprehend first.

I agree with this statement, up until he says "that I have to comprehend first". You don't. The name of the class or method you are calling should be enough to tell you what is happening there. You know the what, not the how. To me this is key to making complex business models work. I never add layers of indirection, I add layers of I-don't-care-ness.

But this is where I get confused. This makes sense to me, but most people seem to disagree. Everyone wants to understand every single layer first, but that is just impossible to do when you live in a world of complex business rules (as opposed to simple CRUD operations).

Maybe this is just a naming problem? Of course, if a method is poorly named, this will become a layer of misdirection. But I think there is more to it.

What if you just made it hard on yourself

Just let go.

Adding a layer, or simply a private method, allows me to understand a small portion of the algorithm faster than anything else. When you write a complex algorithm without tests, what do you do? Write a part of it, refresh, does it work? Yes, keep going. You iterate. Test the algorithm bit by bit. Adding layers allows you to do just that, in a TDD flow that makes sure you get every part of the algorithm right.

Here is a simple example. What does this do?


receipt = new Receipt(employeeNumber);
for(int i = 0; i < items.size; i++) {
    receipt.registeredItems.add(items[i]);
}
return receipt;

You can most likely figure it out, but this version should be faster to understand :


receipt = receiptFactory.create(employeeNumber);
receipt.registerItems(items);

What happened? Did I really remove code? Not at all. In fact, I would have added code. The number of lines of code pertaining to the business logic would be the same, but code to make the compiler happy would be added. That is class declaration, brackets, etc. It would even add one more file. 

Is it still worth it? Hell yeah! I've now stopped caring about two things: how to create a receipt and how to add items to it. Do you need to queue the new receipt somewhere? Don't know don't care. Do you need to add something between each items? Don't know don't care. Our brain is very limited, asking less of it each time is good. You get more lines of code, but less code to care about for each step: you brain is happy.

Tests get better too. I can now have a test that says "you should create a receipt and add items to it" (hint: mocks required!). Obviously, the factory will now have to be tested: is the receipt created properly? Same goes for the receipt itself. 

I added a layer of indirection, but the resulting algorithm is simpler, in each and every one of these classes. +1 for the readability because you know where to look if you want to change how a receipt is created. If you don't care, you don't even need to look for it. +1 for test documentation too, I now know they fully document these pieces of code.

This code is not tested!

The code above has not been tested. It looks like java, but who cares.

If I was to do it in TDD, I would probably have come up with better names, better collaborators. I have found that the best way to figure out the proper use of layers is through (mockist) TDD.

If a mock is hard to reason about, it is wrong. An integration test will not put enough pressure on my design to let me know about it. The slice which is being tested will grow, and grow, and grow... until the "in-between" becomes unreadable. What do we do? We split it! We add a layer of I-don't-care-ness, or at least that's what we think we are doing. We are using the code immediately, so it looks easy enough to understand. Here is the catch: how do you know you made the right separation? Is it viable in the long term?

This is where I leave it up to you

What do you think? Do you have to understand each parts before changing an algorithm? How does it add complexity?

I know the example is an over simplification, but in my experience, it scales properly.

Am I the only one to imagine software in my head and see all the moving parts interacting with each other? No I am not crazy, my mother had me tested - Sheldon Cooper.

UML, agilité, et rétroaction

Publié par David Beaumier le vendredi 4 octobre 2013 à 16:17

Depuis longtemps j’utilise UML comme formalisme pour exprimer certains concepts et favoriser une compréhension commune des personnes impliquées dans le développement d’une fonctionnalité.  Je suis partisan d’un UML pragmatique et des principes de modélisation Agiles.

Il m’arrive régulièrement d’utiliser un bloc-note ou un tableau blanc pour jeter les bases d’une conception. À ce moment-là, il est clair pour toutes les personnes impliquées qu’il s’agit d’une conception initiale, d’un draft quoi.  Personne ne s’attend à ce stade-ci que tous les cas de figure aient été envisagés dans les moindres détails. Il y a une entente tacite sur le niveau de précision du diagramme.

Cependant, il arrive parfois qu’il soit plus pratique d’utiliser un outil de modélisation pour réaliser un diagramme UML. Personnellement, j’apprécie beaucoup Enterprise Architect de Sparx Systems que j’utilise depuis nombre d’années. Avec le temps, j’en suis toutefois venu à  constater que l’entente tacite qui existe lorsque je modélise sur papier ou au tableau ne tient plus vraiment lorsque je produis un modèle informatisé. Les gens plutôt ont l’impression d’avoir devant eu un « plan » précis de ce qui doit être fait.

L’analogie qui me vient à l’esprit est celle d’un plan de bâtiment. Un plan fait avec un logiciel de DAO semble dégager une impression de précision et d’achèvement alors que, dans les faits, il peut être aussi préliminaire qu’un plan à main levée.  Dans l’exemple ci-dessous, le plan fait par DAO semble plus achevé. Pourtant, avec l’esquisse je pourrais déjà débuter les travaux puisque j’ai des mesures. Comme je suis un menuisier Agile, et qu’ainsi ma priorité est de satisfaire mon client en lui livrant rapidement des fonctionnalités, je suis en mesure de débuter un mur et l’assemblage des chevrons dès maintenant.

Plan fait avec un logiciel de DAOPlan fait à la main

J’ai déjà vécu un problème similaire avec les interfaces graphiques  à l’époque où j’utilisais des outils de conception visuelle se rapprochant énormément du produit final. Trop souvent les utilisateurs avaient l’impression que la fonctionnalité était quasiment terminée, tellement la maquette était réaliste. Bien souvent cela avait comme conséquence de limiter l’échange d’idées puisque les parties prenantes voyaient la solution comme déjà établie. Aujourd’hui, j’utilise l’outil Balsamiq Mockups pour produire des maquettes qui ne laissent aucune place à l’ambigüité quant à leur niveau de précision. Ci-dessous, un exemple de ce que permet de faire Balsamiq. C’est vraiment merveilleux car les utilisateurs sont conscient de la facilité à modifier un tel design et sont plus enclins à proposer des améliorations.

Maquette Balsamiq

C’est tout récemment que j’ai eu une révélation sur les diagrammes faits dans Enterprise Architect : pourquoi ne serait-il pas possible de leur donner un style à la Balsamiq? De cette façon il serait évident pour tout le monde qu’il ne s’agit pas de modèles complets et finaux, mais plutôt  d’une base pour débuter le travail. Quelle idée géniale, me dis-je! Avant de soumettre l’idée aux gens de Sparx Systems, j’ai tenté une petite preuve de concept en modifiant légèrement l’apparence des polices de caractères d’un diagramme. Après quelques minutes d’expérimentation, j’ai découvert les options « Hand Drawn » et « Whiteboard Mode » dans les propriétés du diagramme. Wow, c’est déjà là… Trop génial!

Ea Handdrawn Ea Whiteboard
Mode dessin à la main (Handdrawn) Mode tableau blanc (Whiteboard)

Voilà donc, d’un simple clic on peut obtenir une apparence « napkin » pour nos diagrammes. Tout comme avec Balsamiq, il devient alors possible de restaurer l’entente tacite sur la précision du diagramme tout en profitant des avantages d’un outil de conception visuelle. Non seulement cela repositionne les échanges au bon niveau,  mais j’ai en plus l’air d’être super bon en dessin.

Bonne conception!

À deux, c'est mieux !

Publié par Jean-Francois Gilbert le mercredi 19 juin 2013 à 00:00

Tout le monde connait la programmation en binôme, plus souvent appelée « pair programming ». Ses bienfaits ont été empiriquement démontrés, mais sa popularité demeure relativement marginale dans les faits. Il est évident que l'apport de nos collègues ne devrait pas être négligé. Il y a plus d'une façon de collaborer avec les membres de notre équipe et de bénéficier de leur expertise. Dans l'équipe où j'œuvre présentement, cette collaboration porte le nom de "test par un pair". Il s'agit d'une validation de la part d'un collègue, du travail qui a été exécuté et fait partie intégrante de la définition de "terminé" des éléments du carnet de Sprint. Cette pratique s'est avérée très efficace et ses bénéfices sont multiples. Elle a été empruntée à une autre équipe qui avait eu cette brillante idée et nous l'avons bonifiée par la suite.

Fonctionnement

Lors du découpage des tâches des éléments du carnet par l'équipe Scrum, nous créons presqu'obligatoirement une tâche nommée "Test par un pair". C'est la dernière tâche effectuée et elle détermine si l'élément du carnet est terminé ou non. Le collègue qui fait la vérification procède à peu près de la façon suivante :

  • Prend connaissance de l'élément du carnet et des critères d'acceptation.
  • Exécute les tests fonctionnels, aidés par les critères d'acceptation.
  • Consulte le gestionnaire de sources (Team Foundation Server dans notre cas) afin de connaître les changements qui ont été effectués au niveau du code (ajout/suppression de fichiers, modification du code existant, etc.)
  • Valide la qualité du code, c'est-à-dire si celui-ci est conforme à l'architecture et aux normes prescrites par l'équipe, s'il suit les bonnes pratiques de développement, etc.
  • Au besoin, il peut effectuer des corrections ou améliorer le code. Si ces modifications sont non-triviales, il avertira l'auteur du code original pour s'assurer que le changement est sans risque (de plus, les tests unitaires font office de filet de sûreté).
  • Valide la documentation au besoin.

Bénéfices

Outre le fait que l'élément du carnet est testé, cette façon de faire comporte d'autres avantages :

  • Le code que vous écrivez sera presqu'assurément lu par vos collègues. Cela ajoute une saine pression qui incite à produire du code qui respecte les règles de l'art. C'est juste normal de vouloir bien paraître devant nos collègues.
  • Cela force l'équipe à définir des critères d'acceptation et souvent des scénarios de tests avant de commencer à coder.
  • Cela favorise l'échange d'informations. Si vous n'avez pas eu la chance de travailler sur un élément du carnet, le fait de le réviser vous permet de vous mettre au parfum autant au niveau fonctionnel que technique.
  • C'est un excellent moyen d'apprendre et d'enseigner. Si vous trouvez que le code n'est pas optimal, vous avez une excellente opportunité d'enseigner un nouveau design pattern, un nouvel outil, une nouvelle façon de faire à votre collègue.

Les qualités requises

Le test par un pair demande aux membres de l'équipe 2 qualités importantes. D'abord, il faut être en mesure d'accepter de voir son travail scruté et potentiellement critiqué par nos collègues. Parfois, ça fait un peu mal à l'égo, mais c'est une occasion de s'améliorer comme développeur. D'un autre côté, cela demande nécessairement de la diplomatie et du tact lorsque l'on critique le code d'un compère. Les deux parties doivent en tout temps garder en tête que c'est un exercise qui se veut constructif.

Un risque potentiel

Que se passe-t-il si le code révisé ne respecte clairement pas les exigences de qualité ? Si fonctionnellement ça répond au besoin mais que le code est mal écrit ? Après tout, si l'élément du carnet est à toute fin pratique terminé et que vous renvoyez votre collègue à la table à dessin. cela ne mettra-t-il pas le sprint en danger ?

Dans notre équipe, nous avons trouvé une façon d'éviter ce problème. Nous le traitons en amont. Lorsqu'un élément du carnet comporte un tant soit peu de difficulté technique ou d'inconnu, nous ajoutons une tâche qui s'appelle "Atelier de conception". Elle doit être faite avant d'entreprendre les autres tâches de cet élément du carnet. Avant de commencer le développement, 2 ou 3 membres de l'équipe sont sollicités afin de discuter de ce qui doit être fait et de s'entendre sur l'architecture ou l'orientation à prendre pour répondre aux critères d'acceptation. Ainsi, lors de la revue par un pair, la personne faisant le révision ne devrait pas avoir de surprise quant  à la solution technique choisie. En effet, il a soit pris part aux discussions initiales ou bien il sait que plusieurs personnes ont réfléchi à l'approche utilisée. Si jamais la solution venait à changer en cours de route, il suffit d'expliquer ce qui nous a fait prendre une autre approche. Cela n'élimine pas complètement la possibilité de s'égarer en chemin mais le risque est grandement diminué.

Voilà donc notre façon de renforcer la qualité du code que nous écrivons tout en transmettant les connaissances d'un coéquipier à l'autre. C'est tout simple, mais très efficace !

Archive