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.

Le fléau des cadres agiles

Publié par Philippe Tremblay le mardi 2 septembre 2014 à 13:00

Récemment, Jurgen Appelo exprimait son désarroi face à l’infestation de cadres et méthodologies « agiles » dans le domaine des TI.  La détresse de son appel s’appuie sur l’argumentaire que les cadres tels Scrum, XP, SAFe, DAD et autres acronymes « fastfood »  de notre industrie représentent une prescription de pratiques et de principes qui ne font que diminuer les chances de succès des initiatives de changements.  Puisque chaque contexte requiert son adaptation.

M. Appelo a probablement raison sur l’effet que peuvent avoir les cadres et méthodologies (agiles ou non).  Bien trop souvent, les individus considèrent ces cadres comme des processus et croient que, lorsque bien exécutés, ces processus mèneront aux bénéfices espérés.  Ces attentes et cette perception précipitent invariablement l’organisation vers une approche méthodique plutôt que la démarche empirique nécessaire à transformer un domaine où l’adaptation est gage de succès!

Les cadres agiles ne sont pas ce qui empêche le succès des transitions tentées par les organisations.  Au contraire!  Si les individus ont peine à comprendre, mesurer et adapter les principes ainsi que les pratiques proposées par un cadre, imaginez s’ils devaient le faire à partir de l’ensemble des possibilités d’une transition agile!  Les chances de succès seraient encore plus minces.

« Feel free to ignore the frameworks, but please do consider the practices! » -- Jurgen Appelo

Je suis d’accord avec M. Appelo sur le fait que nous devons absolument favoriser la compréhension et l’adaptation des pratiques derrière les cadres agiles.  Par contre, ce serait une erreur d’ignorer les cadres existants par crainte que leurs utilisations soient déficientes!

Mots-Clés :

DDD vs Clean Architecture: un trop court résumé...

Publié par Félix-Antoine Bourbonnais le jeudi 7 août 2014 à 00:00

DDD vd Clean Architecture

La semaine dernière, Anis Berejeb a publié un billet sur l’architecture par cas d’utilisation. Suite à mon commentaire, il y a eu un petit débat concernant la différence entre DDD (Domain-Driven Design) et Clean Architecture. La question portait surtout sur la place des cas d’utilisation (Jocobson) en DDD.

Mon commentaire se résumait à dire que la conception par les cas d’utilisation n’est pas la seule façon d’atteindre les objectifs de séparation de concepts, inversion des dépendances, séparation de la logique de la persistance et de l’affichage, etc.

Disons-le immédiatement, il est difficile de tracer une ligne claire, car DDD et Clean Architecture sont, sur plusieurs aspects, orthogonaux. Mais risquons-nous... 

La portée  

DDD est de plus haut niveau que Clean Architecture qui est plus technique. Ainsi, une bonne partie de Clean Architecture se trouve à être un moyen architectural, parmi d’autres, pour appliquer la philosophie DDD. 

DDD vise la modélisation de problèmes complexes dont la logique d’affaires est importante. DDD va aller toucher la modélisation du domaine et la communication avec des experts du domaine (établissement d’un langage commun, contextes ...). C’est donc beaucoup plus qu’un modèle architectural (technique), c’est aussi une philosophie de modélisation, voire une façon de réfléchir.

L’accent 

Dans l’approche, la principale différence en est une d’accent (focus) lors de la conception. CleanArchitecture focalise sur les cas d’utilisation alors que DDD focalise sur la modélisation du domaine (les entités, valeurs, …).

Ainsi, les praticiens de Clean Architecture semblent plus enclins à encapsuler les cas d’utilisation dans un « Interactor ». DDD risque de préférer intégrer les cas d'utilisation au domaine (dans les entités, Domain Service ...) puisque c’est là qu’est portée toute l'attention.

Où placer la logique

En CleanArchitecture la question sera possiblement: « est-ce indépendant de l’application? » La tendance sera donc d'en mettre plus dans « l’Interactor » et de mettre dans le modèle seulement ce qui est indépendant de manière évidente.

DDD a aussi le même concept (dépendant ou indépendant), mais aura tendance à poser la question à l’inverse en considérant d’abord le domaine, ce qui va avoir comme conséquence de placer d’avantage de code dans le domaine (notamment dans les entités) et de fragmenter le cas d’utilisation. DDD a aussi le concept du Bounded Context qui entrera en jeu dans cette décision en limitant les dérives de l'approche.

À l’inverse, Clean Architecture aura tendance à créer des entités avec moins de logique et d’en placer plus dans les « Interactors ». Évidemment tout cela est du cas par cas, mais on parle de tendance naturelle ici...

