Ce qu’on ne m’avait pas dit sur le développement

Non, ceci n’est pas un howto.

Ce n’est pas non plus un how-not-to.

C’est du vécu. Ici je vous parle de mon ressenti, de l’arrivée dans mon premier projet jusqu’à sa fin. Je vous parle de tout ce qui a pu me marquer, me donner envie d’adopter l’état d’esprit d’un software craftsman, et surtout de ce que chaque étape m’a apporté. Pourquoi ? Parce qu’il n’y a pas qu’une manière, ou contexte pour y parvenir, mais qu’en revanche n’importe quelle expérience peut inspirer la vôtre (et ça aussi c’est du vécu).

Premier contact avec un membre de la tribu CRAFT

Il se présente, il va faire partie de notre équipe de 3 devs. Il me raconte ce qui l’intéresse, qu’il est à Octo depuis près de 2 ans, ce que défend sa tribu, les méthodes telles que…

– Attend, TDD c’est quoi ? Honnêtement, j’en ai jamais entendu parlé ou j’ai jamais fait attention – lui demandé-je sans honte (et pourtant j’aurais dû !).
– Alors, le TDD c’est une méthode qui guide le développement de ton application par l’écriture de tests, me dit-il. Ça protège ton code au fil de son développement et ça le documente aussi !
– Ah ça m’intéresse tiens.
– Ah ben c’est bien parce qu’on ne fera pas le projet autrement ! On se réserve l’aprem et on fera un randori basique, un puissance 4, tu verras c’est plutôt sympa.
– Cool !

L’aprem, on sort les crayons …

Il m’explique :

– Le but, c’est d’écrire un puissance 4. On va y aller par étape. D’abord, on a besoin d’une grille.
– Un tableau de valeurs ?
– Oui, pourquoi pas, mais ne t’embête pas de suite avec ces détails là. Quels sont les tests que tu ferais sur cette grille ? Que ferais-tu pour vérifier son bon fonctionnement ? Vas-y lance des idées, on verra à la fin ce qui se recoupe, ce qui tire d’autres choses, bref, ça n’a pas besoin d’être parfait.

Après avoir proposé de vérifier que la grille fait la bonne taille, que l’on ne met pas de pion au même endroit qu’un autre, ou que le pion envoyé n’est pas null, il me réoriente : La grille est-elle vide au début du jeu ? L’ajout d’un jeton se fait-il bien au dessus d’un autre ? L’ajout est-il bien impossible au delà de la taille maximum de la grille ? Ce sont les règles. Le message passe (pour un court instant) : oublie l’implémentation, quelles sont les fonctionnalités attendues par l’utilisateur de cette grille ?

Au fur et à mesure, les choses à tester s’éliminent, et certaines en font émerger d’autres. C’est fun ! Mais on a pas encore écrit une ligne.

– Alors, ce qu’on va faire, c’est qu’on va se passer le clavier à chaque phase : J’écris un test, tu écris le code qui le fait passer, on voit ce qu’on peut améliorer dans le code, tu écris un test, j’écris le code qui le fait passer, et caetera. Ça te permettra de voir comment passer chaque phase et de la pratiquer ensuite. On parle souvent de ce cycle en nommant ses étapes : Red, Green, Refactor.

“Red”

assertThat(cellule).isEqualTo(0);

– Écrire le test devrait être très simple, on suit le format Gherkin : Given un contexte dans lequel évolue la classe à tester, When j’exécute une méthode de cette classe, Then on s’attend à un comportement, un résultat. Regarde, on va même te faire un raccourci pour t’écrire le squelette de ta méthode de test facilement.

Il écrit au moins la signature de la méthode pour que ça compile, il écrit le test, et il lance le test. Attend, quoi ?

– On est pas à l’abri d’une erreur : il doit d’abord échouer, et pour les bonnes raisons. Tu peux très bien écrire un test qui n’échoue pas alors que ta méthode n’est pas encore implémentée. À quoi servirait ce test alors ? Tu dois vérifier que ton test guidera ton implémentation, et te protégera bien quand tu retoucheras ton code.

“Green”

return 0;

– Ok, quel est le code le plus simple que tu puisses écrire pour que le test passe au vert ?… Nan nan, ne t’embête pas avec tout ça, on attend juste une valeur pour l’instant. Retourne-la, ne te lance pas encore dans les conditions, on n’a pas encore les tests pour ça. On ne fait pas de code défensif, c’est du stock (traduction: avec des si … vous connaissez la suite).

Test au vert, implémentation la plus bête, code défensif, stock, je sens que j’ai du chemin.
J’implémente donc le code qui fera passer le test. J’ai rarement écrit une ligne aussi bête… Mais elle faisait passer le test.

“Refactor”

return CELLULE_VIDE;

– À mon tour, dit-il en reprenant le clavier. Bon, y’a pas grand chose à faire, c’est notre premier test et notre première implémentation. Hmm aller, 0 c’est un nombre magique, CELLULE_VIDE ça a du sens …

