mercredi 12 février 2020

Formation en ligne : proposer une pédagogie adaptée

Depuis novembre 2019, j’anime une formation en ligne de 4 semaines sur la dette technique. Habitué des formations en présentiel, j’ai adopté une nouvelle posture de formateur “facilitateur” dont le rôle est primordial pour guider les apprenants à distance. L’enjeu est de favoriser les interactions, d’éviter l’isolement et le décrochage que l’on observe souvent avec ce type d’apprentissage en ligne. 
Jusqu’à présent, je pensais que les cours en ligne se limitaient aux MOOCs. Selon moi Internet était l’occasion pour le formateur d’atteindre un très grande nombre de participants avec un investissement de temps de travail raisonnable. Certes les MOOCs sont une réelle opportunité de s’instruire à un moindre coût mais l’apprenant reste relativement “passif” derrière son écran. À l’époque, je n’avais pas encore compris en quoi sous une autre forme l’elearning pouvait rendre l’expérience d’apprentissage encore plus intense même pour un petit groupe.
Cela fait environ 7 ans que j’ai lu “Training from the back of the room”. Cet ouvrage a changé la façon dont j’anime mes formations. À travers ce livre, j’ai compris pourquoi je n’aimais pas les cours magistraux et pourquoi j’avais raison de ne pas les aimer. En effet ce mode d’enseignement est mal adapté à la façon dont, nous les humains, apprenons. 
Ce livre rend accessible la recherche sur la pédagogie et surtout les avancées depuis le siècle dernier. On y découvre que nous apprenons surtout par nos actions et interactions. Il vaut mieux avoir du matériel à disposition, un groupe d’apprenants avec qui échanger, un formateur dont le rôle est de guider et de répondre à nos questions, ni plus ni moins. Ensuite il est indispensable de passer rapidement à la pratique pour bien ancrer les notions apprises pendant le cours. Le formateur est là pour créer cet environnement et il est aussi là pour nous expliquer tout ce qui ne s’invente pas, que nous pourrions difficilement découvrir par nous-mêmes.
J’ai donc optimisé mes formations en présentiel afin qu’elles exploitent au maximum le temps de pratique et qu’elles privilégient l’échange, soit entre formateur et stagiaires, soit entre stagiaires. Il y a des démos pour ce qui ne s’invente pas, mais une fois passés ces moments obligatoires, chacun a la possibilité d’avancer à peu près à son rythme. J’ai rendu les stagiaires autonomes au maximum, à tel point que je les accompagne tout en les laissant évoluer librement, puisque les exercices et le matériel “se portent” tout seuls. 


En présentiel, les démos sont les mêmes pour tout le monde, au même rythme. Aussi certains échanges dans le groupe n’intéressent pas tout le monde. Et passé le jour de fin de formation, toutes les explications ne sont plus disponibles.

L’humain et l’interaction personnalisée au coeur de la formation en ligne

Ma vision en faisant une formation en ligne était d’améliorer l’utilisation du temps pour les stagiaires. Lorsqu’on regarde une vidéo, on peut le faire dans les transports, à la vitesse qu’on souhaite. Si on veut la revoir c’est possible, n’importe quand, n’importe où. On peut choisir de ne pas regarder certains sujets qu’on connaît déjà ou qui ne nous concernent pas. Quant aux exercices, on peut prendre le temps que l’on veut pour les terminer. C’est en quelque sorte une consommation “à la carte”, à son rythme.
Dans ma formation en ligne sur la dette technique, j’ai choisi d’ajouter une bonne dose d’interaction avec le groupe à travers le pair-programming à distance. Cet exercice fonctionne très bien avec LiveShare ou Floobits. En complément, les apprenants échangent entre eux et avec moi chaque semaine lors des lives, un groupe slack permet aussi des échanges individualisés ou en groupe sur le code. On y retrouve alors les bénéfices du présentiel, avec les avantages de l’indépendance offert par le format en ligne, tout simplement.
D’ailleurs les retours de la première promo sont très positifs. Si vous voulez découvrir les témoignages des participants ou en savoir plus sur le programme de la formation, je vous invite à aller sur le site de SuperTilt. Si vous souhaitez vous former, profitez-en le premier module est gratuit. Après cette première semaine de découverte, vous aurez le choix de poursuivre l’aventure avec nous en rejoignant le groupe des apprenants.

mardi 28 janvier 2020

Quand faut-il refactorer pour éviter le legacy?



Quand est-ce que nous devons refactorer le code? Pour ne pas contracter trop de legacy il faut refactorer, on le sait, mais quand? 

