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

le 18/01/2016 par Julien Tellier
Tags: Software Engineering, Craft

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


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 du 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


“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 !