Ah oui, j’aurais pu y penser tiens.

Le cycle

Plus les cycles red-green-refactor passaient, plus le code, et surtout les tests, gonflaient. Plus il y avait de remarques, plus la phase de refactor prenait de l’ampleur. Chaque implémentation d’un cycle empiétait sur celle du cycle précédent. Et pourtant les tests finissaient toujours par être verts, et le code était toujours aussi lisible, évident. Ce n’était qu’un exercice, une grille de puissance 4 bête et méchante, mais quel soin pour le réaliser ! Il suffisait de suivre les étapes, et d’écouter les conseils du craftsman.

Puis d’autres notions apparaissaient, mocks et stubs, et il me racontait que la confusion était très courante pour beaucoup. Ok, je ne confondrai pas. Et que certaines choses, telles que les méthodes statiques étaient une plaie pour la maintenabilité de ton programme : tu ne peux pas les stubber. Ça a l’air important, je ferai gaffe.

Ça avait l’air facile. C’était du bon sens, pour t’y retrouver :

– ne met pas trop de choses dans tes méthodes
– écris du code explicite
– ne teste qu’une chose à la fois

Découverte

Les premiers jours sur le projet ont démarré en binôme. Sur le même principe, on échangeait le clavier à chaque étape du cycle. C’était presque un jeu où il fallait trouver le code le plus simple, le plus lisible, qui permette d’obtenir la fonctionnalité. Le craftsman avait la patience et l’humilité pour recevoir mes remarques, et je prenais tout le recul que je pouvais pour pouvoir faire de même. Ça m’a permis d’apprendre une chose simple : il n’y a pas de mon code ou ton code. L’amélioration continue se porte mieux quand elle n’est pas encombrée de l’ego de chacun.

Le meilleur signe d’évolution pour moi était les revues de code. Ce qui était hors de nos pratiques devenait de plus en plus évident, et les retours sur les stories que j’implémentais de moins en moins volumineux. Mais j’étais encore trop lent. Je le sentais, ce n’était pas encore naturel. Je passais encore pas mal par des phases de TDD exploratoire et parfois, par fatigue, je collais mes tests sur l’implémentation. Pourquoi était-ce plus fastidieux que ce que l’on m’avait vendu ? Avec le recul, je me dis que de nombreux développeurs ont dû passer par cette phase avant de comprendre d’où venait cette fatigue. C’est presque un passage nécessaire, où l’on réalise qu’on n’arrive pas à tirer le bénéfice des méthodes avec la même efficacité que les autres. C’est à mon avis le passage où nombre d’entre eux abandonne, alors qu’ils sont si proches !


Passer le cap

Prise_escalade_falaise_rocher_main_arquée

TDD : T’as les doigts sur la prise. Tu tires. T’as mal, mais faut pas lâcher !

L’équipe s’agrandit !

Près de deux mois après le démarrage du projet un autre membre de la tribu CRAFT rejoint l’équipe.

– Une demi-heure pour mettre en place l’environnement de développement, pas mal, nous dit-il avec le sourire.

C’était plutôt rassurant.

Quelques jours ont suffi pour qu’un membre de plus fasse évoluer nos standards. Le code de l’application évoluait encore différemment, et en peu de temps il avait rattrapé ce que nous connaissions de l’application.

Un jour ce dernier pose un livre sur la table : The Software Craftsman – Professionalism, Pragmatism, Pride (Par Sandro Mancuso). Il nous explique qu’il vient de le finir et que ça lui a beaucoup plu.

– Franchement c’est facile à lire. Tu sens que le mec est passé par ce qu’on a connu. C’est pas mal d’anecdotes, et beaucoup de bon sens. Je pense qu’on devrait tous l’avoir lu. Ça t’intéresse ? dit-il en me regardant.
– Ah ben pourquoi pas oui. Avec le métro je devrais trouver le temps ! (pour ceux qui ont lu ce livre, cet état d’esprit me rappelle la partie ‘Creating time’, que d’excuses…)

Attitude professionnelle

Je n’avais jamais saisi ce que ça pouvait signifier dans le domaine développement.

Flashback. Je suis sorti d’une école où on m’a appris à me débrouiller. Honnêtement c’était efficace. On m’a appris à plonger dans n’importe quelle technologie, et ça m’a rendu productif (ouch) dès la première année de stage. Enfin productif, pour des boîtes où les développeurs avaient autant de bonnes pratiques dans leurs bagages que moi.

Le résultat, pour moi comme pour nombre d’étudiants de cette école, c’est que je me suis mis à « bricoler » des programmes. On passe du temps à penser au code, à faire des diagrammes de ce que sera un travail dont on ne peut raisonnablement pas voir tous les défauts avant d’être dedans. Puis on « pisse » une centaine de lignes d’un coup, on lance l’application en vérifiant que « ça marche », sinon on debug à coups de printf, puis quand c’est fini (ou même avant que ce soit stable) on tente déjà de l’optimiser.

