.comment-link {margin-left:.6em;}

5h du matin

15 septembre 2008

Stack Overflow en beta publique



Jeff Atwood et Joel Spolsky nous dévoilent enfin leur projet commun dont ils parlent depuis plusieurs mois déjà: Stack Overflow.

Il s'agit d'un site pour développeurs en quête de réponses. S'ils n'y trouveront pas nécessairement le bonheur et l'amour, ils y trouveront en tout cas d'ores et déjà un grand nombre de questions techniques auxquelles d'autres développeurs ont répondu, comme par exemple "Comment itérer en C# sur un result set après une requête SQL?" ou "Comment intégrer les tests unitaires dans un projet existant?"

Le principe est simple: n'importe qui peut poser une question, et n'importe qui peut y répondre. Même sans avoir créé de compte! Les utilisateurs inscrits peuvent ensuite voter pour une question ou une réponse, afin de faire remonter les meilleurs contenus. Les utilisateurs enregistrés gagnent des points de réputation à chaque fois que l'une de leurs questions ou réponses est votée vers le haut, et passé un certain seuil de réputation, l'utilisateur peut modifier les questions et les réponses, ce qui permet de corriger les réponses au fur et à mesure qu'elles deviennent obsolètes (ce qui peut aller vite).

J'ai eu la possibilité de participer à la beta privée du site depuis plusieurs semaines, et je dois dire que le site fonctionne très bien. Développé par Jeff Atwood et son équipe, le site est réactif et très simple à utiliser. Et surtout, ça marche! On pose une question, et on voit apparaître les réponses dès les premières secondes. Pas de débat (le site est construit de telle sorte à le décourager), et si la question a été posée et que la réponse a été fournie, on les trouve instantanément. C'est véritablement impressionnant.

Alors développeurs anglophones, allez faire un tour sur Stack Overflow, et poser vos question - ça en vaut la peine. Et si cela ne vous dit rien, et que votre anglais est correct, écoutez tout de même le podcast entre Joel et Jeff, vous apprendrez plein de choses.

02 juillet 2008

Quand Joel change d'avis sur les interfaces graphiques

Joel Spolsky, de JoelOnSoftware, vient de publier un tout petit billet qui dit en substance: il ne faut ni cacher, ni désactiver les entrées d'un menu lorsque l'action correspondante ne peut être effectuée.

Le fait de cacher des éléments d'une interface est généralement considéré comme une mauvaise pratique. Il m'arrive souvent de chercher, dans des logiciels complexes comme OpenOffice ou Live, où diable cette fonctionnalité qui avait l'air si prometteuse à bien pu se planquer. C'est déjà suffisamment frustrant de naviguer dans les menus alors que j'ai du travail à faire, sans qu'en plus on m'enlève toute chance de retrouver ladite fonctionnalité parce que la commande n'est même plus là!

Jusque là, je suis d'accord avec Spolsky. Mais voyons ce qu'il nous recommande à la place:

Instead, leave the menu item enabled. If there's some reason you can't complete the action, the menu item can display a message telling the user why.


Sous quelle forme? Info-bulle? Boîte de message? Barre d'information en bas de la fenêtre, à l'autre bout du monde par rapport à l'endroit où l'utilisateur regarde?

Tout de suite, je me suis souvenu d'un essai qui m'avait marqué, où Spolsky assénait avec assurance que les utilisateurs ne lisent pas:
This may sound a little harsh, but you'll see, when you do usability tests, that there are quite a few users who simply do not read words that you put on the screen. If you pop up an error box of any sort, they simply will not read it. This may be disconcerting to you as a programmer, because you imagine yourself as conducting a dialog with the user. Hey, user! You can't open that file, we don't support that file format! Still, experience shows that the more words you put on that dialog box, the fewer people will actually read it.

Mettons que la commande "coller" ne peut pas être exécutée à un moment donné. Comment expliquer à l'utilisateur qu'il ne peut pas coller dans Paint .NET les 26 colonnes de nombres qu'il vient de copier dans Excel de manière à ce que 1) il lisent l'explication, et 2) il la comprenne?

Moi, pour ma part, je ne lis pas les messages. Et comme je suis un utilisateur comme un autre, je ne m'attends pas à des miracles sur ce front-là.

Pire encore, faut-il apprendre à l'utilisateur que chaque élément actif à l'écran peut lui envoyer un message à la figure plutôt que de produire le résultat attendu? Pas très bon pour l'encourager à explorer les fonctionnalités du logiciel...

D'autant que tout le monde grise des éléments des menus lorsqu'ils ne sont pas applicables. Or à la même époque, Spolsky nous vantait les mérites de la constance dans les interfaces graphiques:

