Mokona Guu Center

Debug : un état d'esprit

Publié le

Il y a peu, un chef de projet demandait à l'équipe informatique : « comment vous faites pour trouver des bugs ? ». La question m'a surprise et ma réponse fut quelque chose du genre « si tu as quelques jours devant toi, je t'explique ». À vrai dire, je n'avais pas vraiment de réponse à donner et l'ampleur de la réponse me semblait importante.

Cependant, cela m'a fait réfléchir à la question, car il y a effectivement un apprentissage du debug (déverminage en français... ce sera la dernière fois de l'article que j'utiliserai ce mot) et tous les programmeurs ne sont pas forcément à l'aise avec cette partie du développement.

C'est d'ailleurs le première chose qui m'est venu à l'esprit : il y a des débutants qui se lancent dans du debug efficace et des programmeurs de longues dates qui ne s'en sortent qu'avec difficultés. Je me suis donc dit qu'il y avait quelque chose de plus que l'expérience, qui compte néanmoins, pour aborder un debug avec efficacité.

Le debug commence par une constatation : il y a un bug. Le programmeur peut être confronté à ce bug directement ou indirectement, via un rapport de testeurs ou d'utilisateurs. Cette première phase est importante car elle nécessite une lecture de ce bug. S'il est souvent facile de comprendre un bug lorsqu'il se produit juste après un développement, il est beaucoup moins facile à interpréter lorsqu'il intervient plus tard, voire lorsqu'il intervient beaucoup plus tard chez un utilisateur qui n'est pas forcément en mesure de reproduire les étapes d'utilisation du logiciel qui l'ont amené à ce bug.

Ici, avoir des testeurs aide grandement : ils sont capables, généralement, d'isoler le bug et de trouver un chemin minimum permettant de le reproduire. Ils peuvent ensuite rédiger un rapport sur les étapes à effectuer et les résultats.