C’est chaotique. À cette époque je pensais qu’un algo qui résolvait n’importe quel problème en complexité temporelle O(log n) était le meilleur code que je pouvais produire, que c’était ça le développement.

Ce que m’a fait comprendre ce livre ? J’étais irresponsable. Je me suis mis à penser à tout ce que j’avais fait, et je me suis senti bien gêné. Comment se fait-il qu’un code produit pour une entreprise n’ai pas été développé pour qu’elle puisse s’en resservir ? Pourquoi n’importe qui ne pourrait-il pas le comprendre, le faire évoluer et s’assurer de sa stabilité ?

Imaginez, une fois que vous quittez votre entreprise elle réalise que, dans une des applications que vous avez développé pour elle, il lui manque une fonctionnalité, ou que l’une d’entre elle est instable. Si votre code n’est pas maintenable que fait-elle ? Elle paie très cher pour restabiliser ce qui a déjà été (mal) fait ? Elle jette votre travail ? Et quand vous revenez 1 an après sur votre travail, que se passe-t-il ? Combien de temps passerez vous à revenir sur ce que vous avez déjà fait ? Combien de temps vous faut-il pour finalement avancer et prendre votre pied ? Honnêtement, pourquoi s’infliger tout ça ?

Aujourd’hui une variable que vous avez appelé « date » pour être tapée plus vite (et l’auto-complétion ?) peut être renommée à travers tout votre fichier en « lastEventDate » grâce à un raccourci clavier. Votre IDE peut détecter le code dupliqué. Avant un commit il vous prévient des erreurs qu’il a détecté. Besoin d’extraire du code ? Un raccourci. La technologie ne nous donne plus d’excuses, nous avons le temps et les moyens de bien faire.

Je pense qu’il n’y avait pas mieux que ce bouquin pour m’embarquer plus loin dans le mouvement. Une dose le matin et aussi le soir, pour que la fin de la journée ne soit pas la fin des réflexions. C’est plus que ma façon de développer que j’ai fini par remettre en cause.

Sortir du passage

Pourquoi était-ce plus fastidieux que ce que l’on m’a vendu ? Tout ça a déclenché pas mal de questions. Ce projet est un cas particulier ? J’aurais mal compris l’une des méthodes ? D’autres méthodes ? Pourquoi y arriveraient-ils alors ? L’expérience ? Suis-je trop bête ?

Le problème était, comme souvent, entre la chaise et le clavier. Et alors que je me torturais à savoir où ça pouvait pêcher, où les pièges que l’on trouve dans les livres pouvaient se terrer, je finis par appeler à l’aide notre nouvel équipier : « J’arrive pas à m’en sortir. Je suis certain que c’est idiot. La fonctionnalité ne me parait pas plus compliquée qu’une autre, mais j’arrive pas à voir comment écrire les tests sans penser à l’implémentation. »

– Ok, si je peux me permettre ? – venant de quelqu’un d’honnête qui ne demande jamais s’il peut l’être, ça impliquait d’être attentif à l’avis qui allait suivre – Je pense que tu te prends trop la tête. Tu te projettes trop sur des choses que tu ne peux pas encore savoir. Je vois que tu veux bien faire, et ça parait contre intuitif, mais ton meilleur code tu ne le feras pas en imaginant toutes les possibilités en avance, en te demandant « et si je fais ça, et ça, mais s’il y a ça, non peut être autrement ». Il y a trop de possibles. Je ne dis pas que tu ne dois pas te demander quelles dépendances ou quelles technos tu pourrais employer, mais KISS, Keep It Simple, Stupid.

Et là vient le déclic, ce qu’il me raconte ensuite je le comprends comme ça : « Écris un test simple, certainement pas encore complet, mais pose une base, une certitude. Tente d’écrire le code qui y répond, fais évoluer le test au besoin, au fil du code. Pour l’instant oublie des perfs, ne t’occupe pas du découpage, de la qualité, parce que tu n’arrives même pas à implémenter la fonctionnalité. De certitudes en certitudes, de lignes évidentes à d’autres tu auras écrit un code qui marche. Certes il n’est pas parfait, mais tu as atteint ton premier objectif: Tu passes le test. Ensuite seulement, sur cette base de certitudes, de code qui marche, tu peux commencer à refactorer, à faire du joli que tout le monde comprendra. Un seul combat à la fois. »

Aussitôt après, je vire le code que j’avais commencé, et je joue le jeu. Non sans difficultés, mais comme disait Morpheus « Libère ton esprit ». J’avance aveuglement en me disant : « ça je m’en moque, ça aussi, et ça ah tiens j’en ai besoin hop je mock, ok le code est moche, mais ça marche. ». J’ai ma fonctionnalité : « Ok refactor : trop de mocks, le test se lit mal, j’ai sûrement un problème de responsabilité dans cette classe. » Je crée un service avec la même méthode, en y mettant ce que ma classe précédente n’avait pas besoin de connaître. « Wow. C’est ça. L’archi vient d’émerger. Le code est fonctionnel, lisible, il respecte nos standards, et je n’ai pas passé mon temps à me projeter. »