If Microsoft is doing it in a popular program like Word, Excel, Windows, or Internet Explorer, then millions of people are going to think that it's right, or at least, fairly standard, and they are going to assume that your program works the same way. Even if you think (as the Netscape 6.0 engineers clearly do) that Alt+Left is not a good shortcut key for "Back", there are literally millions of people out there who will try to use Alt+Left to go back, and if you refuse to do it on some general religious principle that Bill Gates is the evil smurf arch-nemesis Gargamel, then you are just gratuitously ruining your program so that you can feel smug and self-satisfied, and your users will not thank you for it.

And don't be so sure it's not right. Microsoft spends more money on usability testing than you do, they keep detailed statistics based on millions of tech support phone calls, and there's a darn good chance that they did it that way because more people can figure out how to use it that way.

Alors au vu de tout ça, que penser de ce brusque revirement? Je respecte beaucoup les conseils de Spolsky. A tel point que je suis prêt à lui accorder le bénéfice du doute: est-ce une simple provocation? Peut-être nous donnera-t-il lui-même sa raison d'ici peu.

28 juin 2008

Tests unitaires - test ou conception?

Karl Seguin vient de publier un livre, Foundations of Programming, disponible en téléchargement. J'ai feuilleté un peu le PDF, et notamment le paragraphe intitulé "Why Wasn't I Unit Testing 3 Years Ago?" - littéralement "Pourquoi n'utilisais-je pas les tests unitaires il y a 3 ans?".

I had misconception about the goals of unit testing. As I’ve already said, unit testing does improve the quality of a system, but it’s really all about making it easier to change / maintain the system later on. Furthermore, if you go to the next logical step and adopt Test Driven Development, unit testing really becomes about design. To paraphrase Scott Bellware, TDD isn't about testing because you're not thinking as a tester when doing TDD – you’re thinking as a designer.

Lorsqu'on écrit un test unitaire, selon Karl Seguin, on ne porte pas la casquette du testeur, mais bien celle du concepteur. L'idée va à l'encontre de l'intuition, bien sûr: dans "test unitaire", il y a "test", et les tests c'est d'habitude plutôt la queue du train, à l'opposé de la conception.

Mais l'idée est non seulement rafraîchissante: elle sonne juste! Chez Ableton, nous n'utilisons pas les tests unitaires de manière systématique, mais cela ne m'empêche pas d'en avoir une petite collection dans mon coin, et je dois bien dire que celles de mes fonctionnalités qui sont testées de cette manière sont aussi matérialisées par les classes les plus indépendantes et les mieux conçues. En effet, comment tester une classe qui en requiert 14 autres? Et notez bien qu'on peut retirer ces bénéfices même sans pratiquer le TDD, c'est-à-dire sans écrire ses tests avant le code à tester.

Je n'ai pas lu le reste de l'ouvrage, mais je pense qu'il contient quelques de bons conseils. Amis de Shakespeare, jettez-y un coup d'œil.

24 juin 2008

Bien utiliser assert()

Début 2006, j'ai écrit un article intitulé Asserter n'est pas jouer, qui traitait des assertions en C++. Avec deux ans d'expérience en plus dans les pattes, je me rends compte qu'il est grand temps de peaufiner, voire de corriger mon avis sur la question. C'est d'autant plus utile que peu de développeurs savent vraiment utiliser les assertions. En effet, la gestion des erreurs fait partie, avec la programmation concurrente, de ces domaines de la programmation dont la difficulté est largement sous-estimée.

A quoi servent les assertions?

La définition classique, du genre "les assertions permettent de détecter lorsqu'une condition qui doit être vraie n'est pas vérifiée", est à peu près aussi utile qu'un GPS dans un tunnel: elle nous dit ce que sont les assertions, mais pas comment les utiliser. Les assertions servent à une seule chose: vous taper sur les doigts lorsque vous ou l'un de vos collègues introduisez un bug dans le code. Ni plus, ni moins.

Si votre code suppose quelque chose d'une variable, assertez, et faites comme si tout allait bien. La pire chose à faire, c'est d'essayer de retomber sur ses pieds en corrigeant des valeurs non valides ou en gérant des conditions bizarres "au cas où": non seulement vous aurez caché le bug, mais vous l'aurez même rendu plus difficile encore à détecter. C'est mal !

Bug, bug, bug. Une fois que ceci est rentré dans le crâne, on comprend deux choses:
  • On ne peut pas s'en servir pour valider une condition influencée par une donnée venant "des autres".
  • Supprimer les assertions dans le code de production est une aberration.

C'est qui, les autres ?

