5 points par GN⁺ 2023-11-12 | 1 commentaires | Partager sur WhatsApp
  • Si vous maîtrisez les outils de développement du navigateur à un niveau intermédiaire ou avancé, vous pouvez étendre les points d’arrêt conditionnels au-delà de simples conditions d’arrêt pour en faire des outils de journalisation, de traçage, de modification d’état et de mesure du temps
  • En plaçant des API de console comme console.log, console.count, console.trace ou console.time dans un point d’arrêt ou dans le Watch Pane, vous pouvez enregistrer le flux d’exécution et l’état sans interrompre l’exécution
  • En combinant le nombre d’arguments d’une fonction, le temps, les valeurs CSS calculées, le nombre d’exécutions, l’échantillonnage aléatoire et un booléen global, vous pouvez contrôler beaucoup plus finement le moment où l’exécution s’arrête
  • Dans Chrome, monitor, debug, copy, $0, getEventListeners et monitorEvents permettent de manipuler rapidement depuis la console les appels de fonctions, le DOM, les événements et les opérations de presse-papiers
  • Sur les écrans où le DOM change en permanence sous l’effet de JS ou d’événements utilisateur, la mise en pause de JS, setTimeout(debugger), les instantanés DOM et la surveillance du focus sont particulièrement utiles pour le débogage HTML/CSS

Étendre les points d’arrêt conditionnels en outils de débogage

  • Dans un point d’arrêt conditionnel, vous pouvez mettre non seulement une condition vrai/faux, mais aussi une expression JavaScript avec des effets de bord

  • Cela permet d’utiliser les points d’arrêt de base comme des outils de journalisation, de traçage, de modification d’état ou de mesure du temps

  • Logpoints et tracepoints

    • Appeler console.log dans un point d’arrêt conditionnel le fait se comporter comme un logpoint, qui écrit une valeur dans la console sans interrompre l’exécution
    • Avec console.count, vous pouvez aussi compter combien de fois la ligne a été exécutée
    • Depuis la mise à jour de mai 2020, les principaux navigateurs prennent directement en charge les logpoints/tracepoints
  • Utiliser les API de console dans le Watch Pane

    • Vous pouvez aussi mettre des expressions comme console.log dans le Watch Pane
    • Chaque fois que l’application s’arrête dans le débogueur, console.table(localStorage) permet de prendre un instantané de localStorage
    • En combinant un point d’arrêt sur modification du DOM avec une expression Watch, vous pouvez enregistrer dans un tableau des instantanés après modification d’un sous-arbre DOM
    (window.doms = window.doms || []).push(document.documentElement.outerHTML)
    
    • Les points d’arrêt sur modification du DOM ne peuvent pas être configurés pour ne pas interrompre l’exécution
  • Tracer la pile d’appels

    • Pour trouver un problème où une fonction affiche un spinner de chargement et une autre le masque, mais où seul l’appel d’affichage subsiste, vous pouvez mettre console.trace dans un point d’arrêt conditionnel
    • En cliquant sur l’appelant dans la dernière stack trace de la fonction d’affichage, vous pouvez accéder à l’emplacement de cet appel
  • Modifier le comportement d’un programme en cours d’exécution

    • En écrasant les arguments d’une fonction dans l’expression d’un point d’arrêt conditionnel, vous pouvez modifier à la volée le comportement du programme dans le navigateur
    • Par exemple, vous pouvez modifier la valeur id de la fonction getPerson
    • Une expression évaluée à vrai comme id=1 arrête le débogueur ; pour éviter l’arrêt, vous pouvez ajouter , false
  • Mesure rapide des performances

    • Comme le temps d’évaluation du point d’arrêt conditionnel s’y mêle, cela ne convient pas au profilage de performance précis
    • Quand vous avez besoin d’une mesure rapide, vous pouvez mettre console.time('label') dans la condition au point de départ et console.timeEnd('label') dans la condition au point d’arrivée afin d’enregistrer le temps d’exécution dans la console