Je ne dis pas que je n’ai jamais recommencé, les mauvaises habitudes ont la peau dure. Mais je connais mon principal souci. Et à partir du moment où je m’en rends compte, je passe le cap. Puis enfin, je peux prendre le temps de bien faire les choses, de prendre du plaisir, d’être fier de ce que je fais. Mieux, je peux le partager, tout le monde peut le lire.


Transmettre

Un grand chat à côté d'un chaton de même couleur

“T’inquiète pas, moi aussi j’ai connu ça.”

Agile et Software craftsmanship

Il est arrivé plusieurs fois que le Product Owner (quelqu’un du métier, pas versé dans le développement) se prenne au jeu et lise notre code par dessus notre épaule :

– Should redirect to english home location when no uri provided. Given first time on site true, when controller index, then redirect location is equal to « accueil ». Ce serait pas « en_home » plutôt ?

Oui il l’a très bien compris. Si ça ce n’est pas un signe… Bien sûr l’objectif n’est pas de rendre son code lisible même pour le Product Owner. Mais on réalise dans cet exemple qu’avec la communication dans le même bureau, par mail, par téléphone entre tous les membres de l’équipe, que chacun entend et se soucie des problématiques des autres.

Il est arrivé que notre Delivery Manager côté client et notre Product Owner défendent nos méthodes à leur hiérarchie avec conviction parce que leur effets sont immédiats et évidents pour eux. Quand un refactoring a lieu, ils comprennent que cette action aura un impact positif sur notre vélocité à moyen et long terme. C’est leur premier projet agile. Aujourd’hui, quand une fonctionnalité est demandée ils comprennent que des tests seront écrits. Ils n’ont pas peur de demander une fonctionnalité, peur qu’une modification ait des effets de bords, et ils savent que les tests sont cette garantie. Ils comprennent aussi que parce que nous faisons du TDD, tout démarre par les tests, et donc qu’une couverture de test à 97% n’est pas de la surqualité, car elle n’est qu’une conséquence et non un objectif.

Notre techlead

Je n’ai jamais été autant challengé sur mes choix techniques. Avoir quelqu’un qui se soucie autant de la qualité ou des standards d’utilisation d’une technologie c’est avoir quelqu’un qui permet à l’équipe d’utiliser cette technologie telle qu’elle a été pensée, de ne pas sortir du cadre pour lequel elle a été pensée.

Ça peut paraître évident, mais cette approche est indispensable pour éviter les effets de bords d’une mauvaise utilisation. Et ça a été notre cas à plusieurs reprises :

– Je ne suis pas à l’aise avec ce qu’on a fait, il nous répétait. Je ne le sens pas. Je ne regarde pas maintenant mais faudra qu’on s’y mette.

Et le soir même ou à la moindre occasion, il se relançait dans l’étude de ce qu’il sentait mal, parcourant la doc et étudiant le code des bibliothèques. Et pour chaque cas hors des clous un refactor, de taille variable, et pour autant avec la même aisance, protégé par l’ensemble des tests. Le résultat était au choix et souvent tout en même temps : moins de bugs, de code smell ou de prises de têtes lorsqu’on revenait sur ce morceau de l’application.

Je me rappelle de l’utilisation d’une des annotations que j’avais mal comprise. Plus j’avançais dans la story, plus elle me paraissait difficile à réaliser. Puis je me suis mis à chercher pourquoi dans telle ou telle condition l’application ne réagissait pas comme je l’imaginais, pourquoi tel cas n’avait pas été prévu ou telle fonctionnalité évidente semblait inexistante. Tout ce que j’ai pu trouver était des questions StackOverflow sans réponse et des forums au topic mort. La source (PEBCAK encore) était cette mauvaise utilisation de la technologie. Quand il m’a remis sur la route, mes problèmes redevenaient des sujets communs auquels la communauté avait largement répondu. C’est un code smell à mon sens. La technologie que vous employez ne répond pas au besoin qu’on vous à vendu ? L’utilisez vous correctement ?

Chacune de ses actions sont attachées à la qualité de ce que nous créons, pour que du code à l’intégration de l’application, en passant par l’architecture logicielle, il n’y ait pas de fausse note. Pour que quiconque reprenant cette application ne parcoure qu’un ensemble prévisible, maîtrisé, pour lequel il trouvera toute l’aide possible.

L’épreuve

L’un des soucis de la version précédente du projet était la qualité du code. Son évolution était tellement douloureuse que notre Product Owner hésitait à leur demander des fonctionnalités supplémentaires de peur que le projet s’effondre dans les régressions.

Pour ne plus rencontrer ce problème, au milieu du projet, le client a voulu intégrer un développeur externe. L’un des objectifs était certes d’augmenter la vélocité, mais surtout de vérifier que quelqu’un qui ne vient pas de la boite puisse reprendre facilement le projet en cours.

