Voir le flux RSS

GeoffreyOnRails

[Actualité] La correction de bug en 7 étapes

Noter ce billet
par , 31/07/2015 à 00h19 (536 Affichages)
La correction de bug occupe une place importante dans la vie d'un développeur. Entre les bugs qui remontent durant la phase de développement et la phase de pure maintenance, nous sommes littéralement infestés.

Il faut bien reconnaître que les bugs ont un certain talent pour apparaître sous des formes très variées (erreur système, problème fonctionnel, soucis de performance…) et avoir des causes toutes aussi diverses : mauvaise configuration, mauvais choix d'algorithme, mauvais format de données, cas particulier non pris en compte…

Néanmoins, il est surprenant de constater que malgré le temps qu'on y consacre, le bug fixing est un sujet parmi les moins discutés dans la communauté des développeurs, un peu comme si le simple fait d'évoquer le mot "bug" accroissait le risque de passer sa journée en s'en occuper…

« La peur d'un nom ne fait qu'accroître la peur de la chose elle-même.» - Albus Dumbledore

Par conséquent il s'agit peut-être de l'un des domaines d'activité du développeur parmi les moins matures, ou la plupart y vont un peu "au feeling". L'objectif de ce billet va donc être de remédier à ça en proposant une méthode pour la résolution de bugs.

La démarche

Rentrons tout de suite dans le vif du sujet, avec la délicatesse d'un troupeau d’éléphants.
La démarche de résolution de bug est constituée de deux grandes phases, d'abord identifier le bug, et enfin le corriger. (Là déjà, vous vous dites que vous avez bien fait de venir!).
Ce qui fait la particularité et la difficulté de la résolution de bug est presque toujours la première phase. En effet, une fois le problème clairement posé, corriger un bug est extrêmement similaire aux autres activités de développement, le développeur se retrouve alors de nouveau en terrain connu. Tout l'enjeu de la résolution du bug réside dans l'analyse du problème.

« If I had an hour to solve a problem, I would spend the first 55 mns thinking about the problem and 5 minutes thinking about the solution» - Albert Einstein (en fait non, il semblerait qu'il n'ait jamais dit ça, mais tant pis, même si elle a été inventée par un sombre crétin, la "citation" est bien quand même)

L'identification du bug

Tout d'abord, c'est quoi un bug? Un bug est la différence entre le résultat attendu (tel que décrit dans les spécifications dans le meilleur cas, tel que pensait l'avoir pourtant clairement demandé le client lorsqu'il a discuté de sa vision avec le commercial à la machine à café sinon) et le résultat effectivement implémenté dans le produit. La première chose à faire est de chercher à observer le bug, et à en comprendre les contours.

Pour cela, il faut :

1. Être capable de reproduire le bug.

La toute première chose à faire est de réussir à reproduire le bug. Vous ne pourrez jamais être sûr que le bug a disparu si vous n'avez pas été capable de le reproduire de manière fiable lorsqu'il était là. Vous n'aurez pas non plus de moyen à votre disposition pour pouvoir mieux le cerner.

En cas de difficultés à reproduire le bug, il apparaît nécessaire d'aller chercher un maximum d'informations sur l'état du système au moment du bug. Vos deux principales sources d'informations sont :

  • la personne qui a remonté l'incident
  • les logs.


2. Comprendre le comportement attendu.

Le comportement attendu peut être parfois simple à comprendre, mais dans certaines applications complexes, déterminer le comportement du système lorsqu'il reçoit tel ensemble de données peut nécessiter une fine compréhension du métier. Ce savoir est néanmoins indispensable, il faut donc aller le chercher là où il est (documentation, spécifications, chef de produit/expert fonctionnel…).

Dans les cas les plus complexes, il est utile de valider le comportement attendu auprès du chef de produit. Il apparaît parfois que le comportement souhaité n'est pas clair et/ou que fixer ce bug est finalement loin d'être aussi prioritaire qu'on le pensait. Parfois même, il n'y a finalement pas de bug.

Nom : imagesbug_20vs_20feature_small.jpg
Affichages : 2531
Taille : 38,3 Ko

« It's not a bug, it's a feature» - Bill Gates


3. Circonscrire le problème


Une fois que l'on est capable de montrer ce qui ne va pas (étape 1), et que l'on sait où on va (étape 2), il va falloir circonscrire le problème, afin de trouver l'endroit exact où se trouve l'erreur.