Est-ce qu'il faut faire des stories de refactoring? Est-ce que ce sont des stories "utilisateur"? Ou faut-il le faire plutôt au fil de l'eau? Pour moi la réponse est "majoritairement au fil de l'eau", pendant les stories. Dans cette video on voit pourquoi cette approche est la plus intéressant à mon sens et s'il faut le faire avant, pendant ou après la story. 

On verra aussi en quoi ceci peut être contre nature au début car c'est pas dans nos habitudes. 


Toi aussi tu aimes le code propre et le legacy t'embête, voire le fais chier :), viens à mon webinar sur la dette technique, ce vendredi 31/1 et tu pourras poser les questions qui te brulent en direct!
  T peux aussi jeter un oeuil sur la formation interactive en ligne Clean code, sortir de la dette technique, il y a des chances que ça te plaira autant que les personnes qui l'ont déjà suivi ;o)

lundi 6 janvier 2020

Stop feeding the big classes - Class division

I typically ask people to show me the one or the a few classes that hinder them from writing unit tests. Almost consistently (!) this is a class of 1000 lines or even a few thousand lines of code and this is where most of the development work takes place. Of course this is very difficult to test and even though sometimes these classes are tested with unit tests, they are very very hard to maintain. 

I wouldn't even try

I do admit that it is very hard to write unit tests for these classes. I would find it very difficult to write good unit tests for them too. In fact I wouldn't even try to test this with unit tests. Now what I would do is I would extract the behaviour that I want to modify into other classes or more likely to a completely new class. If I move out a method of say 10 to 50 lines of code then that's very easy to test and things that are easy to test are easy to change by adding behaviour to it. In doing this, besides making things testable, we improve things by splitting things into smaller pieces. Smaller pieces are more easy to understand to test and to assemble into new behaviour i.e. reuse these classes elsewhere

flickr.com/photos/102642344@N02/10203178726/


Given that we do split the big classes into pieces as we do features then it doesn't take many stories to make our codebase a lot more maintainable and all the way we been able to work with lower level tests. All we needed to ensure is that the things we split apart are meaningful pieces that are functionally coherent. This is probably the most difficult part - extracting something meaningful. It requires being able to design code and if we don't have much practice doing it won't be very good. On the other hand if we don't start practicing this then all code we  create will just be a bigger and bigger mess. I mean there's a reason (almost) all living things grow by cell division, not by cell growth.

The actual act of extracting something is really simple. Very often it is as simple as

  1. Extract Method (automated refactoring)
  2. Create class and add it as a member of the existing class
  3. Move Method (automated refactoring)


The bottom line

The next time you touch a big class then split something out, find something meaningful you can move out and test and then change that piece. Done! The modification is safe and it's done rather quickly. We started out with a testability issue, but really it's a design issue regardless of the need to test.

jeudi 5 septembre 2019

Tester c'est douter, la qualité coûte cher!


Qui n'a pas entendu le dicton Tester c'est douter. Certes ce dicton reste peu crédible. Bien que c'est une façon s'excuser de ne pas faire des tests sur le ton de la rigolade, personne croit vraiment que cela soit une bonne approche. Par contre La qualité coûte cher est déjà plus crédible dans l'oreille des gens. Pourtant cela est tout aussi faux.

Faux? Ah bon, vraiment? La qualité ne coûterait pas cher?
Non. Je dirais que c'est la non-qualité qui coûte cher. Et ce même à court terme! Visiblement, au regard des projets que je vois passer, tout le monde ne partage pas mon avis et pense que cela vient en partie d'un regard différent que l'on porte sur nos projets;

D'abord dev puis test

Si on aperçoit le coût de dev séparément de sa validation, si on divise l'écriture du code et l'écriture des tests une fois le code fini, on va voir un coût de dev. Puis un coût d'écriture des tests. Puis le coût de validation manuelle. Dans ce monde la qualité a un coût très mesurable, c'est déjà l'ensemble de l'écriture des tests. C'est aussi tout éventuel refactoring énoncé lors du dev. Dans ce système on cherchera naturellement à réduire les coûts qui n'ont pas un intérêt évident. Soit en caricaturant faire le dev vite fait mal fait, sauter les tests au profit d'une autre story. Certes on garde les tests manuels car sinon il y aurait trop de bugs en prod.

Test au fil de l'eau (~TDD)