Rendre les conditions d’arrêt plus précises

  • Utiliser le nombre d’arguments d’une fonction

    • Pour s’arrêter uniquement lorsque la fonction courante est appelée avec 3 arguments, vous pouvez utiliser la condition suivante
    arguments.callee.length === 3
    
    • C’est utile dans les fonctions surchargées avec des arguments optionnels
    • Pour s’arrêter uniquement lorsque le nombre d’arguments définis et le nombre d’arguments réellement passés diffèrent, vous pouvez utiliser la condition suivante
    (arguments.callee.length) != arguments.length
    
    • Cela aide à trouver des bugs dans les sites d’appel de la fonction
  • Conditions basées sur le temps

    • Pour ne pas s’arrêter avant que 5 secondes se soient écoulées après le chargement de la page, vous pouvez utiliser la condition suivante
    performance.now() > 5000
    
    • C’est adapté lorsque vous ne vous intéressez au point d’arrêt qu’après le chargement initial de la page
    • Pour que l’exécution ne s’arrête pas pendant les 5 secondes suivant la définition du point d’arrêt, puis s’arrête ensuite, vous pouvez stocker l’heure de référence dans une variable globale
    window.baseline = window.baseline || Date.now(),
    (Date.now() - window.baseline) > 5000
    
    • Vous pouvez redémarrer le compteur depuis la console avec window.baseline = Date.now()
  • Conditions basées sur l’état CSS

    • Vous pouvez vous arrêter en prenant comme condition une valeur CSS calculée
    • Par exemple, pour s’arrêter uniquement lorsque la couleur d’arrière-plan de document.body est rouge, vous pouvez utiliser la condition suivante
    window.getComputedStyle(document.body).backgroundColor === "rgb(255,0,0)"
    
  • Nombre d’exécutions et échantillonnage

    • Vous pouvez incrémenter un compteur à chaque exécution d’une ligne pour ne vous arrêter qu’une fois sur deux
    window.counter = (window.counter || 0) + 1, window.counter % 2 === 0
    
    • Pour ne vous arrêter qu’une fois sur 10 avec un échantillonnage aléatoire, vous pouvez utiliser la condition suivante
    Math.random() < 0.1
    
  • Ne jamais s’arrêter à un emplacement précis

    • Dans Chrome, en faisant un clic droit sur la gouttière et en sélectionnant “Never Pause Here”, un point d’arrêt conditionnel toujours à false est créé, ce qui empêche l’arrêt sur cette ligne
    • Cela peut servir à exclure une ligne précise d’un point d’arrêt XHR ou à ignorer une exception spécifique
  • Attribution automatique d’ID d’instance

    • Vous pouvez placer un point d’arrêt conditionnel dans le constructeur d’une classe pour enregistrer chaque instance dans un tableau
    (window.instances = window.instances || []).push(this)
    
    • L’ID unique d’une instance donnée peut être vérifié via son index dans le tableau
    window.instances.indexOf(instance)
    
  • Bascule globale des points d’arrêt

    • Vous pouvez contrôler un ou plusieurs points d’arrêt conditionnels avec un booléen global
    • Il est possible de le modifier directement depuis la console, depuis un autre point d’arrêt, ou via un timer
    window.enableBreakpoints = true
    setTimeout(() => (window.enableBreakpoints = true), 5000)
    

Tracer les appels de fonctions et de classes

  • Surveiller les appels de méthodes de classe

    • La méthode en ligne de commande monitor de Chrome permet de tracer facilement les appels de méthodes de classe
    • Si vous voulez voir les appels de méthodes pour toutes les instances de la classe Dog, vous pouvez appliquer monitor à toutes les propriétés du prototype
    var p = Dog.prototype;
    Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));
    
    • La console affiche des informations d’appel comme function bark called with arguments: 2
    • Si vous voulez arrêter l’exécution lors de l’appel au lieu de seulement journaliser, vous pouvez utiliser debug à la place de monitor
  • Tracer une classe depuis une instance donnée

    • Si vous ne connaissez pas le nom de la classe mais disposez d’une instance, vous pouvez la surveiller de la même manière via le prototype de son constructeur
    var p = instance.constructor.prototype;
    Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));
    
    • C’est utile pour créer une fonction qui agit sur une instance arbitraire sans être liée à une classe précise
  • Déboguer juste avant un appel de fonction depuis la console

    • Avant d’appeler depuis la console la fonction à déboguer, exécutez debugger, puis faites Step into sur l’appel suivant pour entrer dans l’implémentation de la fonction
    debugger; fn(1);
    
    • C’est utile lorsqu’il est fastidieux de trouver l’emplacement de définition de la fonction, ou lorsque fn est liée dynamiquement et que son emplacement source est inconnu
    • Dans Chrome, vous pouvez aussi appeler debug(fn) pour vous arrêter à l’intérieur de la fonction chaque fois que fn est appelée