Dans certain cas, on a la chance d'avoir un message d'erreur explicite qui nous pointe directement au bon endroit, mais dans d'autres, il va falloir trouver par nous-mêmes.

Cette étape est itérative, on va d'abord déterminer quel module est fautif, puis quelle composant du module, jusqu'à arriver à la fonction qui pose problème.

Prenons un exemple :

Imaginons que notre bug concerne une application RH. Un clic sur un bouton de Daisy, la charmante assistante RH chargée de la paie, devrait permettre d'envoyer à Roger, salarié de MyPoney.Inc, son bulletin de salaire par mail, mais Daisy est désespérée, car le mail n'arrive jamais à Roger.

C'est à ce moment là que Super Mario, que vous commencez à regarder dans le code. Le use-case est implémenté de la façon suivante dans le logiciel :

Nom : Bug1.PNG
Affichages : 2429
Taille : 7,6 Ko

La première étape est de trouver lequel des modules ne fonctionne pas comme il faudrait. Une fois que l'on a déterminé que le module RécupérationDesDonnéesDuSalarie est fautif, nous reprenons la même démarche et nous allons décomposer ce module. Celui-ci est constitué de trois sous-modules, un composant qui filtre les données auxquelles l'utilisateur à le droit d'accéder, un composant qui va chercher les données en base, et un composant qui les mets en forme pour celles qui nécessitent des traitements… Nous allons ensuite chercher le responsable parmi ces sous-modules, etc…

« Et ça continue, encore et encore» - Françis, Lead Dev.

Comment détermine-t-on qu'un composant est fautif?

Pour déterminer quel module ne fait pas correctement son travail, toujours la même démarche, comprendre ce qu'il est censé faire, observer son comportement. C'est dans cette phase d'observation du comportement que l'on utilise les techniques classiques :
  • Utilisation des logs (ceux déjà présents & ajouts de nouveaux si besoin)
  • Ajout de tests unitaires
  • Utilisation d'un débuggeur
  • Refactoring pour clarifier le code
  • Replacement de blocs de code complexe par le résultat attendu pour les données d'entrée jusqu'à ce que ça marche… (long, mais utile en dernier recours pour demander de l'aide sur un forum ou pour prouver la responsabilité d'un composant tierce que vous ne connaissez pas en détail)


Et si je m'aperçois qu'aucun des modules n'est fautif, que chacun fait ce qu'il lui est demandé de faire?

« Sorry Mario, but you bug is in another castle» - Un champignon qui parle

Nom : Bug2.PNG
Affichages : 2412
Taille : 8,1 Ko

Alors c'est sans doute un problème d'architecture. Deux cas possibles : gros soucis de conception, et gros travail en perspective (ci-dessus, remplacer un module par un autre), ou alors il s'agit d'un souci (heureusement plus classique) d'interface entre deux des modules (l'un s'attend un recevoir le format A et l'autre envoie le format B)

4. Auditer le code autour.

Une fois le problème à l'origine de notre bug bien défini, il va falloir auditer le code autour. Cette phase a deux objectifs : trouver la présence d'autres erreurs connexes, déjà identifiées ou non, ayant la même cause, et vérifier qu'on ne fera pas de dégâts collatéraux lors de la phase de correction.

La correction du bug

Ça y est. La cause de notre bug est identifiée. On sait également ce qu'il y a autour du bug. Bien que le problème puisse être un gros souci de design global nécessitant 3 mois de développements, dans 99% des cas, il s'agit d'une correction de taille relativement modeste. Rien qui ne soit à la portée d'un développeur tel que vous.

Alors on fonce et on lui règle son compte?? Pas si vite. Le problème d'un bug, ce n'est pas seulement que "ça ne marche pas". Cela prouve bien souvent deux autres choses : les tests ne couvraient pas ce cas (sinon ils auraient planté, et le code ne serait jamais passé en production), et le code n'était pas suffisamment clair (sinon votre collègue n'aurait pas pu se planter. Sauf si c'est une grosse buse. Mais une grosse buse ne produit pas du code suffisamment clair, CQFD.).

5. Ajoutez des tests.

Certes, il est illusoire d'espérer couvrir tous les cas d'usages avec des tests. Pour rappel, même un test coverage de 100% ne couvre pas tous les cas possibles.

Cependant, l'objectif des tests est de couvrir les cas ayant le plus de valeurs pour l'utilisateur, et ceux qui sont les plus susceptibles de poser problème. Si un utilisateur a remonté un bug, c'est qu'il a eu un problème (ça avait de la valeur pour lui, sinon il n’aurait pas pris le temps -et le risque - de créer un ticket auprès du support), et que c'est susceptible de planter (puisque ça a planté). C'est donc un cas que nos tests auraient dû couvrir.