Les autres, par rapport à moi, ce sont tous ceux qui ne sont pas dans mon équipe de développement. Il peut s'agir aussi bien de l'utilisateur final, de l'autre côté de l'écran ou de mon API, que d'un fournisseur d'une classe ou d'une fonction que j'utilise moi-même. Pour moi, tout ce qui ne vient pas du code produit par mon équipe est suspect, mais n'est pas de mon ressort. On ne peut donc pas utiliser les assertions pour :
  • valider un paramètre d'entrée si la personne susceptible d'utiliser la fonction n'a pas accès au code de celle-ci,
  • vérifier que l'utilisateur, ce cloporte mal éduqué, a bien entré un nombre entre 1 et 10, comme c'est marqué en gras avec du texte rouge qui clignote à deux pixels de la zone de saisie,
  • vérifier qu'un fichier, si important soit-il pour l'application, est bien présent et contient des données valides,
  • vérifier que malloc() renvoie bien un pointeur non nul (notez que dans Asserter n'est pas jouer, j'avais testé la valeur de retour de new, qui d'après le standard doit lancer une exception si l'allocation échoue, mais j'utilisais à l'époque Visual C++ 6.0 qui ne s'y conformait pas sur ce point-là)
  • répandre la paix sur terre
Par contre, pour toutes les conditions d'erreur où il est possible d'aller trouver le fautif dans un bureau avoisinant, l'assertion est le meilleur outil. Si en plus, en cas d'assertion qui échoue, vous en profitez pour lui verser du Tabasco dans son café, le respect pour les assertions montera en flèche dans votre équipe.

Pourquoi faut-il laisser les assertions dans le code de production ?

Par code de production, j'entends la version compilée en mode "release". Toutes les invocations de la macro assert() standard disparaissent comme par enchantement lorsque DEBUG n'est pas défini. Le raisonnement est qu'une fois le code bien testé, on peut se passer de ces vérifications et gagner ainsi en performance.

C'est très très mal !

C'est vrai, il faut du temps pour accepter que que son code ne sera jamais totalement, complètement testé. C'est cette fameuse humilité à laquelle Jeff Atwood fait référence dans son blog Coding Horror. Mais des bugs, nous en laisserons toujours derrière nous. Du coup, la question change: il ne s'agit plus de savoir quand l'application sera complètement débuggée, mais plutôt de savoir comment elle doit réagir en présence d'un bug. Les assertions servent justement à ça.

La bonne réponse, aussi désagréable soit-elle, c'est de tuer l'application le plus vite possible. C'est le rôle de l'assertion: après avoir détecté un bug, elle doit planter l'application en exécutant un minimum de code. La solution est dramatique, mais ne pas le faire peut être pire encore. Vous pouvez par exemple corrompre des données. C'est le pêché ultime dans le mode du logiciel, qui vous apporte malédiction à vous et votre descendance sur sept générations. Mieux vaut planter avec panache que de cacher une erreur.

Toujours pas convaincu? Imaginez que vous programmez un système médical de respiration assistée. Les petits systèmes embarqués comme ça ont souvent un mécanisme qui permet de détecter que l'application s'est terminée et la redémarrent aussi sec. Mettons qu'au bout de 24h de fonctionnement, l'une de vos assertions détecte une erreur dans la fonction qui détecte les seuils de débit pour le déclenchement. Vous ne voulez surtout pas asphyxier votre pauvre patient! En tuant le programme, votre assertion lui permettra de redémarrer dans un état sain.

Il n'est d'ailleurs pas souhaitable d'implémenter des assertions sous forme d'exception. Même si celle-ci remonte jusqu'au main(), elle déclenchera sur son chemin une flopée de destructions d'objets. Or votre programme à ce moment-là, il faut bien le dire, il est bien pourri, et même les destructeurs peuvent faire des bêtises! Moins on exécute de code, mieux c'est. Votre nouvelle meilleure amie: abort().

Apprendre de ses erreurs

Mais s'il faut terminer au plus vite, c'est en revanche dommage de manquer l'occasion d'avoir plus d'info sur le problème. En implémentant votre propre mécanisme d'assertions, vous pourrez rassembler toute l'information qu'il vous faut, empaqueter tout cela à votre guise et vous le renvoyer directement chez vous (ou si vous êtes très fort, vous le faire envoyer par e-mail par l'utilisateur). En développement, profitez-en pour faire intervenir le débuggeur. En production, soyez sympa, évitez à votre utilisateur la surprise de voir votre application disparaître subitement, et montrez-lui au moins un petit message en lui expliquant bien que c'est de votre faute.

Et si vous êtes le malheureux développeur du ventilateur mécanique, déclenchez donc une petite alarme, histoire qu'une infirmière puisse vérifier l'état du patient que vous venez de laisser tomber...