Débogage des changements d’URL et des lectures de propriétés

  • S’arrêter avant le routage d’une SPA

    • Pour s’arrêter juste avant qu’une application monopage modifie l’URL, vous pouvez associer une fonction contenant debugger à history.pushState, history.replaceState, window.onhashchange et window.onpopstate
    const dbg = () => {
      debugger;
    };
    history.pushState = dbg;
    history.replaceState = dbg;
    window.onhashchange = dbg;
    window.onpopstate = dbg;
    
    • Cette version de dbg n’inclut pas d’implémentation permettant de s’arrêter sans casser la navigation
    • Si window.location.replace ou window.location.assign est appelé directement, la page est déchargée juste après l’affectation, donc cette méthode ne permet pas de le gérer
    • Dans Chrome, vous pouvez placer debug sur les méthodes concernées pour voir la source de la redirection et l’état à ce moment-là
    debug(window.location.replace);
    debug(window.location.assign);
    
  • Trouver le point de lecture d’une propriété

    • Si vous voulez savoir à quel moment une propriété précise d’un objet est lue, vous pouvez mettre debugger dans un getter
    { get configOption() { debugger; return true; } }
    
    • C’est utile pour vérifier quel code utilise une option de configuration et comment
    • Vous pouvez l’appliquer en modifiant la source originale ou en utilisant un point d’arrêt conditionnel

Copier des données depuis la console

  • L’API de console copy() permet de copier des informations du navigateur dans le presse-papiers sans troncature des chaînes
  • Vous pouvez l’utiliser pour copier l’instantané DOM courant, les métadonnées de ressources comme les images, un gros blob JSON ou un dump de localStorage
copy(document.documentElement.outerHTML)
copy(performance.getEntriesByType("resource"))
copy(JSON.parse(blob))
copy(localStorage)

Débogage HTML/CSS

  • Inspecter le DOM pendant que l’exécution JS est en pause

    • Dans l’inspecteur DOM, sous Chrome/Windows, appuyer sur ctrl+\ permet de mettre en pause l’exécution JS à tout moment
    • Même lorsque JS modifie le DOM ou que des événements comme mouseover modifient le DOM, vous pouvez examiner de façon fiable l’instantané DOM courant
  • Inspecter des éléments qui disparaissent

    • Pour inspecter un élément DOM conditionnel qui disparaît quand vous déplacez la souris, vous pouvez lancer le débogueur avec un timer de 5 secondes dans la console
    setTimeout(function() { debugger; }, 5000);
    
    • Après avoir affiché l’UI pendant 5 secondes, l’exécution JS s’arrête, l’élément ne disparaît donc plus, et vous pouvez passer aux outils de développement pour l’inspecter
    • Pendant que l’exécution JS est en pause, vous pouvez inspecter des éléments, modifier le CSS et exécuter des commandes dans la console JS
    • C’est efficace pour inspecter un DOM qui dépend de la position du curseur ou du focus
  • Enregistrer des instantanés DOM

    • Pour copier le DOM courant, vous pouvez utiliser la commande suivante
    copy(document.documentElement.outerHTML);
    
    • Pour enregistrer la chaîne DOM dans un tableau chaque seconde, vous pouvez utiliser setInterval comme suit
    doms = [];
    setInterval(() => {
      const domStr = document.documentElement.outerHTML;
      doms.push(domStr);
    }, 1000);
    
    • Vous pouvez aussi l’afficher en continu dans la console sans l’enregistrer dans un tableau
  • Surveiller l’élément focalisé

    • Vous pouvez comparer périodiquement document.activeElement et afficher les changements de focus dans la console
    (function () {
      let last = document.activeElement;
      setInterval(() => {
        if (document.activeElement !== last) {
          last = document.activeElement;
          console.log("Focus changed to: ", last);
        }
      }, 100);
    })();
    
  • Trouver les éléments affichés en gras

    • Vous pouvez trouver tous les éléments dont le fontWeight calculé est "bold" ou "700"
    const isBold = (e) => {
      let w = window.getComputedStyle(e).fontWeight;
      return w === "bold" || w === "700";
    };
    Array.from(document.querySelectorAll("*")).filter(isBold);
    
    • Pour ne chercher que dans les descendants de l’élément actuellement sélectionné dans l’inspecteur, vous pouvez effectuer la recherche à partir de $0
    Array.from($0.querySelectorAll("*")).filter(isBold);
    
  • Manipuler l’élément sélectionné et les événements

    • Dans la console, $0 est une référence automatique vers l’élément actuellement sélectionné dans l’Element Inspector
    • Dans Chrome et Edge, vous pouvez accéder aux éléments inspectés précédemment avec $1, puis $2, etc.
    • Dans Chrome, vous pouvez vérifier les écouteurs d’événements de l’élément actuellement sélectionné
    getEventListeners($0)
    
    • Pour déboguer tous les événements de l’élément sélectionné, vous pouvez utiliser monitorEvents($0)
    • Si vous voulez ne voir qu’un groupe d’événements spécifique, vous pouvez passer un tableau
    monitorEvents($0, ["control", "key"])
    