Dans cette étape, on va donc ajouter des tests. Idéalement, pour chacun des niveaux qu'on a examiné lors de la troisième étape, le use-case global, au niveau du module, au niveau du sous-module… (notons qu'il aurait étape possible, de le faire en parallèle de l'étape 3, pour aider à localiser le bug). A minima, couvrir le use-case global et les tests unitaires. Ensuite, on va rajouter les cas collatéraux que nous avons identifiés dans l'étape 4.

Enfin, si nécessaire, on va rajouter les cas qui fonctionnent bien, mais qui risquent d'être victimes de dommages collatéraux lors de la correction (dans le jargon, on appelle ça protéger ses arrières. Si vous voulez savoir pourquoi, ne le faites pas, et après vous saurez.)

6. Faites passer les tests

Vous êtes des développeurs, non? Donc j'imagine que vous savez ce que vous avez à faire. C'est un peu dans le nom de votre métier tout de même : développe - eur.

7. Nettoyez le code

Quoi, mais on vient de régler le problème là, non?? Oui et non. Comme indiqué plus haut, si le code était limpide, votre collègue ne se serait pas planté. Vous avez passé un certain temps à comprendre les exigences, comprendre le code, son environnement… Ça serait dommage de ne pas en profiter pour améliorer la qualité du code, et graver votre compréhension dans le marbre du code. C'est l'étape de refactoring.

En réalité, vous pouvez déjà avoir commencé le refactoring dans l'étape 3, ce qui aide à comprendre l'existant (référence). De plus, selon l'importance de l'étape 6, il est tout à fait possible de suivre un cycle itératif (commencer à nettoyer, corriger un premier problème, nettoyer, corriger, nettoyer…) Mais "corrigez votre bug en sept étapes", ça a du potentiel pour faire le buuzzzz, alors je l'ai mis là.

Vous avez peut-être remarqué que la description de la "correction du bug" ressemble étrangement aux bonnes pratiques de développement habituelles (Test-driven development, refactoring…). C'est normal. Selon moi, c'est le meilleur moment pour les appliquer :

  • Les délais sont souvent moins contraint que lors de la création d'une nouvelle fonctionnalité (peu de monde se risque à donner/demander une date précise pour la correction d'un bug qu'on ne comprend pas)
  • Un bug étant un problème de qualité, ce n'est pas le moment le plus confortable pour vous demander de renier sur la qualité.


La morale de cette histoire

La principale étape du bug-fixing (en terme de temps à consacrer et d'importance) est l'identification du bug. C'est lorsqu'on ne prend pas le temps de faire cette étape qu'on voit de grosses horreurs : qui n'a jamais vu un junior taper son message d'erreur sur google, prendre la première configuration/bout de code qui fait disparaître le message et considérer le problème comme réparé sans avoir compris ce qu'il s'est passé… (Et je suis bien placé pour le savoir, je l'ai fait aussi lorsque je débutais...) Une fois l'étape d'identification faite, la correction devient très simple, et est l'occasion ou jamais d'appliquer les meilleures pratiques de programmation et d'améliorer la qualité du code.

Dans un sens, la démarche est extrêmement similaire à celle d'une enquête policière, d'un audit financier, ou d'un diagnostic médical : là où le médecin, l'auditeur et le policier cherchent la cause/le coupable d'un dysfonctionnement, dans un système biologique, une société humaine ou une entreprise, nous cherchons le bug dans notre propre système complexe : une application informatique, ses milliers de lignes de code, ses centaines de dépendances.

Bon débug!

Envoyer le billet « La correction de bug en 7 étapes » dans le blog Viadeo Envoyer le billet « La correction de bug en 7 étapes » dans le blog Twitter Envoyer le billet « La correction de bug en 7 étapes » dans le blog Google Envoyer le billet « La correction de bug en 7 étapes » dans le blog Facebook Envoyer le billet « La correction de bug en 7 étapes » dans le blog Digg Envoyer le billet « La correction de bug en 7 étapes » dans le blog Delicious Envoyer le billet « La correction de bug en 7 étapes » dans le blog MySpace Envoyer le billet « La correction de bug en 7 étapes » dans le blog Yahoo

Mis à jour 10/08/2015 à 10h24 par Hinault Romaric

Catégories
Programmation

Commentaires