À noter qu’avec DDD, la couche d’Application Service en est une d’orchestration, mais pour les considérations plus techniques (ex.: les transactions) ou encore pour orchestrer sur plusieurs contextes (Bounded Contexts). On pourrait la voir comme une couche de cas d’utilisation, mais avec moins de détails (rappelez-vous que la définition et le découpage ne sont pas exactement pareils dans les deux approches).

Les services du domaine (Domain Service) auront aussi un rôle d’orchestration, mais pour ce qui ne peut être intégré à une entité.

Exemple 

Prenons l’exemple du paiement d’une commande illustré par ce graphique qui montre une conception plus orientée cas d’utilisation (Clean Architecture). En DDD, il serait plus probable de voir la méthode « payOrder » initialement placée dans l’entité « Order ». Cela ne veut pas dire que ça ne changerait pas dans le temps par contre. 

Pour un exemple en DDD, consultez le code de l’application DDD Samplecette formation en ligne ou encore le livre de Vaughn Vernon.

Avantages et inconvénients

Voici quelques avantages, inconvénients et risques naturels associés au «focus» différent. 

Quelques avantages de la modélisation par cas d’utilisation (Clean Architecture):

  • Cas d'utilisation identifiables facilement et clairement énoncés;
  • Facile de déployer les entités indépendamment et de les réutiliser dans plusieurs applications de l’entreprise (note: DDD a les concepts de Bounded Context et Shared Kernel pour balancer);
  • Plus facile de comprendre le cas d’utilisation rapidement.

Quelques risques:

  • Domaine moins structuré et plus « vide » ;
  • Réutilisation de la logique pour plusieurs cas d’utilisation plus complexe (ou dangereuse);
  • Danger de tomber dans un domaine anémique ou de faire de la programmation procédurale involontairement;
  • Danger de concevoir par les données les entités plutôt que d’avoir un domaine consistant représentant bien la logique du domaine d’affaires;
  • Moins d'outils concernant la manière d’arriver à modéliser son domaine, d’avoir un langage commun, etc;
  • Risque de complexité plus élevé dans « l’Interactor », plutôt que d’abstraire cette complexité dans le design du domaine.

 Et inversement pour DDD… Évidemment, comme toujours, tout est une question de compromis en fonction de son contexte. C’est ça l’art du développement logiciel !

Résumé et recommandations: complémentarité!

En résumé, tout est une question d’accent (focus). DDD focalise sur le domaine, CleanArchitecture sur les cas d’utilisation. Non pas que DDD ne considère pas les cas d’utilisation, mais ce n’est pas sur ça qu’est placé l'accent!

Si vous êtes dans un domaine de TI avec beaucoup de règles d’affaires, DDD vous sera probablement très utile pour modéliser le domaine, le découper, établir votre langage commun, etc. Vous pouvez certainement y combiner les techniques de Clean Architecture pour ce qui est de l’inversion des dépendances, de l’interfaçage, etc. 

À mon avis, l’important est de se souvenir qu’ils peuvent êtres complémentaires et que ce sont des techniques, pratiques et concepts à avoir dans sa trousse à outils. 

Donc comme le dit Bob Martin lui-même: « It's not really important which of the two [Use Case, Domain] drives […] It is the designers job, in each case, to prevent that muddle. » [2]. 

Après tout, les deux poursuivent le même but ultime! Tous s’entendent sur la nécessité de séparer la logique d’affaires des préoccupations techniques, l’inversion des dépendances, de rendre visible le domaine d’affaires plutôt que les frameworks et outils, etc.

Références 

[1] Comparaison selon Bob Martin:
https://groups.google.com/d/msg/clean-code-discussion/oEFEWq8qdFQ/i0gsi3eU5VoJ

[2] Lequel choisir selon Bob Martin:
https://groups.google.com/d/msg/clean-code-discussion/xm8QtaYwl3k/LWgFBs3erd0J

[3] Réponse connexe concernant l’application layer en DDD:
https://groups.yahoo.com/neo/groups/domaindrivendesign/conversations/messages/24325

Méthodes et classes statiques (ou partagées) : Une règle du pouce

Publié par Olivier Dugas le lundi 7 juillet 2014 à 09:00

Avez-vous déjà lu ou même écrit des méthodes statiques dans un projet pour votre entreprise? Certains l'ont sûrement fait, puisqu'il nous arrive fréquemment d'en voir lorsque nous plongeons dans le code d'entreprises que nous visitons. La question du jour est : Croyez-vous que c'est une bonne pratique?

Avant de répondre à cette question, précisons que les méthodes statiques et partagées sont la même chose. "Static" est le terme employé par Java et C#.Net, alors que VB.Net utilise plutôt le terme "Shared". Par conséquent, les deux doivent être considérés comme interchangeable dans cette publication.

Voici ce que MSDN a à dire à propos des méthodes et classes partagées :