1 commentaires

 
GN⁺ 2023-11-12
Avis sur Hacker News
  • Au cours des dernières décennies, les outils de débogage intégrés aux navigateurs ont vraiment énormément progressé.
    En tant que personne qui utilise JS depuis longtemps, je suis profondément reconnaissant envers celles et ceux qui ont rendu le débogage de code dans le navigateur aussi intuitif.
    Quand je passe à d’autres domaines du développement, comme le backend ou d’autres langages, cet écosystème d’outils de débogage que les navigateurs modernes fournissent par défaut me manque.

    • J’aimerais aussi applaudir les personnes qui ont créé l’extension Firebug, qui a véritablement fait passer les outils de développement des navigateurs à un autre niveau.
    • Je me demande ce qui, précisément, vous semble si particulier dans l’expérience de débogage JS.
      J’ai beaucoup débogué en Python, Java et C++ pendant des années, et je n’ai jamais eu le sentiment de manquer d’outils.
      Bien sûr, j’ai vu pas mal de gens qui ne connaissaient pas les options disponibles dans ce domaine.
    • Ce n’est pas parfait, mais c’est largement mieux que l’expérience de débogage de Node.js avec VS Code que poussait un collègue développeur.
      Entre cet enfer et la console, je choisis la console.
  • setTimeout(function() { debugger; }, 5000); est malin.
    Au final, la seule façon pour le débogueur de Chrome de venir à bout de cette pile récursive de tortues où il se débogue lui-même, c’est l’instruction debugger.
    Le célèbre ver Myspace Sammy, sam.pl, utilisait aussi des pièges de débogage pour empêcher les visiteurs de décortiquer la page d’accueil HTML obfusquée.

    • C’est l’un de mes bookmarklets habituels : https://darekkay.com/blog/debugging-dynamic-content/
    • C’est ce qui se produit quand un site web bloque le panneau latéral Console/DevTools ouvert.
      Il fait tourner la boucle principale en insérant un peu partout des instructions debugger; agaçantes, exécutées à 30 FPS / toutes les 32 ms, ce qui rend DevTools inutilisable.
      Parce qu’il n’existe aucun moyen « d’ignorer » l’instruction debugger.
    • Si vous ne voulez pas attendre 5 secondes, utilisez le raccourci de pause de l’exécution du script cmd-\.
      La page s’arrête sur la ligne JS en cours d’exécution, et vous pouvez inspecter l’élément problématique comme d’habitude.
    • J’aimerais que les éditeurs de navigateurs fassent de l’ignorance des instructions debugger une option à activer explicitement dans DevTools, et qu’elle soit activée par défaut.
      La plupart des usages légitimes de l’instruction debugger, quand on travaille sur son propre code, peuvent être remplacés par un simple point d’arrêt.
    • Attendez, c’est tout ? C’est trop simple, mais c’est excellent.
  • L’absence de queryObjects saute aux yeux.
    C’est une API assez folle qui renvoie la liste de tous les objets créés avec un constructeur donné.
    Par exemple, queryObjects(Function) permet d’obtenir la liste de toutes les fonctions présentes dans le tas.
    Elle renvoie même les fonctions « private » à l’intérieur d’un module.

    • Franchement, on pourrait comprendre qu’ils ne veuillent pas en faire la publicité.
      C’est vraiment une fonctionnalité dingue, et probablement réservée à Chromium.
  • Je n’arrive tout simplement pas à utiliser correctement les variables surveillées.
    Les règles de portée et de mise à jour sont trop opaques ; on pourrait croire que seules les variables globales peuvent être surveillées, mais même là ça ne fonctionne pas comme prévu, si bien qu’en test je finis par inonder les logs de valeurs.
    Cela fait des années que je pense qu’il faudrait intégrer à la console une UI à la Data.gui [1], pour pouvoir voir les variables et les valeurs de configuration pendant les tests.
    On peut la voir fonctionner dans ce CodePen [2].

    1. https://github.com/dataarts/dat.gui
    2. https://codepen.io/russellbeattie/full/kGxaqM
    • J’ai ressenti la même frustration.
      En théorie, les navigateurs ont beaucoup d’excellentes fonctionnalités de débogage, mais en pratique elles ne semblent pas fonctionner de façon fiable.
      Je n’arrive même pas à faire en sorte que tous les points d’arrêt se déclenchent de manière stable.
      Quand le code est non transformé, ça va ; mais dès qu’il est bundlé, même sans être minifié, beaucoup de fonctions du débogueur semblent se casser.
      Comme je ne suis pas ingénieur frontend, je fais peut-être quelque chose de travers.
    • Il vaut mieux essayer de désactiver les source maps dans le débogueur.
      C’est probablement la cause de la frustration, et je ne comprends toujours pas pourquoi elles sont activées par défaut alors que l’expérience de débogage actif avec elles est aussi mauvaise.
    • Dans le panneau latéral de Chrome, les variables minifiées apparaissent avec leur nom correct, mais dans le watchpoint elles provoquent une erreur indiquant qu’elles ne sont pas définies, ce qui est agaçant.
  • Ce serait bien d’avoir un moyen d’accéder aux variables locales d’une IIFE sans interrompre l’exécution du code dans le scope de l’IIFE
    Existe-t-il un moyen de convaincre le débogueur de faire ça ?

    • Une IIFE est une expression de fonction immédiatement invoquée
      https://en.m.wikipedia.org/wiki/Immediately_invoked_function...
    • Si vous êtes entré dans un point d’arrêt dans une fonction appelée par l’IIFE, il suffit de remonter la pile
      Si vous êtes complètement en dehors de l’IIFE, je pense que c’est impossible
      La variable que vous cherchez peut ne pas exister en mémoire avant ou après l’exécution
      JavaScript n’a pas de variables statiques comme d’autres langages, donc à chaque appel de l’IIFE, les variables internes sont jetées
      JavaScript a un modèle monothread assez strict, sauf si vous utilisez des choses comme les web workers ; donc, à moins de créer volontairement une condition de concurrence avec des appels async/await, l’idée même que ce genre de variable reste en mémoire hors du scope de l’IIFE tient difficilement
      Si vous abusez de var pour initialiser une variable hors du scope de l’IIFE, vous pouvez voir la valeur créée par le dernier appel de l’IIFE
    • Il y a environ 12 ans, j’ai créé un outil qui fait ce genre de chose
      Il faudrait quelques ajustements, mais le rapport effort/bénéfice est élevé
      https://github.com/kristopolous/_inject
      On peut se promener dans presque n’importe quel contexte de fonction à un moment arbitraire et voir ce qui s’est passé
      Le principe consiste à détourner les compteurs de références pour empêcher la destruction du contexte
      À l’époque où je faisais beaucoup de JS côté client, c’était vraiment utile
      La killer app bien faite pour ça serait une version qui ouvre un REPL dans n’importe quel contexte
      Même dans son état actuel, il faut pas mal d’expérience pour bien l’utiliser
    • Ce que vous cherchez, ce sont des logpoints ?
      https://firefox-source-docs.mozilla.org/devtools-user/debugg...
      Édit : je viens de me rendre compte que c’est littéralement le premier sujet abordé dans l’article lié
    • Dans Chrome, vous pouvez inspecter la closure décrite dans https://news.ycombinator.com/item?id=38226743#38231705 via le panneau Watch, puis regarder la pseudo-propriété [[Scopes]]
      Dans Firefox, il ne semble pas y avoir de méthode
  • Par souci d’exhaustivité, on peut recommander Werkzeug
    Je l’utilise pour du développement backend Django et c’est extrêmement utile
    Il permet d’avoir un shell « PDB » dans le navigateur, à tout moment et à n’importe quel endroit où une exception se produit

    • C’est facile : il suffit de mettre 1/0 à l’endroit où vous voulez faire apparaître Werkzeug dans le navigateur
    • J’aimerais avoir cette expérience partout, y compris avec nose.js et dans le navigateur
      C’est très simple et intuitif
    • A-t-on encore besoin de django-extension/runserver_plus ?
  • Une astuce que j’utilise toujours consiste à chercher dans les scripts chargés à partir d’une chaîne de l’UI pour déboguer
    Allez dans le panneau Network, lancez l’enregistrement des requêtes réseau, ouvrez la barre latérale gauche, puis cherchez le code ou la chaîne d’interface que vous voulez trouver
    En général, on la retrouve dans un étrange fichier de chunk JS bundlé ; en cliquant sur le résultat, la requête réseau de ce fichier s’ouvre
    Faites un clic droit n’importe où dans ce fichier et choisissez une option du genre « Open in Sources » pour passer au débogueur
    Il ne reste plus qu’à placer une instruction debugger, et les source maps seront probablement chargées au passage

  • En tant que programmeur Python/Elixir depuis longtemps, j’ai beaucoup utilisé pdb.set_trace() et IEx.pry()
    J’ai récemment hérité d’un backend NodeJS très sale, et je m’arrache les cheveux qu’il me reste faute d’outils de débogage corrects
    Je suis revenu au débogage à coups de console.log, mais j’ai l’impression d’être un homme des cavernes
    J’ai du mal à croire qu’un écosystème aussi populaire n’ait pas de vrai REPL de débogage ; quelqu’un peut m’indiquer vers quoi me tourner ?

    • Il y en a un
      Utilisez node --inspect-brk, puis connectez-vous avec VS Code ou Chrome DevTools
      La partie délicate est de savoir si des outils de build, comme une transpilation TypeScript, se trouvent avant la commande nodejs
      Si vous lancez simplement node, c’est assez simple
      Le REPL de débogage, c’est la console JavaScript elle-même
    • Avec les outils de développement de VSCode, on peut déboguer NodeJS de manière assez solide
      En l’ajoutant comme option de débogage, vous pouvez poser des points d’arrêt et avancer pas à pas dans les fonctions
      Comme la plupart des choses sont encapsulées dans des objets, JavaScript se débogue plutôt bien
    • Comme d’autres l’ont dit, ce que vous voulez, c’est --inspect ou --inspect-brk
      Dans VSCode, vous pouvez aussi utiliser la commande « open JavaScript debug terminal »
      Elle ouvre un terminal où n’importe quelle commande node démarre automatiquement avec le débogueur attaché
    • Ce ne sera peut-être pas suffisant, mais je viens de découvrir python snoop
      https://pypi.org/project/snoop/
      C’est un décorateur pour du traçage approfondi
    • Ce qui est appréciable dans le REPL d’Elixir, et qui ressemble à celui d’Erlang, c’est la possibilité de se connecter à un processus en cours d’exécution
      Je n’ai pas vu ce type de fonctionnalité dans l’écosystème Node JS
  • Dans la section « Debugging Property Reads », je me demande comment on utilise un point d’arrêt conditionnel pour transformer {configOption: true} en {get configOption() { debugger; return true; }}

    • Ce n’est pas dans la partie sous le titre sur les points d’arrêt conditionnels
      Il suffit de remplacer la valeur par un getter dans la console ; si vous avez les droits d’écriture, vous pouvez aussi la modifier directement dans le code source
  • C’est quelque chose qu’il faudrait vraiment apprendre, mais je me demande s’il existe des livres dédiés ou des ressources d’apprentissage
    Ou bien n’y a-t-il pas d’autre choix que de creuser sans fin le développement web et le frontend ?

    • Je recommande vraiment de suivre la série “What’s new in DevTools” de l’équipe Chrome
      Quand on est en plein travail, cliquer sur un lien pour lire des notes de version peut ne pas être très tentant, mais parcourir celles d’une nouvelle version pendant seulement 5 minutes en vaut largement la peine
      Il y a aussi des vidéos de quelques minutes, faciles à assimiler, qui permettent d’avoir un aperçu rapide
      L’objectif est de présenter les nouvelles fonctionnalités, mais d’après mon expérience, cela aide aussi souvent à comprendre les limites actuelles des outils