Quel est le bon sens pour programmer ? Quelle méthode ou ensembles de méthodes peuvent aider à ne pas perdre le fil de la pensée ? À répartir sur plusieurs heures ou plusieurs jours une tâche complexe ? Comment revenir, après une interruption, dans le « flow » où il nous semble que tout est clair ? Comment enfin partager un développement quand on ne peut pas connecter nos pensées ?

Ce sujet, que l'enseignement initial de la programmation laisse malheureusement très souvent de côté, me semble primordial. Et je me pose régulièrement la question : comment programmer dans le « bon sens » ? Voire, comment programmer avec « bon sens » ?

Programmer at work - From https://en.wikipedia.org/wiki/File:Coding_Shots_Annual_Plan_high_res-5.jpg

Un programmeur débute généralement son apprentissage avec une méthode que je nommerais « méthode implicite ». Implicite car non expliquée, apprise par mimétisme, parce qu'elle semble logique : d'un problème, on passe à l'étude, puis à la programmation proprement dite, elle-même sous forme d'une série de modifications suivie de tests manuels.

Au fur et à mesure de ses pérégrinations, l'étudiant en programmation découvre des méthodes pour renforcer la robustesse de sa production. La programmation par contrat, l'analyse statique, les tests automatisés.

Malheureusement, cette démarche va rarement assez loin et bon nombre de programmes sont écrits « implicitement ». Cette méthode implicite qui a été apprise lors de l'apprentissage créé des programmes un peu bancals, qui fonctionneront dans le cadre de l'exercice qui n'est généralement pas de grande envergure. Des programmes terminés à des heures tardives peu avant la date limite.

Ce qui se retrouve dans le domaine professionnel : des programmes par totalement maîtrisés à cause du changement d'échelle, et terminés en heures sups avant la date butoir.

Pourtant, la littérature et les conférences sont nombreuses qui s'intéresse à la problématique de la méthode de création. Si les méthodes agiles sur la conduite de projet prennent, celles plus techniques concernant la programmation a proprement parler ont toujours du mal à s’implanter, malgré les exemples de réussite de ceux qui les appliquent.

Mêlée !

Les Méthodes Agiles, dont l'une des plus connues est la « méthode Scrum » s’implantent plutôt bien. Même si l'ensemble du message véhiculé par la profession de foi n'est pas forcément toujours bien compris ni appliqué, le passage sur l'impossibilité de tout prévoir dans le moindre détail parle aux gestionnaires de projets.

Du coup, ces méthodes sont soutenues et sont appliquées avec succès par des non techniciens. Ces méthodes sont enseignées dès la formation initiale. Et c'est tant mieux.

Mêlée technique !

La difficulté survient lorsque l'on passe à des méthodes agiles plus techniques. Plus complexes à comprendre, elles sont complexes à faire accompagner par les gestionnaires du projet, pour qui l'ordre des choses, le « bon sens » est alors bousculé sur un domaine qu'ils ne comprennent pas.

Ce qui fonctionne alors bien pour Scrum[1] passe moins pour l'eXtreme Programming (XP), ou le TDD (Test Driven Development, Développement Piloté par les Tests). Et au final, les équipes qui implémentent ces méthodes sont des équipes majoritairement acquises à la technique et qui s'auto-gèrent.

Les programmeurs n'ayant pas reçu dans leur formation initial d'introduction ou d'enseignement complet sur ces méthodes en sont généralement ignorant et très frileux à l'idée de s'y plonger. Après tout, la méthode implicite fonctionne à peu près.

Comme tout apprentissage qui bouscule le sens acquis, utiliser de nouvelles méthodes va faire perdre en vélocité et le programmeur peut se sentir découragé de revenir sur du code hésitant, sur des erreurs. Se positionner à nouveau en étudiant peut donner l'impression de régresser, et beaucoup s'arrêtent là.

Et pourtant, je ne connais pas de programmeurs faisant marche arrière une fois ces pratiques acquises. Revenir à la méthode implicite pour autre chose qu'un script de 10 lignes donne l'impression de régresser, et j'ai pu faire l'expérience de nombreuses fois où le script de 10 lignes se retrouve à grossir plus que prévu et où l'on regrette d'avoir fait une exception...

Par analogie, si vous travaillez actuellement dans un environnement avec intégration continue et rapport automatiques d'erreurs de compilations, envisageriez-vous de revenir en arrière ?

Dans le sens eXtreme

N'importe quel programmeur, comme n'importe quel créateur, auteur, artiste, fait l'expérience du « flow ». C'est ce moment où tout coule de source, où les distractions extérieures faibles n'ont plus d'emprise, où tout se déroule naturellement.

Et n'importe qui ayant fait l'expérience du « flow », à moins d'être extrêmement chanceux, fait l'expérience de son interruption. Une distraction extérieure plus forte que les autres, une hésitation qui fait perdre le sens de ce qui a été fait jusque là, une impression de ne plus pouvoir comprendre tous les morceaux, la perte de la maîtrise.

La programmation a cela d’intéressant qu'elle s'occupe de processus automatisés qui peuvent être testés. Et l'importance des tests apparaît très vite afin d'aider à garder sous contrôle la compréhension de l’œuvre dans sa globalité.

