Correction de bogues étranges et stratégies de débogage éprouvées par le temps



Vous vous souvenez de la dernière fois que vous avez rencontré une erreur d'interface utilisateur qui vous a pris des heures et des heures à corriger? Cette erreur s'est peut-être produite périodiquement, sans raison apparente. Peut-être est-il apparu sous certaines conditions (cela pouvait dépendre de l'appareil, du système d'exploitation, du navigateur ou des actions de l'utilisateur) ou était-il caché quelque part dans les profondeurs de l'une des nombreuses technologies frontales qui font partie du côté client du projet Web?



J'ai récemment dû me rappeler à quel point les causes des erreurs d'interface utilisateur peuvent être déroutantes. À savoir, nous parlons de corriger un bug intéressant qui affecte la sortie des images SVG dans le navigateur Safari. Cette erreur s'est produite sans aucun système spécifique et sans raison évidente. Face à un problème, j'ai essayé de trouver des cas similaires, espérant que les descriptions de tels cas me donneraient une idée de ce qui se passait. Mais je n'ai rien trouvé d'utile. Certes, malgré tous les obstacles devant moi, j'ai pu faire face à cette erreur.



J'ai analysé le problème en utilisant certaines des stratégies de débogage que je vais aborder dans cet article. Après m'être débarrassé de l'erreur, je me suis souvenu du conseilque Chris Coyer a donné à ses lecteurs Twitter il y a quelques années. Ce conseil est le suivant: "Écrivez l'article que vous aimeriez trouver lorsque vous avez visité un moteur de recherche." En fait, c'est ce que j'ai fait.



Aperçu du problème



Dans un projet sur lequel je travaillais, sur un site en direct, j'ai trouvé une erreur dont j'ai enregistré la manifestation sur cette vidéo. Voici à quoi ressemble le bouton dans son état normal.





Bouton en état normal



Et voici le même bouton après un problème.





Une partie du bouton est coupée



J'ai reproduit cette erreur dans diverses situations provoquant le redessiner de la page. Par exemple, cela s'est produit lorsque la fenêtre du navigateur a été redimensionnée.



Afin de démontrer le problème, j'ai créé un exemple sur CodePen . Nous en discuterons plus en détail ci-dessous. Mais vous pouvez expérimenter vous-même cet exemple. À savoir, nous parlons du fait que si vous ouvrez cet exemple dans le navigateur Safari, alors, lorsque la page se charge, les boutons auront l'aspect prévu. Mais si vous cliquez sur l'un des deux plus gros boutons, le bogue sort sa vilaine tête.





Pourquoi l'image SVG est-elle recadrée?



Chaque fois que l'événement se produitpaint, les images SVG utilisées dans les boutons plus grands ne s'affichent pas correctement. Ces images sont simplement recadrées. Cela peut se produire, sans raison apparente, lors du chargement de la page. Cela peut se produire même lorsque la fenêtre est redimensionnée. En général, l'erreur apparaît dans diverses situations.



Un aperçu du projet dans lequel l'erreur s'est produite



Je pense qu'en parlant de l'erreur, il serait bon de révéler des détails sur le projet et les conditions dans lesquelles il se produit.



  • Le projet utilise React (mais le lecteur de cet article n'a pas besoin de connaître React pour le comprendre).
  • Les images SVG sont importées dans le projet en tant que composants React et incorporées au HTML à l'aide de webpack.
  • . .
  • CSS.
  • , , HTML- <button>.
  • Safari ( 13 ).




Regardons l'erreur et voyons si nous pouvons faire des hypothèses sur ce qui se passe. Les raisons de telles erreurs ne se trouvent généralement pas quelque part en surface, par conséquent, face à de telles erreurs, on ne peut pas dire immédiatement avec confiance ce qui se passe. Lorsque nous faisons notre première tentative pour comprendre un problème, nous n'avons pas à nous efforcer d'identifier sa cause avec une précision de 100%. Nous étudierons l'erreur étape par étape, en formulant et en testant des hypothèses qui nous aideront à réduire la liste des causes possibles de ce qui se passe.



Formuler une hypothèse



À première vue, ce qui se passe ressemble à une erreur CSS. Peut-être que lorsque vous passez la souris sur le bouton, certains styles lui sont appliqués, ce qui rompt la mise en page. Peut-être que l'attribut d' overflowimage SVG est à blâmer . De plus, on a le sentiment que l'erreur se produit sans aucun système spécifique lorsque la page est redessinée pour diverses raisons (un événement paintlorsque la fenêtre du navigateur est redimensionnée, lorsque le pointeur de la souris est sur un bouton, quand on clique dessus, etc.).



Commençons par l'hypothèse la plus simple et la plus évidente. Disons que l'erreur est dans le CSS. Nous pouvons supposer qu'il existe un bogue dans le navigateur Safari qui entraîne une sortie SVG incorrecte lorsque des styles spécifiques sont appliqués à des éléments SVG. Par exemple, comme les styles utilisés pour créer des mises en page flexibles.



Nous venons de formuler une hypothèse. Notre prochaine étape consiste à effectuer un test qui confirmera ou infirmera cette hypothèse. Le résultat de chaque test nous donnera de nouvelles informations sur l'erreur et aidera à formuler les hypothèses suivantes.



Simplifier le problème



Nous utiliserons une stratégie de débogage appelée "simplifier le problème". Cela nous permettra de déterminer où l'erreur s'est produite. Dans une conférence sur l'informatique à l'Université Cornell, cette stratégie est décrite comme «une approche pour se débarrasser progressivement du code qui n'est pas lié à l'erreur».



En supposant que l'erreur se trouve dans le CSS, nous pouvons éventuellement trouver la cause de l'erreur ou exclure le CSS de l'équation, ce qui réduira le nombre de causes possibles de l'erreur et réduira la complexité du problème.



Testons notre hypothèse. Essayons de le confirmer. Dans ce cas, si vous désactivez temporairement tous les styles non standard de la page, l'erreur ne devrait plus apparaître.



Voici le code pour inclure la feuille de style appropriée:



import 'css/app.css';


J'ai créé ce projet CodePen pour démontrer la sortie d'éléments sans CSS . Dans React, les graphiques SVG sont importés dans un projet en tant que composant , puis le code correspondant est intégré au HTML à l'aide de webpack.





Vue du bouton normal



Si vous ouvrez le projet susmentionné dans Safari et cliquez sur l'un des gros boutons, il s'avère que l'erreur n'est partie nulle part. Cela se produit également lorsque la page est chargée, mais lorsque vous utilisez CodePen, vous devez cliquer sur le bouton pour déclencher une erreur.





L'erreur n'a pas disparu même lorsque CSS a été désactivé (Safari 13)



En conséquence, nous pouvons conclure que CSS n'a rien à voir avec cela. Cependant, nous pouvons faire attention au fait que dans de telles conditions, seuls deux des cinq boutons ne s'affichent pas correctement. Souvenons-nous de cela et passons à l'hypothèse suivante.



Isolation d'erreur



Notre hypothèse suivante est que Safari a un bogue lors du rendu des images SVG à l'intérieur des éléments HTML <button>. Puisque le problème survient lorsque les deux premiers boutons sont affichés, nous isolons le premier et voyons ce qui se passe.



Sarah Drazner dans ceLe matériau explique l'importance de l'isolation. Je recommande vivement de lire ce matériel à toute personne intéressée par plus de détails sur les outils de débogage et les différentes approches pour trouver des bogues. Voici une citation de ce matériel: «L'isolement est peut-être le principe de base le plus important du débogage. Le code qui fonctionne dans nos projets peut être dispersé dans différentes bibliothèques et frameworks. De nombreuses personnes peuvent être impliquées dans le travail sur les projets, et certains de ceux qui ont contribué au développement des projets n'y travaillent plus. Isoler le problème nous aide à couper lentement ce qui ne fait pas apparaître l'erreur. Cela permet, au final, de trouver la source du problème et de se concentrer dessus. "



L'isolation des pannes est souvent appelée « cas de test abrégé ».



J'ai déplacé le bouton sur une page vierge distincte (créé un test distinct pour cela). À savoir, ce projet CodePen a été créé pour étudier un bouton de manière isolée. Bien que nous ayons conclu que CSS n'est pas la cause du problème, nous devons laisser les styles désactivés jusqu'à ce que nous découvrions la véritable cause du problème. Cela nous permettra de simplifier au maximum le problème.





Une page avec seulement un bouton



Si vous ouvrez ce projet dans Safari, il s'avère que nous ne pouvons plus provoquer d'erreur. Même après avoir cliqué sur le bouton, l'image ne change pas. Mais cela ne peut pas être considéré comme une solution acceptable au problème. Cependant, le code de ce projet CodePen nous donne une excellente base pour créer un exemple minimal reproductible.



Exemple reproductible minimal



La principale différence entre les deux projets CodePen précédents est la combinaison de boutons. L'examen de toutes les combinaisons de boutons possibles nous permet de conclure que le problème ne se produit que lorsqu'un événement paintse produit pour une image SVG plus grande, à côté de laquelle une image SVG plus petite se trouve sur la page.



Sachant cela nous a permis de créer un exemple reproductible minimal qui nous a permis de reproduire l'erreur tout en nous débarrassant de tous les éléments inutiles. Grâce à un exemple minimal reproductible, nous pouvons étudier le problème plus en profondeur et mettre en évidence exactement la partie du code qui le cause.



Voici le projet CodePen en question.





Exemple reproductible minimal



Si nous ouvrons ce projet dans Safari et cliquons sur le bouton, nous rencontrons à nouveau un problème. Cela nous permettra de formuler une hypothèse selon laquelle les deux éléments SVG sont en quelque sorte en conflit l'un avec l'autre. Si vous superposez la deuxième image SVG au-dessus de la première, il s'avère que la taille de ce qui reste de l'arrière-plan de la première image correspond exactement à la taille de la plus petite image.





Un dessin dans lequel la deuxième image est placée au-dessus de la première, déformée par une erreur



Diviser pour régner



Nous avons pu reproduire le bug en utilisant le nombre minimum d'éléments représentés par une paire d'images SVG. Nous allons maintenant réduire davantage la portée du problème, en le réduisant au morceau spécifique de code SVG qui est à l'origine du problème. Si nous comprenons le code SVG en termes généraux et que nous voulons toujours trouver la source du problème, nous pouvons utiliser une stratégie de recherche dans un arbre binaire en utilisant une approche de division et de conquête. Voici un autre extrait de la conférencede Cornell University Computer Science: «Par exemple, vous pouvez commencer par examiner un gros morceau de code et placer le code de validation au milieu du morceau que vous examinez. Si une erreur ne se produit pas ici, cela signifie que sa source se trouve dans la seconde moitié du code. Sinon, sa source se trouve dans la première moitié du code. "



Lors de l'examen du code SVG, vous pouvez essayer de supprimer un élément <filter>de la description de la première image (et aussi <defs>, puisqu'il n'y a rien dans ce bloc de toute façon). Intéressons-nous au type de tâches que l'élément résout <filter>. Une bonne explication à cela peut être trouvée ici . À savoir, nous parlons de ce qui suit: «Pour appliquer des filtres aux images SVG, il existe un élément spécial appelé<filter>... Il ressemble essentiellement à des éléments conçus pour fonctionner avec des dégradés linéaires, des masques, des modèles et d'autres effets graphiques. L'élément n'est <filter>jamais affiché seul. Il n'est utilisé que comme quelque chose qui peut être référencé à l'aide d'un attribut filteren code SVG ou d'une fonction url()en CSS. "



Dans notre image SVG, un filtre est utilisé pour ajouter une petite ombre intérieure au bas de l'image. Après avoir supprimé le filtre du code de la première image, on attend que cette ombre disparaisse. Si après cela le problème persiste, alors nous pouvons conclure que quelque chose ne va pas avec l'autre code pour décrire l'élément SVG.J'ai



créé un autre projet CodePen afin de démontrer les résultats de ce test.





Conséquences de la suppression de l'élément <filter>



Le problème, comme vous pouvez le voir facilement, n'est allé nulle part. Et l'ombre intérieure continue de s'afficher même après la suppression du code de filtre. Mais maintenant, entre autres choses, le problème apparaît dans tous les navigateurs. Cela nous permet de conclure que l'erreur se trouve quelque part dans le reste du code de description du bouton. Si vous supprimez le resteidde<g filter="url(#filter0_ii)">, l'ombre disparaît. Que se passe t-il ici?



Examinons à nouveau la définition ci-dessus d'un élément<filter>et notons les mots suivants: «Un élément n'est<filter>jamais affiché seul. Il n'est utilisé que comme quelque chose qui peut être référencé à l' aide d'un attributfilterdans SVG. " (J'ai mis en évidence un fragment du texte.)



Donc, sachant cela, nous pouvons conclure que la déclaration de filtre de la deuxième image SVG est appliquée à la première image SVG, ce qui conduit à l'erreur.



Correction d'un bug



Nous savons maintenant que notre problème est lié aux éléments <filter>. Nous savons également que les deux images SVG ont cet élément, puisque le filtre est utilisé pour créer une ombre intérieure circulaire. Comparons le code de deux images SVG et voyons si nous pouvons expliquer l'erreur et la corriger.



J'ai simplifié le code des deux images afin que vous puissiez voir clairement ce qui s'y passe.



Voici le code de la première image SVG:



<svg width="46" height="46" viewBox="0 0 46 46">
  <g filter="url(#filter0_ii)">
    <!-- ... -->
  </g>
  <!-- ... -->
  <defs>
    <filter id="filter0_ii" x="0" y="0" width="46" height="46">
      <!-- ... -->
    </filter>
  </defs>
</svg>


Voici le code de la deuxième image:



<svg width="28" height="28" viewBox="0 0 28 28">
  <g filter="url(#filter0_ii)">
    <!-- ... -->
  </g>
  <!-- ... -->
  <defs>
    <filter id="filter0_ii" x="0" y="0" width="28" height="28">
      <!-- ... -->
    </filter>
  </defs>
</svg>


En analysant ces deux fragments, vous pouvez remarquer que id=filter0_iile même identifiant est utilisé dans la construction . Safari applique la dernière définition de filtre analysée par le navigateur aux éléments (dans ce cas, le filtre de la deuxième image). Cela conduit au fait que la première image est recadrée. Sa taille d'origine est 48px, et après l'application du filtre, un morceau en est découpé 26px. La propriété idDOM doit avoir une valeur unique. S'il y en a plusieurs identiques sur la page id, le navigateur ne peut pas déterminer lequel il doit utiliser. Et puisque la propriété est filterremplacée à l'occurrence de chaque événementpaint, alors, selon laquelle des définitions sera prête en premier (quelque chose comme une condition de concurrence se produit ici), l'erreur apparaît ou non.



Essayons d'attribuer des valeurs uniques iddans le code de chacune des images et voyons les résultats. Voici le projet CodePen correspondant.





L'attribution d'identifiants uniques a résolu le problème



Si vous ouvrez maintenant le projet dans Safari et cliquez sur le bouton, vous pouvez être sûr que nous avons résolu le problème en attribuant desidfiltresuniquesutilisés dans les images SVG. Si vous pensez au fait que le projet avait des valeurs non uniques pour un attribut commeid, cela conduira à la conclusion que le problème aurait dû apparaître dans tous les navigateurs, pas seulement dans Safari. Mais pour une raison quelconque, d'autres navigateurs (dont Chrome et Firefox) semblent avoir géré cette situation inhabituelle sans erreur. Cela pourrait être une coïncidence, cependant.



Résultat



C'était une autre aventure! Nous avons commencé à savoir seulement qu'il y avait une sorte d'erreur dans le projet, qui apparaît parfois, puis ne le fait pas, mais à la fin, nous avons parfaitement compris les raisons de ce qui se passait et avons résolu le problème. Déboguer le code de l'interface utilisateur et comprendre pourquoi les graphiques sont déformés peut être délicat si vous ne comprenez pas ce qui se passe. Heureusement pour nous, il existe des stratégies de débogage qui peuvent nous aider à trouver la cause première des erreurs les plus déroutantes.



Tout d'abord, nous avons simplifié le problème en formulant des hypothèses qui nous permettaient de supprimer du projet des composants qui n'étaient pas liés à l'erreur (styles, balisage, événements dynamiques, etc.). Après cela, nous avons isolé le balisage et trouvé un exemple minimal reproductible. Cela nous a permis de nous concentrer sur un petit morceau de code. Et nous avons finalement identifié le problème en utilisant une stratégie de division pour vaincre pour éliminer l'erreur.



Merci à tous ceux qui ont pris le temps de lire cet article. Mais avant de terminer la conversation, laissez-moi vous parler d'une autre stratégie de débogage mentionnée dans les conférences.L'Université de Cornell. Le fait est que dans le processus de travail, vous devez prendre des pauses, vous reposer et libérer votre tête de toute pensée: «Si le débogage prend trop de temps, le programmeur se fatigue. Il se peut que lui, étant dans un tel état, travaille en vain. Dans une situation comme celle-ci, cela vaut la peine de faire une pause et de tout jeter de la tête. Et après un certain temps, vous devriez essayer de regarder le problème d'un point de vue différent. "



Comment corrigez-vous les erreurs incompréhensibles?






All Articles