Il connaissait Scrum, mais ne pratiquait pas le TDD. En une itération, grâce au binomage, aux revues de codes et aux différentes aides dont nous bénéficions des uns et des autres pendant le développement, il avait adopté nos pratiques et connaissait les entrailles du projet en grosse maille. Plus le temps passait et plus il se passionnait des méthodes qu’on employait. Et même observation, les retours de revue de code perdaient en volume. Si bien que lors d’une rétrospective, il nous a sorti un très beau keep :

– Il (l’un des craftsmen de l’équipe) ne m’a fait aucun retour de revue de code sur une des stories que j’ai développé !

C’était comme une fenêtre sur un passé proche. Je me revoyais passer toutes ces étapes : Curiosité, enthousiasme, doute, et une fois le cap passé, l’état d’esprit, les pratiques, faisaient partie intégrante de sa vision du développement.

Je passe les discussions autour des bières sur ce qu’on passait notre temps à faire en journée, sur les mauvaises expériences qu’on a connu avant d’atterrir sur ce projet. Bref, on ne s’en lassait plus, et on en redemandait.

Toute l’équipe a finit par aller à un meetup Software Crafts(wo)manship Paris organisé chez OCTO Technology. Il y avait tant de choses intéressantes, de notions inconnues, et pourtant, aussi récente que soit notre initiation pour notre nouveau membre comme pour moi, on était déjà capable de comprendre et de partager avec une communauté dont c’était tout l’objectif.

Je me suis aussi retrouvé de l’autre coté de l’enseignement au début de cette période ! J’en étais déjà convaincu : transmettre c’est être capable de transformer son savoir, son expérience, et l’adapter à la personne qui va le recevoir. Ça implique de comprendre ce que l’on a assimilé, puis d’en trouver l’essence. C’est la conceptualisation. Une fois que ces concepts se dégagent, il faut comprendre la situation de celui avec qui on les échange. Puis enfin on les remet dans sa situation, à sa portée. C’est à mon sens le meilleur moyen de renforcer un savoir, de le remettre en question, ou d’en découvrir de nouvelles facettes. En rendant accessible cette connaissance, j’ai encore une fois changé. Notre échange nous a changé.

Un thème pour les rassembler tous, et dans la lumière…

Il y a à mon avis un point commun entre l’agile, du code responsable, et l’attitude des craftsmen. La communication doit être fluide, sans accroche, de la technique à l’humain. Pour que l’agilité fonctionne, que les retours soient efficaces et rapides, l’empathie et la curiosité du craftsman, tout comme l’excellence technique qu’il manifestera sont les clés. Pour atteindre cette excellence, le code doit être impeccable, expressif, prévisible, accessible à tous les développeurs, facilement modifiable. Pour qu’il ne cesse de l’être, pour que l’on prenne plaisir à travailler ensemble et à retrouver ce genre de plaisir en arrivant sur un projet même externe, il faut transmettre au maximum nos pratiques, notre état d’esprit. Il y a un cercle vertueux, et nous sommes responsables de son inertie.

Quel est le résultat ?

Alors voilà, ce projet sur lequel je ne contribuerai bientôt plus était pour moi la meilleure expérience de développement que j’ai vécu. J’ai beaucoup gagné personnellement, et je pense que la clé du succès sur la transmission de tout ce savoir faire, se trouve dans les quelques moments clés décrits ici. Des liens forts sont nés, et c’est sans aucun doute le signe que le courant a été bien établi, que les pratiques n’ont pas été bêtement appliquées, et que la communication qui en a résulté en est aussi la cause.

Récemment je me suis mis à faire un pet project en TDD. Ça me parait désormais aberrant de me passer de tout ce que j’ai pu apprendre. Après avoir lu The Software Craftsman, je me retrouve au milieu de Clean Code. Je me passionne de plus en plus pour un sujet, une attitude que je me vois appliquer dans d’autres conditions.

Je suis persuadé que je peux faire le parallèle avec l’architecture de SI, mon principal sujet. Après tout, sa construction est aussi organique que les entités qui la composent. Les problématiques de communication (dans l’organisation comme dans la technique), les problématiques de responsabilité de composants, la duplication, la clarté d’une architecture pour ses utilisateurs, l’adéquation de sa structure avec l’organisation en place. Et pourquoi pas des bad smells sur l’architecture de SI ?

Et si je veux faire ça chez moi ?

L’idéal, c’est déjà d’avoir un craftsman avec soi. Rien de mieux que quelqu’un qui a déjà parcouru ce chemin, qui connaît les obstacles et qui s’est donné pour objectif de transmettre son savoir. Donc si vous êtes déjà équipés, écoutez-le ! Mais voilà, si vous me posez la question maintenant, c’est certainement que vous ne l’avez pas à vos côtés.