Si au contraire on rend visible le temps passé entre début de dev et le tampon de validation de la part du PO et des testeurs, voire jusqu'au stabilisation en prod pour les plus modernes des chaines de livraison continue. Alors une méthode de dev qui supprime les retours des testeurs et PO deviendra beaucoup plus intéressante. En effet le fait de spécifier à travers des exemples deviendra naturelle pour éviter les erreurs d'interprétation, ces exemples deviennent au fur et au mesure du dev des tests automatisés et la validation manuelle après-dev se réduira au stricte minimum. Il n'y a plus d'erreurs de compréhension à détecter à la fin, car elles sont toutes relevées en amont du moindre ligne de code.

Dans le premier mode de travail la qualité, en termes d'écriture de tests après dev, a un coût difficile à rentabiliser. Dans le deuxième cas les tests sont naturelles et rentabilisés par conception de mode de travail.

C'est notre regard sur le système qui dictera le façon que nous l'optimiserons. Si nous voulons un changement de fonctionnement il faut trouver un nouveau regard.

vendredi 3 mai 2019

Mob-programming, l'histoire d'une équipe

J'ai eu la chance d'interviewer une équipe qui fait du mob-programming pour le travail quotidien. Personnellement j'en ai fait souvent lors des coding-dojos, mais jamais pendant une longue durée dans un projet, c'était donc pour moi une excellente opportunité de bénéficier de leur expérience! Comme j'ai adoré ce petit échange d'une heure avec cette équipe très épanoui et fier de leur travail, je vous partage cher lecteur une version condensé en espérant que vous trouverez cela aussi utile que moi.



Comment cela a-t-il commencé?

Au début il y a avait qu'un développeur, et avec l'arrivée de deux autres on voulait essayer le mob rien qu'en mode apprentissage. On pouvait ainsi continuer à livrer tout en enseignant l'environnement technique et fonctionnel. "C'est bien plus agréable que de lire des docs" Remarque l'un des développeurs!

C'était très progressif, mais au bout de quoi, peut-être 3 semaines les nouveaux étaient opérationnels et participaient à pied d'égalité. Cela fait environ 9 mois maintenant et depuis nous avons intégré une nouvelle personne. La nouvelle personne a aussi beaucoup apprécié.

Cette nouvelle personne connaissant un peu moins la stack et les méthodes de développement (souvent du TDD) il a beaucoup apprécié le mob et notamment le fait de forcer le passage au clavier, pendant 7-10 minutes à la fois. Quand on est obligé comme ça on est obligé de s'accrocher et on progresse très très vite.
Il a été naturel de continuer le mob même après cette phase de montée en compétences

Justement, je suis curieux, faites-vous du mob tout le temps?

Non, on travaille aussi souvent en pair-programming, et même tout seul parfois. C'est assez naturel de switcher entre l'un et l'autre.

Qu'est-ce qui fait que vous choisissez le mob ou le pair ou le solo.

Le mob c'est lorsqu'il y a un peu de complexité, quand on sent qu'il y aura pas mal de discussion. Si quelque chose est très clair, qu'il n'y a pas trop de questions à se poser on va le faire seul ou à deux. Le choix se fait assez naturellement.

Quels sont d'après vous les bénéfices du mob?

Le code est bien meilleur en mob, on va tout de suite faire une remarque qui va être pris en compte sans perte de temps. Souvent c'est des choses qu'on aura même pas pris la peine de remonter lors d'un code-review.
Parfois on s'est retrouvé à même annuler une story suite à une discussion survenue dans le mob. C'était pas si facile de gérer ceci dans le code et c'était discutable d'un point de vue métier. Si quelqu'un l'avait fait seul on peur qu'il aurait juste foncé sans se poser ce genre de question.

<< Et là le PO a très envie d'intervenir :) >>

- Oui le truc le plus génial c'est que maintenant le dev plutôt js se retrouve à faire un peu d'embarqué (seul) et du coup je n'ai plus à planifier un petit peu de ceci et un peu de cela pour que tout le monde soit occupé dans l'équipe. Je peux me soucier de ce qu'il faut fonctionnellement!

- Effectivement ça doit faciliter les absences aussi, quelque part ça crée automatiquement une équipe pluri-disciplinaire

- Exactement!

Est-ce qu'il y a eu des tensions à gérer?

Oui, d'un point de vue matériel une fois qu'on était lancés, on s'est rendu compte qu'il fallait acheter un grand écran de qualité et on a fait des modifications à un bureau pour qu'il n'y ait pas de gêne pour s'approcher, genre des box à tiroirs.

Sinon dans l'équipe il y a beaucoup de bienveillance donc il n'y a jamais de problème de ce point de vue là. Par contre c'est assez intense et après une journée de mob on sent qu'on a travaillé! On n'a pas de pauses programmés sauf un peu le matin et en début d'après-midi et ça marche bien comme ça. Si quelqu'un n'est pas à fond tout le temps ce n'est pas très grave non plus.