Utilisez une classe statique comme une unité d'organisation pour les méthodes qui ne sont associées à aucun objet particulier. Par ailleurs, une classe statique peut simplifier et accélérer votre implémentation, car vous n'êtes pas obligé de créer un objet pour appeler ses méthodes.

 

Voici un résumé des avantages que les gens nous disent régulièrement à propos des méthodes partagées :

  • Elles sont de bonnes assistantes dans des classes utilitaires
  • Elles sont plus concises à utiliser que le code qui crée une instance sans utilité par la suite dans la classe appelante
  • Elles sont efficaces pour de petits projets sans besoins architecturaux et sans maintenance future envisagée.
  • Il n'y a pas de raison de créer d'instance lorsqu'une classe n'a besoin d'aucun état.

Il y a des avantages évidents, mais de façon générale, nous ne recommandons pas son utilisation car les désavantages représentens une dette ne valant pas les bénéfices.

Polymorphisme

Supposons que nous avons une méthode partagée dans un utilitaire quelconque qui vit nonchalamment dans son coin. Soudainement, nous devons altérer légèrement la fonctionnalité. La plus grande partie de la fonctionnalité est la même, mais nous devons néanmoins changer quelques petites parti La technique normalement employée dans une situation comme celle-ci est de faire de l’héritage, mais il n’est pas possible de procéder ainsi avec les méthodes statiques.  Il n’y a pas de manière élégante de procéder ici, ce qui nous contraindrait d’ajouter une branche conditionnelle (un if). Cela provoquera l’augmentation de la complexité du code et, par extension, l’accumulation de la dette technique.

Le choix des noms

Oh, regardez, une méthode partagée très utile. Oh, en voilà une autre qui joue un rôle semblable, à peu de choses près. Rassemblons-les en une seule classe statique. Comment nommer cette classe? Je ne sais pas trop quel nom lui donner, mais les deux méthodes font des choses en lien à la validation de chaîne de requêtes HTML, alors je vais l'appeler "ValidationHelper"!

Les classes statiques tendent à être difficiles à nommer et, pour cette raison, elles finissent inévitablement par recevoir un nom avec un préfixe générique (tel que Validation) joint à une terminaison horrible comme "Helper", "Manager" ou "Utils". Sentez-vous la fumée? Voici une vision de l'avenir : "Tiens, j'ai créé une nouvelle méthode statique qui valide l'état d'une valeur envoyée sur le réseau. Je me demande où je pourrais bien déposer cette méthode..."

Parce que les éléments partagés sont difficiles à nommer, ceux-ci risquent fort de recevoir un nom générique. De nouvelles fonctionnalités s'y grefferont avec le temps. Ultimement, ces classes deviendront des amalgames de méthodes non reliées, et les paramètres optionnels (ou des méthodes au nom identique mais des paramètres différents) vont commencer à ramper dans les méthodes statiques qui s'y logent. Rappelons au passage qu’un mauvais nom mène souvent à une violation du SRP (Single Responsibility Principle).

Tests unitaires

Il peut être complexe d'écrire et de maintenir des tests unitaires de classes qui utilise des éléments statiques. Bien entendu, nous excluons les classes statiques comme Math, puisque ce sont des outils standards des langages. Rien ne sert de s'isoler des outils statiques d'un langage de programmation. Également, il y a parfois des cas où l’utilisation de classes statiques pour faire de la composition peut être intéressant. Et dans le cas de la composition, nous atteignons souvent mieux l’objectif de “documentation” par nos tests lorsqu’ils ne sont pas parfaitement unitaires. Par contre, nous ne devrions pas dépendre de classes statiques que nous (ou quelqu'un d'autre) pourrait possiblement changer un jour.

Il n'y a aucune manière élégante d'isoler une classe des méthodes statiques dont celle-ci fait usage. Puisque nous ne pouvons faire d'interfaces implémentées par des classes statiques, vous ne pouvez pas les remplacer par des mocks. Également, imaginez que cette classe statique crée et utilise trois classes et appelle l'interface graphique ou la base de donnée. Un simple test unitaire ne pourra être rapide, ne sera pas unitaire et risque d'échouer aléatoirement en fonction de l'orientation du vent. Par la bande, nous vous invitons à lire l'article du blog de Martin Fowler au sujet des tests non-déterministes.

États globaux

Si vous devez absolument avoir des classes partagées dans vos projets (évitez cette situation autant que possible), assurez vous au moins de ne pas maintenir d'attributs partagés à l'intérieur de cette classe. Cela signifie que vous avez des variables globales, ce qui est un véritable cauchemar à déboguer. Et ne vous en faites pas, vous aurez à déboguer, puisque vous n'avez pas de filet de sécurité fait de tests unitaires mais aussi parce que la complexité augmente exponentiellement avec les états globaux. De surcroît, le couplage induit par cette situation tue votre architecture telle un coup de tronçonneuse dans la colonne vertébrale de votre projet. Et avez-vous déjà souhaités porter votre application dans un nuage (Cloud Computing)? Où même d'espérer une meilleure performance de votre système? Il serait bien plus simple de dépôser cette tâche dans la liste des choses-impossibles-à-faire, car les états globaux empêche carrément la parallélisation. 