Alors le mieux reste encore les ouvrages et la communauté. Lisez des livres sur le sujet, comme The Software Craftsman – Professionalism, Pragmatism, Pride (Sandro Mancuso). On m’a aussi conseillé The Pragmatic Programmer: From Journeyman to Master (Par Andrew Hunt et David Thomas). Et surtout, il ne faut pas hésiter à rejoindre le groupe meetup Software Crafts(wo)manship Paris, c’est un rassemblement de gens plein de bonnes intentions qui ne cherchent qu’à partager ! C’est aussi votre meilleure occasion d’échanger sur ce que vous avez appris, vos déboires, vos victoires ! La communauté a besoin de vous !

13 commentaires sur “Ce qu’on ne m’avait pas dit sur le développement”

  • Hello, très bel article, je suis déjà conscient que pour faire du beau code il faut qu'il soit réutilisable, par contre j'avais pas encore entendu parlé de TDD. Je vais commencé ma formation en avril si tout ce passe bien ( 1 ans de galère à chercher une alternance en ile de france ) et je commence déjà à m'auto former et je pense en complément me renseigner sur les ouvrages pouvant améliorer la qualité du code. Ces ouvrages sont ils uniquement en anglais ? car je me suis pas encore former en anglais et la lecture pourrait être mal interpréter ou incomprise . Merci pour le partage de ton expérience.
  • Lui demandé-je O.O
  • Hello ! @NekoDown : Je n'ai pas trouvé ces ouvrages en d'autres langues :s Ils (en tout cas celui de S.Mancuso) parle plus de l'état d'esprit que de TDD en lui-même. Le style du livre est assez direct, je le trouve plutôt accessible. Mais pour te former à TDD (pour les pratiques de manière générale) n'hésite surtout pas à rejoindre les communautés comme le meetup en lien dans l'article ou d'autres. Être accompagné sur ce sujet te permettra de progresser bien plus vite et à partager plusieurs expériences pour te construire efficacement la tienne. Bon courage à toi ! @Nicolas : Règle sur le présent des verbes du premier groupe ! Moi aussi ça m'a perturbé la première fois ;)
  • Plop, superbe article, merci beaucoup ! Mais au risque de faire mon puriste-rabat-joie en français "library" ça donne "bibliothèque" :p
  • @Stroggle : T'as bien raison ! corrigé :)
  • Bonjour, Super article qui illustre bien les difficultés que l'on rencontre et surtout le besoin d'être accompagné dans cette pratique. Le constat que tu as fait dès la "sortie du crayon", et qui n'a pas été souligné explicitement, et que la méthodo te fais écrire des tests plus orienté fonctionnel, quand tu prends ton problème à partir des besoins. Ce qui rend la confusion entre les tests automatisables issus de BDD et les tests qui nous on servis à écrire notre code beaucoup plus grande, à mon goût. Cela fait maintenant 1 an que je pratique TDD, et ma pratique a évoluée, j'aimerai avoir ton point de vue là dessus. Ce qui m'a également aidé à passer le cap du "je perd trop de temps", c'est de prendre un peu de recul par rapport aux tests que j'écrivais, du coups de prendre plus confiance par rapport à TDD et être confiant sur mon code. J'ai ainsi compris que certains tests sont obvious, sans réelle valeur et me font perdre du temps, et que l'étape de l'implémentation rapide fait perdre du temps. La façon dont la méthodo m'a été abordée était, comme tu l'as décrite, et je prenais soin de bien respecter tous les points, notamment commencer par écrire les tests sur papier et commencer par l'implémentation simple. Pour le premier point, le papier est devenu directement le fichier tests, rien empêche de mettre l'entête des tests dedans (de mon point de vue). Pour le deuxième point et pour prendre un exemple, si je devais faire une fonction qui fait une multiplication, j'étais très scolaire au début : je réfléchis à mes tests, j'en identifie en gros 3 : multiplication par 0, par 1 et 2 nombres quelconques. Du coup la première implémentation du code aurait été que ma fonction retourne 0. Puis la deuxième implémentation, j'aurai regardé que l'un des paramètres est 1, auquel cas je retourne le deuxième paramètre de ma fonction. Puis arrive le dernier test, et là en fait je fais cette fonction toute bête qui fais la multiplication des 2, en dégageant mon implémentation pas propre, bête et sans valeur. Du coup, en suivant ceci, je me suis retrouvé, sur du code plus compliqué que ça, à perdre énormément de temps. Jusqu'au jour où je me suis dit, "merde, certains tests ne servent à rien car ça va péter automatiquement, et remerde j'en ai marre de ces implémentations stupides qui me font perdre un temps fou". Du coup aujourd'hui, pour reprendre mon exemple bête et méchant, je ferai les 3 assertions dans un seul test et je fais directement la bonne implémentation. Est-ce aussi une étape que tu as rencontré ou alors, est-ce un constat de mon échec sur la méthodo et que d'une façon ou d'une autre, j'ai abandonné ? Dernier point, on aborde TDD en disant qu'on écrit des tests unitaires. Du coup, je suis parti à écrire des tests unitaires, avec tout ce qu'on m'a apprit dessus : on isole tout parce qu'un test unitaire doit pouvoir s’exécuter quel que soit le contexte. Et très rapidement, en pratiquant TDD, je me suis aperçu que les tests unitaires faisaient partis des tests sans valeurs et chronophage : à quoi servent, dans la plupart des cas, des tests où tout est isolé ? Comment je peux savoir quand je refactor si j'ai rien pété si les éléments de mon applications ne se parlent pas ? Et quel temps perdu à tout mocker/stuber ? Mon point de vu sur le TU est qu'il est parfois nécessaire pour isoler un problème et tester un algo très précis (mais si on doit en arriver là il y a peut-être un problème de design d'application et de responsabilité). Le mock/stub devrait, à mon goût, être utilisés que lors de dépendances externes.
  • Salut Pierre ! Je vais tenter de répondre à tout, n’hésite pas à me dire si je loupe un truc ou si j'ai mal compris ! Alors je n’ai encore jamais pratiqué BDD, mais en effet le fonctionnel guide mes premiers tests, en tout cas pour les couches les plus proche des utilisateurs. Par contre je ne suis pas sûr de comprendre la différence entre les tests automatisables et ceux qui ont servis à écrire le code (qui pour moi sont toujours présents et donc rejoués automatiquement par l’UDD). Pour le papier/crayon c’était seulement pour l’exercice, l’introduction à la méthode, mais par la suite pas/peu de papier, à part pour clarifier un problème (enfin chacun sa manière, moi ça ne m’aide pas, mais c’est peut être différent pour quelqu’un d’autre). Alors un test peut paraitre « obvious », mais dis toi qu’ils le sont peut être seulement pour toi et dans l’instant. Ce que je veux dire c’est que ces tests documentent le comportement de ton code et la façon de s’en servir, ça sert autant pour les autres (présents et futurs) que pour toi quand tu reviendras dans longtemps sur ce code ! Et s’ils sont « obvious » c’est peut être aussi que les objets de ton architecture sont bien isolés, simples et cohérents, de fait plutôt bon signe pour le design de ton application :) Au format Gerkhin une ligne par Given puis When puis Then c’est le rêve. Un test complexe, bourré de stubs/mocks et avec une assertion bien lourde, c’est souvent le signe qu’il y a de la dette quelque part, par exemple une méthode/classe avec trop de responsabilités. Et un troisième objectif de tests unitaires est pour moi de détecter les régressions au plus bas niveau. Quand un test unitaire est au rouge tu sais que le problème ne se trouvera que dans cet objet. Quand un de mes objets interagit avec d’autres, je vérifie d’abord cette interaction au niveau unitaire, est ce que mon objet donne les bons éléments à mes dépendance. Pour ce qui est de ce qu’on lui donne, ou la façon dont les dépendances utilisent ce qu’elles reçoivent, les TU de ces intervenants sont là pour assurer que chacun fait bien ce qu’il devrait. En gros, en TU on fait confiance à l’environnement dans lequel évolue l’objet. Si un problème survient dans alors que tests couvrent tout, vérifie d’abord les TU et écris un test qui reproduit le problème. Si ce n’est pas possible et que l’interaction est le problème, effectivement un test d’intégration peut t’aider, mais ils doivent rester peu nombreux par rapport aux TU et ne plus important ils ne doivent pas se substituer aux TU. Les tests m’ont aidé à prendre confiance non pas sur mon code en général, mais plutôt sur le code qui se retrouve encadré par les tests. Plus aucune peur de refactorer. Et si je me rend compte que j’ai cassé quelque chose malgré les tests, c’est certainement que l’un d’eux est mal foutu ou qu’il en manque ! (possiblement d’intégration, mais très souvent unitaire) N’hésite pas à regarder des vidéos à ce sujet (celle là par exemple je ne l’ai pas regardé mais ça me semble coller au sujet : https://github.com/sandromancuso/trip-service-kata) Pour la multiplication, c’est un exemple qui me semble peu en lien avec la pratique dans la mesure où une méthode qui ne fait qu’une multiplication c’est en fait une méthode qui ne fait qu’appeller une autre. La multiplication est gérée nativement par le language, mais c’est la méthode d’une lib, tu lui fais donc confiance au moment de tes tests unitaires. En gros si tu dois vraiment faire cette encapsulation, tu voudras plutôt t’assurer que tu as passé les bons paramètres à la multiplication plutôt que de vérifier qu’elle fonctionne -> plus qu’un test. Tu as peut être rencontré cette étape parce que le contexte t’y a amené, comme l’apprentissage de BDD (que je n’ai pas eu) ou une approche différente sur la méthode. Chacun a sa façon pour appréhender TDD, et aussi transmettre son savoir, donc je pense qu’il faut varier les expériences avec d’autres personnes expérimentées sur le sujet pour avoir des chances de l’aborder sous les bons angles. Je penses toujours que si tu n’as pas accès à ces personnes au boulot ou dans ton entourage, qu’il faut aller les chercher, par exemple durant des meetups spécialisés sur le sujet.
  • Superbe article ! Vous abordez les aspects humains - le doute, la remise en question - avec beaucoup de justesse. Je n'ai pas eu l'occasion de faire tout ce processus, juste une partie, à regret. Les questions de Pierre sont intéressantes, car je m'en suis posé des similaires. Le test d'un code horizontal (ou de classes utilisateurs, comme des collections) se passe très bien, un code vertical est plus problématique mais il est testable en isolant chaque couche, en revanche la transversalité devient carrément intestable (cohérence des communications inter-modules, systèmes de persistance implicites, injection de code, système de script, bus de messagerie, agrégateur de données, etc...) - typiquement les sous systèmes techniques comme un Framework ou les système distribués... C'est en tous cas, ce que j'ai constaté. Merci pour cet article.
  • Salut Gabriel, Sur ce projet notre techlead avait donné une contrainte : une classe ne pouvait dépendre que de classes de plus bas niveau, pas de même niveau et encore moins plus haut niveau qu'elle même. C'était une forte contrainte mais elle a évité pas mal de paquets de noeuds et rendu le code plus testable. Quand une classe commençait à comporter un nombre important de dépendances (souvent mis en évidence par la difficulté à tester), on se posait des questions sur le design. Mais pas mal de classes bien problématiques de ce type on pu être refactorées pendant des sessions dédiées (pour ce qu'on n'arrivait pas à faire seuls chacun dans notre coin). Pour ce qui est du framework je crois me rappeler que, pour certains morceaux, lorsqu'il nous posaient problème (typiquement des méthodes statiques) on les isolaient dans une de nos classes. En somme l'effet de bord était isolé dans une classe de plus bas niveau.
  • Bonjour. Excellent article en effet. Je vais essayer de répondre à certaines de vos interrogations puisque c'est l'un de mes domaines d'expertises. En fait, même parmi les experts, les avis divergent. Il y a un exemple célèbre que je ne retrouve malheureusement pas. Un kata où, l'étape un retourne zéro, la deux fait un mini-truc et la 3e code presque tout d'un coup. Une grosse discussion s'en est suivi sur comment s'en sortir en TDD. En gros, tout le monde fait les tests avant. Sauf que, par exemple, Uncle Bob va te dire: "Je suis un idiot, mais si je code petit pas par petit pas, sans trop savoir pourquoi ça va marcher". Stephen Colebourne va dire: "Je réfléchis au problème, je dessine et ensuite je code l'implémentation d'un coup". Je suis plutôt dans la catégorie Colebourne. Mais encore, ça dépend des cas. En fait, avec l'expérience, je remarque qu'on sent à la longue quelle méthode va marcher pour quel problème. Concernant les types de tests. Il faut retenir une seule chose. Le test unitaire sert au développeur à valider ce qu'il pense avoir codé. Le test fonctionnel valide que ça fait ce que le product owner voulait C'est deux choses différentes. Et un test fonctionnel n'est pas obligé d'être en BDD. L'important c'est de tester une fonctionnalité. Ensuite, rien n'empêche de faire un test unitaire sur un group de classes. Si elles sont faites pour être utilisées ensemble ça a souvent plus de sens. Je pourrais continuer un petit moment (ou écrire un livre... un jour peut-être) mais juste un dernier point. Je finis avec le temps par classer mes tests unitaires dans deux catégories. 1- Les tests de classes fonctionnelles: Je teste un bout de code implémentant un fonctionnalité. Ce test est très lisible et simple 2- Les tests de classes de framework: Je vérifie que la classe supporte bien la programmation multifil. Ce test est très technique, vaguement lisible sauf pour un expert et avec plein de commentaires partout. Conclusion, ce n’est pas vrai qu'un test est toujours super lisible, car à un certain niveau de complexité technique, le test finit souvent par être plus compliqué que le code testé.
  • Bonjour Julien, Super article ! je voulais laisser un commentaire pour te remercier de nous avoir partagé ton expérience. Je me retrouve complètement dans ce que tu décris ici (le besoin de "mentor", de "leader" pour aider à mettre en place le TDD, les difficultés rencontrées, etc...) et c'est intéressant de voir que nous sommes plusieurs à passer par ces phases lors de notre apprentissage du craftsmanship mais qu'au final, nous finissons tous convaincus par les avantages que cela apporte au quotidien. Merci pour ton article et ton partage d'expérience.
  • Un plaisir à lire ! En pleine introspection sur ma façon de voir le développement logiciel, cet article tombe à point nommé et va même au delà en abordant les aspects humains mis en jeux dans ce genre de projet. Merci Julien !
  • Super article ! Pour ceux que ça intéresse, et continuer de philosopher sur la manière de développer, creusez du côté du Domain Driven Design (Eric Evans ou Vaughn Vernon), qui s'accorde bien avec les notions de TDD/BDD.
    1. Laisser un commentaire

      Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *


      Ce formulaire est protégé par Google Recaptcha