---

Merci beaucoup à Damien, Romain, Sylvain, Benjamin et Vincent d'avoir partagé toute cette expérience. J'ai pas mal pratiqué dans des dojos, mais je n'ai pas réussi à mettre en place le mob à ce point dans un projet et ce genre de retour m'aide. Que vous continuez à prendre autant de plaisir dans le travail!

mardi 2 avril 2019

In cognitive psychology is refactoring a cost or a benefit?


Did you ever get frustrated when someone refactored your code? Or did you refactor someones code just to find out that the original developer wasn't all too happy about it?  This certainly happened to me a few times. Here's my understanding of what's happening from the point of view of cognitive psychology.

In cognitive psychology there's the term Long-Term Memory (LTM), which is roughly what we usually call just memory. What's interesting with our LTM is that it's optimised for storing large amounts of data, but not for changing existing data.

When we refactor some code we're forcing the developers knowing the code to rewrite their long-term memory. Remember that LTM is made to store a lot but not to be updated. That's a cost we're imposing on the original author. Sure the intention is to make future features cheaper to implement, but that's still a certain cost vs a potential benefit, later.

Given this we can better understand the frustration that a developer might feel when his/her code is thoroughly refactored. The bigger the change the higher the cost for the developers who know the code. In particular if the only motivation for changing the code is to make it cleaner or better, then that frustration might well be justified. After all better for whom? Certainly not for the original developer!

On the other hand whenever we have to evolve the code we're forced to rewrite the code and the corresponding LTM anyway, and if the code is not very well decoupled then it is quite unclear what part of our mental model of the code has to be rewritten. Should our brain just hang on to the original image or replace it or a part of it? The resulting LTM rewrite is probably imprecise, or requires more effort. The more complex the code, the harder it is to remodel and if it contains accidental complexity, we pay accidental remodelling cost. In short, with bad code that keeps changing, it's expensive to maintain a good mental model of the code in our long term memory.

If instead it was implemented respecting the principles of Single Responsibility Principle (small cohesive parts) and Open-Closed Principle (for new features code is added, instead of changed) then for any change in functionality we'd only have to swap one well identified part of the code. It becomes clear to our brain which part has to be rewritten in LTM, hence the cost of a rewrite is low. By respecting the principles of SRP and OCP we minimise the cost of maintenance in our LTM.

Conclusion and disclaimer


Cognitive psychology tells us there's an additional cost of refactoring in the form of (cache) invalidation of our mental model of the code. Of course this doesn't mean we should avoid refactoring as it also tells us that we need well factored code in order to minimise the cost as new features get added. By understanding the forces at play we can better choose when and what to refactor.

I'm nowadays very wary of refactoring code that doesn't need new behaviour. It hasn't always been so, indeed for several years I happily refactored code just because I could. Learning the basics of cognitive psychology has thought me to concentrate refactoring efforts to whenever I need to change the code anyway.

As a last word, there are valid reasons for refactoring code without a need for change. For instance for readability: naming things, extracting methods. Another good example is removing accidental complexity.

Would you like to hear more about this? Like how our LTM is feed through Chunking and Short Term Memory and how to optimise for that?

jeudi 3 janvier 2019

Testability trick - Replace static initialiser with lazy initialiser

A simple 4-step refactoring to make code more testable.

Static initialisers are a major hurdle for testing. If you have a static initialiser that for instance performs a request to the database then you cannot access that class in a unit test. Let alone mock it. In fact only importing this class will execute the query. So even if you're trying to test some other class, you cannot, since the offending class is contaminating every import of it with non testability. You want to make sure to remove the static initialiser so that other classes can be tested in isolation.


The solution is to replace the static initialiser with a lazy initialiser. This will allow you to load the class for mocking. You'll probably have to also replace a static method by an instance method in order to avoid calling the real method and so properly test classes that are calling the offending class, but first things first.

Assuming that your static initialiser is initialising a static field of your class:
  1. Encapsulate the access to the field with a simple getter
  2. Use the *Extract Method* refactoring to extract all the code from the static initialiser
  3. In the getter initialise the field *if it is null* by calling the extracted method 
  4. Delete the static initialiser
Yep it's that simple! :o)

Your next step will likely be to replace the public methods using this getter by instance methods so that those can be tested (example).

Let's have a look at an example of the code before refactoring

And now here's the refactored code.


Having "Replaced static initialiser with lazy initialiser" we're almost there. We can safely import this class, but we can't yet mock the static method. In order to make it mockable we can simply add an instance method as a proxy to the static method, then use the instance method instead of the static method .