Une règle du pouce

Maintenant que nous avons vu comment les éléments statiques sont souvent source de bien des maux, comment faire pour s'en débarasser dans cet énorme projet d'entreprise? La classe statique est déjà utilisée par plus de 9000 classes...

Une possibilité est d'utiliser un singleton, mais sous une variante unitairement testable.Nous savons que les singletons présentent les mêmes problèmes que les méthodes statiques. Malgré cela, s'il est impossible de faire la transition vers une classe régulière en une seule bouchée, le singleton testable est une option correcte. 

Une alternative que nous jugeons plus intéressante serait de créer une classe non-statique Adapter qui se charge de transférer les appels aux méthodes statiques. Progressivement, vous pouvez modifier les utilisateurs des méthodes statiques pour qu'ils utilisent plutôt cet Adapter. Il sera ainsi possible de créer un mock de ce dernier dans les tests pour les rendre unitaires. Ultimement, lorsque seule la classe Wrapper fera les appels vers les méthodes statiques, vous pourrez supprimer les méthodes statiques et les réimplémenter dans la classe Adapter.

La tâche peut être lourde. Pour éviter d'échouer ou de se sentir décourager par le travail à faire, il est préférable d'y aller par petites itérations. Ne faites pas la modification en un élan pour ensuite essayer de refaire passer tous les tests qui échoueront. D'autres ont fait cette erreur avant vous.

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.

Le changement organisationnel [Partie 2 / 2]

Publié par Olivier Dugas le mardi 13 mai 2014 à 15:09

À la partie 1 de cet article, nous avons avancé qu'un changement organisationnel n'était pas nécessairement durable. La grande question est donc de savoir comment arriver à rendre un changement organisationnel durable.

L'une des pistes de réponses se trouve dans le type de motivation choisi pour mener le changement. En effet, il faut ajouter à la motivation extrinsèque du personnel affecté une motivation intrinsèque, c.-à-d. une motivation liée aux valeurs véhiculées par les personnes. La motivation extrinsèque mise sur les aspects techniques et structurels d'un changement, ce qui est généralement véhiculé par les gestionnaires, les experts de ce changement, les agents de changement et les champions. La motivation intrinsèque, elle, mise plutôt sur les croyances, l'intégration du changement dans les valeurs et dans les comportements, ainsi que sur le niveau d'engagement des personnes affectées par le changement. Bien que les motivations extrinsèques soient importantes, un changement a bien peu de chance d'être durable sans les motivations intrinsèques.

Selon la théorie d'autodétermination, il faut amener à faire en sorte que le choix de l'agilité soit fait par les personnes affectées sans influence ni interférence externe. Curieusement, cette théorie se rapproche formidablement d'une conférence de Dan Pink. Selon ces théories et à notre avis, les différents éléments à viser afin d'augmenter la motivation intrinsèque sont l'autonomie, la maîtrise et le but.

  • Autonomie : Le désir de se diriger soit même.
  • La maîtrise (ou la quête de la compétence) : Le sentiment d'urgence à devenir meilleur à quelque chose.
  • Le but (ou l'appartenance psychologique) : Le pourquoi de l'entreprise, le pourquoi du changement, la vision de l'agilité. Ce qui donne envie à tout employé de participer à quelque chose de grand, un projet commun.
  • La satisfaction personnelle : Le niveau de bien-être dans l'entreprise en lien avec les objectifs de vie d'un employé.

Petite parenthèse, certaines compagnies, comme Amazon, Zappos et Netflix, paient leurs employés s'ils démissionnent afin de conserver ceux qui sont réellement motivés par la mission de l'entreprise.

Il faudrait, en tant que consultant, trouver un ou des champions à travers les équipes. Il faudrait orienter les manières de voir et de discuter de l'agilité en visant les valeurs mentionnées ci-haut.

Il faudrait également trouver une manière de capitaliser sur la victoire afin de la soutenir et de souligner les bons coups de chacun. Les champions devraient aussi être capable de faire ce travail lorsque le consultant n'est plus là. Il faudrait utiliser les victoires (petites ou grandes) pour répandre la motivation à d'autres équipes, puis à d'autres départements de l'entreprise. Batement et David (2002) l'ont d'ailleurs suggéré dans leur modèle des niveaux de durabilité d'améliorations.

Improvementsteps

Archive