Le programmeur n'est cependant pas sorti d'affaire avec sa lecture du bug : ce qu'un testeur ou un utilisateur peut reporter est une suite d'actions et un résultat perçu. Ce résultat étant perçu (l'affichage s'arrête, le programme plante, le son se met à boucler, un pad n'est plus reconnu, un personnage du jeu se met à faire n'importe quoi, un message à l'écran est mauvais), il ne reflète pas forcément le domaine du programme dans lequel le bug est présent.

Dans un jeu vidéo, il est très possible qu'un bug de simulation mécanique soit causée par un problème dans le gestionnaire de temps qui lui même est perturbé par la gestion de la pause du jeu. Un testeur risque de relever alors : après avoir fait une pause, choisi telle option puis être revenu dans le jeu, les voitures foncent dans les murs. Le rapport de bug mentionne les voitures et la pause, mais ne mentionne pas la cause du bug, qui se trouve dans le gestionnaire du temps.

J'en viens donc au titre de cet article : l'état d'esprit.

Afin de corriger un bug efficacement, ce qui signifie préalablement identifier sa cause, il faut avoir, ou se mettre dans, un certain état d'esprit. Je le pense fondé sur une base : l'absence de préjugé.

L'absence de préjugé

J'entends souvent à la lecture d'un bug : « ah oui, je sais d'où ça vient ! »

J'entends aussi, mais en tendant l'oreille car c'est dit sur un ton plus confidentiel : « mouais, ça doit être machin qui a archivé une bêtise ! »

D'expérience, ces deux préjugés font perdre du temps. Il est possible que le programmeur ait une intuition de la provenance du bug, il est même possible que la correction soit évidente. Il est aussi possible que ce soit machin qui soit à l'origine du problème.

Mais ignorer ces préjugés est une bien meilleure réaction.

Je sais d'où ça vient !

Ce préjugé entraîne une correction rapide, un archivage plein de confiance et... un autre bug. Oh, pas tout le temps ! Mais assez souvent pour que cela mérite l'attention.

S'il y a une erreur dans le programme, c'est que quelque chose s'est mal passé. Si ce quelque chose s'est mal passé, c'est que le programmeur qui a révélé le bug avec ses ajouts n'a pas été cohérent avec l'existant. Comme ce n'est pas forcément le code ajouté qui introduit le bug, mais qui le révèle, corriger ce que l'on pense être la source du bug peut avoir pour résultat de dissimuler l'erreur et non la corriger.

L'exemple classique est la ligne que l'on ajoute et qui fait planter le programme. On l'enlève, ça ne plante plus, on l'ajoute, ça plante. Le programmeur essai alors de modifier cette ligne pour avoir toujours le même effet sans le plantage (qui est souvent incompris, même pas analysé). Ça ne plante plus... jusqu'à plus tard. À vrai dire, la ligne ajoutée bougeait un peu le comportement du système, et l'erreur venait d'à côté.

Un exemple concret en C/C++ est le classique débordement de tableau stocké en variable automatique. Le débordement est inoffensif jusqu'au moment où, lors d'une modification du code source, il écrase une valeur importante. On peut modifier le code jusqu'à ce que le débordement soit à nouveau inoffensif, mais la vraie correction est celle qui corrige le débordement.

Parfois, la précipitation dans la correction du bug fait que l'on ne cerne pas bien le système et que la correction révèle un autre bug. Dans les pires cas, qui ne sont pas si rares, cela peut entraîner des révélations de bugs en cascade sur plusieurs heures, voire plusieurs jours. J'ai cette image lorsque cela arrive d'une coque de navire que l'on rafistole avec des planches et des clous, mais qui se casse à un autre endroit à cause des coups de marteaux.

Je sais qui c'est !

Cette réaction est gênante car souvent répétée, elle amène dans une équipe une méfiance entre les programmeurs (ou bien, puisque les données aussi peuvent provoquer des bugs, entre les programmeurs et les utilisateurs). Cette réaction a tendance à désigner les « mauvais programmeurs », ceux qui introduisent fatalement des bugs dans la base. Du coup, plutôt que de chercher à corriger le bug, le correcteur va d'abord chercher à savoir ce qu'à fait ce ou ces « mauvais programmeurs » (qui peuvent être différent suivant les membres de l'équipe d'ailleurs).

C'est une perte de temps !

D'abord car cela peut laisser le correcteur sur une fausse piste pendant plus longtemps que nécessaire. Et ensuite car, si effectivement l'archivage en question a révélé le bug, ce n'est peut-être, comme au paragraphe précédent, qu'un révélateur. J'ai vu plus d'une fois quelqu'un commencer sur cette voie, annoncer que tel archivage d'untel avait créé introduit le bug et se faire tout petit quelques temps après lorsque le bug est enfin trouvé... parfois dans du code du correcteur.

Le programme est une œuvre collective et lorsqu'un bug apparaît, il est inutile de savoir qui en est à l'origine. Le plus souvent, il s'agit d'une mauvaise conceptualisation du système par l'ensemble de l'équipe : certains pensent qu'il fonctionne d'une certaine façon, d'autres d'une autre façon.

Un bug doit être corrigé vite, car sa présence coûte au projet (temps de correction, mais aussi temps d'indisponibilité de la fonctionnalité). Il ne faut pas perdre du temps à savoir qui, ni même à partir sur une piste à partir d'une présomption. L'évaluation des membres de l'équipe fait partie du travail de management, pas de debug.

La piste à suivre...

Je pense que l'absence de préjugé nécessaire à un bon debug est ce qui fait qu'un débutant peut très bien être plus efficace que certains programmeurs d'expérience. Un débutant n'a pas de préjugé : ni sur le code, car il n'a pas l'historique nécessaire, ni sur les personnes, car il ne connaît pas encore le fonctionnement d'une équipe. La bonne chose à faire, qui est aussi la difficulté, est de garder cette absence de préjugé tout en gagnant de l'expérience. Ce n'est pas simple et il faut parfois s'y contraindre. Mais je sais que depuis que je m'applique cette discipline, j'ai gagné en efficacité lors d'une correction de bug. Débarrassé des préjugés, je peux me concentrer sur le suivi de la piste du bug.

Suivre une piste de bug est autre sujet passionnant, mais cet article ce termine ici.