D'autre part, il a été compris assez vite, et n'importe quel programmeur en fait l'expérience, que la connaissance de l'écriture d'un programme se perd très rapidement. Les détails, les subtilités, l'architecture générale, tout cela se perd avec les mouvements d'équipe ou simplement avec la pollution mentale apportée par un autre projet et d'autres problématiques. Au delà de la perte de la concentration à l'instant de l'écriture, la programmation ajoute la nécessité de revenir sur ce qui a été produit plusieurs fois, par plusieurs personnes[2].

Lorsqu'il s'agit de tester un système, le coût s'envole donc en fonction du temps entre l'écriture du programme et la spécification de son test, voire de son écriture dans le cas de tests automatiques.

L'eXtreme Programming a proposé, entre autre bonnes pratiques, de tester unitairement et automatiquement un morceau de code avant de l'écrire. Le développement piloté par les tests (TDD : Test Driven Developpement) entérine cette pratique. Puisque le coût du test diminue avec sa proximité avec l'écriture, pourquoi ne pas lui faire franchir une barrière temporelle et spécifier/écrire les tests avant d'écrire le programme, systématiquement ?

C'est le sens inverse de la méthode implicite, qui part d'un premier programme suivi de tests. Mais à la vue des résultats engendrés, cela semble surtout être à la fois le bon sens, et de bon sens.

Le TDD est souvent compris comme l'écriture au préalable de tests unitaires des classes que l'on va écrire. D'autres auteurs vont plus loin (cf. Growing Object-Oriented Software, Guided by Tests) : pourquoi ne pas écrire un test complet d'utilisation de A à Z ? Autrement dit un test (ou plutôt une série de tests) fonctionnel automatisé. En commençant par les tests de déploiement de l'application encore vide.

Quitte à passer pour un publicitaire : l'essayer, c'est l'adopter. C'est confortable, pratique, rapide et plus sûr que la méthode implicite. En petits caractères cependant : ce n'est pas magique ! Ça s'apprend, ça se travaille et ça ne résout pas tout.

On recommence !

Que faire lorsque le résultat d'une création (programme, fonctionnalité) ne convient pas ? La méthode Scrum implique une revue de fin de « sprint » qui peut invalider le résultat pour repartir sur un nouveau « sprint ».

De même, lors de l'écriture technique d'un programme, il arrive que l'on arrive à quelque chose qui ne soit pas satisfaisant, en partie ou totalement.

Mais rien ne dit comment relancer la machine. En pratique, la plupart du temps le résultat obtenu sert de base à l'itération suivante et ainsi de suite, jusqu'à ce que ça passe, que ça casse ou à l'étape du fameux « refactor » qui dans ce cas est souvent une remise à plat du système à un état assez avancé de son développement, et donc complexe.

Une proposition qui bouscule le sens acquis est la suivante : si ça ne fonctionne pas, on efface tout et l'on repart _de zéro_. Cela heurte. Un programmeur n'aime pas défaire ce qu'il a écrit, j'en veux pour preuve la masse de code mort ou en commentaire dans un projet qui reste là juste « au cas où ». Cela heurte le bon sens de la même manière que le TDD avec le spectre de la perte de temps. Tout refaire, c'est forcément avoir perdu le temps passé la première fois.

Mais faire une fois supplémentaire, c'est se servir de ce que l'on a appris lors des exécutions précédentes. La nouvelle création est effectuée plus sûrement, plus rapidement, évite les écueils déjà trouvés et, évitant de se baser sur un travail qui a amené à l'erreur, se débarrasse du superflus.

Tout comme avec le TDD, qui part du principe que le temps passé en amont sera gagné sur le long terme, la remise à zéro (que l'on trouve dans la méthode Mikado) table sur un produit final qui sera de bien meilleure qualité que le résultat d'itérations successives échafaudées sur un ensemble d'erreurs.

Il est même fort probable que, moins empêtré dans du code ancien, mourant ou inutile, le nombre total d'itérations pour atteindre un bon résultat soit moins grand.

Barrières mentales

Sans un enseignement explicite de méthodes de création, sans une démarche volontaire du programmeur cherchant à comprendre sa manière de travailler et prendre du recul pour l'améliorer, il est aisé de poser ses méthodes de côté en se disant que cela marche « assez bien » avec la méthode implicite.

Une fois acquis des réflexes et une routine de création, changer est complexe.

Et pourtant, le jeu en vaut la chandelle. Ce qui prend pour Scrum doit pouvoir prendre pour les méthodes agiles techniques. Cela nécessite de la pédagogie, auprès des techniciens comme des non techniciens, afin de démontrer ou de montrer les avantages de travailler de cette matière.

Expliquer aussi les changements nécessaires dans la planification de projet qui sont généralement taillés pour amener la masse de travail intense sur la fin du projet, lorsqu'il faut terminer dans l'urgence et s'arranger avec la paiement de la dette technique contractée tout du long.

Ces méthodes, en reversant le sens, amènent la charge de travail en début de projet.

Le coût initial est fort. Mais plus l'effort et l'attention sérieuse est portée à cette phase, lorsque l'équipe est motivée, moins l'effort devra être fourni en fin de projet, au moment où l'équipe se fatigue.

Notes

[1] sans aller jusqu'à dire que c'est facile !

[2] Certains programmeurs décident d’évacuer une partie de ces problématiques en refusant de travailler en équipe, ou bien en vivant à des rythmes décalés ; l'image du nerd n'est pas très différente de celle d'un artiste, mais étonnante car appliquée à un métier qui semble industriel et dont le résultat produit n'est pas forcément compréhensible par un non programmeur.