KeiruaProd

I help my clients acquire new users and make more money with their web businesses. I have ten years of experience with SaaS projects. If that’s something you need help with, we should get in touch!
< Back to article list

3 éléments pour des librairies de meilleure qualité

Lors de la conférence JS.Everywhere à Paris, j’ai pu assister à une conférence très intéressante donnée par Rodney Rehm (@rodneyrehm) sur plein de mauvaises choses qui sont présentes un peu partout dans les API Javascript (et jusque dans JQuery). C’est intéressant, mais la portée du sujet est bien plus large: les nombreux conseils s’appliquent au reste du monde open source, et à l’écriture de librairies en général, peu importe le langage. Et c’est aussi valable ailleurs que pour le web.

Le problème de nombreuses API, c’est qu’elles sont diffusées librement, alors que leur code souffre de plusieurs défauts. Sans forcément regarder sous le capot, on se rend compte que de nombreuses choses ne vont dans les méthodes qui sont accessibles dans beaucoup de librairies libres. Il semblerait qu’à un moment, elles perdent de vue l’idée qu’elle peuvent être utilisées par beaucoup de monde, souvent avec des approche du code différentes. La conséquences, c’est qu’elles deviennent inutilement difficiles d’utilisation, les rendant ainsi pénibles et freinant leur adoption.

Pour pallier à celà, 3 idées sont à garder en tête lors du design d’une API : être simple, flexible, et robuste.

Être simple

Une API simple n’est pas forcément une API facile à utiliser. Mais c’est, du moins, une API qui est consistante dans son utilisation.
D’une méthode à une autre il ne devrait pas y avoir de surprises dans la notation des noms de fonctions. Si une methode s’écrit fooBar(), une autre ne devrait pas être écrite BarFoo().

L’ordre des paramètres doit également rester consistant, il ne doit pas changer d’une méthode à une autre. C’est souvent le cas en PHP :

<br /> bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )<br /> string strstr ( string $haystack , mixed $needle [, bool $before_needle = false ] )<br />

Les quelques développeurs à qui j’ai posé la question se posent régulièrement la question de l’ordre des paramètres pour ces fonctions, alors que cela n’a pas lieu d’être. Dans le cas de ces méthodes de recherche, l’argument donné en PHP, c’est que les fonctions qui gèrent des array ont needle en premier, et les méthodes qui gèrent des string haystack en premier. Si cette séparation était un bon argument, des générations entières de développeurs ne se seraient pas arrachés les cheveux sur la question.

Une fonction ou méthode en devrait pas pas prendre trop d’arguments. L’exemple donné par Rodney est plutôt parlant, on a tous un jour ou l’autre croisé une ligne qui ressemblait à ça :

<br /> event.initMouseEvent(<br /> "click", true, true, window,<br /> 123, 101, 202, 101, 202,<br /> true, false, false, false,<br /> 1, null);<br />

Vous avez une idée de ce que fait l’avant dernier paramètre, le 1 ? Si vous avez déjà programmé avec des API windows en C++ ou C#, vous savez quel genre d’horreur c’est. Les paramètres nommés, qui existent en Python, n’existent pas en javascript. Par contre, on peut résoudre le problème en fournissant un objet en argument, ce qui rend envisageable d’avoir :

<br /> event.initMouseEvent({<br /> type:"click",<br /> canBubble:true,<br /> cancelable:true,<br /> view:window,<br /> detail:123,<br /> screenX:101,<br /> screenY:202,<br /> clientX:101,<br /> clientY:202,<br /> ctrlKey:true,<br /> altKey:false,<br /> shiftKey:false,<br /> metaKey:false,<br /> button:1,<br /> relatedTarget:null});<br />

De cette manière, on connait au moins le nom du paramètre. Quand on revient quelques semaines plus tard dessus, ou quand le code est repris ou utilisé par quelqu’un d’autre (ça arrive assez souvent avec une librairie…), on s’arrache moins les cheveux à comprendre ce qui est fait.

Être flexible

Le développeur d’une API ne peut pas penser à tous les cas d’utilisation de son outil. D’une part car il ne pense pas à tout, d’autre part car certaines situations peuvent être en conflit avec d’autres et qu’il ne peut pas tout gérer. En fait, ce n’est de toutes façons pas son rôle.

Être flexible, c’est donc rendre son API extensible pour que chacun puisse y trouver son compte. C’est un peu le fameux principe Open/Closed de la programmation objet : les fonctionnalités d’une classe doit pouvoir être étendues, mais pas modifiées (open for extension, closed for modification).

Pas mal de design patterns existent pour cela. En JS, le plus simple c’est de penser à fournir des callback pour surcharger le comportement par défaut. Comme la fonction ajax() de jQuery, qui permet de surcharger le comportement lorsque l’envoi est terminé :

<br /> $.ajax({<br /> url: "test.html",<br /> context: document.body<br /> }).done(function() {<br /> $(this).addClass("done");<br /> });<br />

Être robuste

Une API robuste, c’est une API qui gère correctement les erreurs. Les erreurs que peut avoir une API viennent, pour beaucoup, des paramètres qui lui sont fournis.

Une API doit pouvoir traiter des types ou des valeurs non prévues. Ca n’est pas un problème qu’elle plante, mais elle ne doit pas le faire silencieusement, car c’est un cauchemar à débugger : on a en général confiance dans le code des librairies tierces, et si elles ne lèvent pas d’erreurs, au premier abord on ne va pas suspecter que l’erreur vient du code tiers.

Bref si une API n’est pas capable de gérer une situation, qu’elle ne s’arrête pas à un simple return au début. Qu’elle fasse au moins un log () ou autre afin de prévenir le malheureux développeur d’où vient l’erreur. Le temps perdu peut être immense.
Être robuste, c’est aussi savoir gérer de nombreux cas. Mais il ne faut pas chercher à gérer à être trop intelligent. Un exemple avec jQuery.toggle, qui permet d’afficher ou masquer un élément :

<br /> .toggle( [duration] [, callback] )<br /> .toggle( [duration] [, easing] [, callback] )<br /> .toggle( showOrHide )<br />

Grâce à ces différentes signatures, on peut entre autre écrire :

<br /> $("#plop").toggle(400)<br /> $("#plop").toggle("fast");<br /> $("#plop").toggle()<br /> $("#plop").toggle(1)<br /> $("#plop").toggle(true)<br />

Les 2 premiers sont compréhensibles : on affiche ou cache un élément à la vitesse donnée. Pour le 3ème, là comme ça on ne sait pas trop quelle est la vitesse utilisée. Pour le 4ème, on se demande si l’entier est utilisé comme un entier ou comme un booléen. Quand au dernier… on est en droit de se demander le rôle du booléen. Sans regarder la doc ou le code de toggle, difficile d’être sûr de ce qui se fait.

Bref à vouloir trop en faire, on perd inutilement les développeurs qui utilisent les API.

Ces quelques exemples ne sont, en aucun cas, une liste gravée dans le marbre de règles à appliquer dans tous les cas. De plus, les bonnes pratiques de conception ne s’arrêtent pas à seulement 3 concepts. Par contre, les avoir à l’esprit quand on conçoit du code open source, quel que soit le langage, me semble nécessaire. Si le sujet vous intéresse, vous pourrez trouver plus d’exemples dans l’article de Rodney, publié sur SmashingMagazine :

Si vous avez des idées, des remarques, ou des retours d’expérience sur le sujet, n’hésitez pas à en faire part dans les commentaires.