Have you ever patched binary code?
« Have you ever patched binary code? … While the program was running? ».
Quote from « The hacker test ».
Raccourcis Eclipse ou comment arrêter de coder à la souris
ctrl+⇧+L : liste des raccourcis (le raccourci pour les contrôler tous)
Navigation rapide dans les fichiers
ctrl+→ : aller au mot suivant (ça va quant même plus vite que de marteler →)
ctrl+← : aller au mot précédent
ctrl+shift+↑ : aller à l’attribut/méthode précédente
ctrl+shift+↓ : aller à l’attribut/méthode suivante
ctrl+l : aller à la ligne Ligne X (utile après consultation d’une stack trace)
ctrl+. : aller à l’item suivant. En fonction des vues il peut s’agir d’un warning, d’une erreur, d’un résultat de recherche, etc
ctrl+, : idem mais pour l’item précédent
alt+→ : naviguer en avant dans son historique
alt+← : naviguer en arrière dans son historique
ctrl+q : retourner à l’endroit de la dernière modification
Navigation entre les fenêtres
Il existe plusieurs niveaux de fenêtres, tout d’abord celles de plus hauts niveaux : les perspectives (ex : java, synchronize, debug). À l’intérieur d’une perspective plusieurs vues prennent place comme par exemple l’explorateur de paquets, la console ou l’éditeur de code. Dans un dernier temps plusieurs vues ou instances de vue peuvent s’organiser en onglets.
F12 : éditeur (mettre le focus sur l’éditeur, là où se trouve le code)
ctrl+e : Éditeurs (idem mais avec liste des éditeurs possibles)
ctrl+⇞ : onglet précédent
ctrl+⇟ : onglet suivant
ctrl+F7 : aller à la vue suivante
ctrl+shift+F7 : aller à la vue précédente
ctrl+F8 : aller à la perspective suivante
ctrl+shift+F8 : aller à la perspective précédente
ctrl+m : Maximise/Minimise la vue active
ctrl+w : fermer l’onglet
ctrl+shift+w : fermer tous les onglets d’éditeur
Dans l’éditeur
ctrl+shift+F : reFormatter le code
ctrl+shift+O : Organiser les imports
ctrl+i : correct indentation
ctrl+1 : quick fix (propose une solution sur une erreur/warning)
alt+shift+← : sélectionne l’élément précédent (plusieurs fois sélectionne les blocs englobants)
alt+shift+→ : idem pour l’élément suivant
ctrl+shift+P : aller à l’accolade/Parenthèse/crochet correspondant.
F3 : open declaration (identique au ctrl+clic)
ctrl+F3 : open structure (affiche la structure du type dans une pop-up)
F4 : open type hierarchy (idem mais dans la vue Hierarchy)
ctrl+o : affiche la vue Outline du type dans une pop-up
Modification
ctrl+shift+/ : commenter ou décommenter les lignes sélectionnées
alt+↓ : déplacer les lignes sélectionnées vers le bas
alt+↑ : déplacer les lignes sélectionnées vers le haut
ctrl+space : appeler l’assistant d’autocomplétion
ctrl+d : supprimer toute la ligne
ctrl+⇧+enter : insérer une ligne juste au dessus
ctrl+backspace : supprimer le mot précédent
ctrl+delete : supprimer le mot suivant
Recherche
ctrl+shift+R : open Resource (ouvrir un fichier sans naviguer dans l’arborescence)
ctrl+shift+T : open Type (idem mais limité aux types Java)
ctrl+h : recherche dans tous le workspace
ctrl+f : Find rechercher dans la vue (éditeur, navigateur, …)
ctrl+k : aller au résultat de recherche suivant
ctrl+shift+k : aller au résultat de recherche précédent
ctrl+j : recherche incrémentale
ctrl+shift+j : recherche incrémentale inversée
ctrl+shift+G : rechercher toutes les déclarations de l’élément dans le workspace
Refactoring
alt+T : menu de refactoring
alt+⇧+R : renommer toutes les occurrences
Run
alt+⇧+X T : Exécute en tant que tests unitaires
A Lisper visits Versailles

Benar Venet exhibit at the Palace of Versailles by using more parenthesis than a Lisper.
photo by Ammar Abd Rabbo
Une action simple pour un meilleur code
Un jour, un développeur m’a demandé un conseil simple pour écrire un meilleur code. Sur le moment, je n’ai pas su trop quoi répondre ou plutôt su donner une réponse simple et facile à mettre en pratique comme sa question m’y invitait. Alors cet article est l’occasion de me rattraper.
De tout les conseils que j’ai pu recevoir, de tout ce que j’ai pu lire et de tout ce que j’ai pu découvrir, si je ne devais garder qu’un seul et unique élément à transmettre alors ce serait d’écrire des méthodes courtes.
C’est tout simple, ça parait même être du bon sens, mais c’est important car de ce principe découle un grand nombre de bonnes pratiques qui sont difficiles à mettre en place sur de grosses méthodes.
Il s’agit d’une préconisation courante, mais c’est Robert C. Martin dans son livre Clean Code qui m’a vraiment fait prendre conscience de ce critère et du soin particulier à y apporter. Comment ? En chiffrant la longueur d’une méthode :
Qu’est ce qu’une méthode courte ? Combien de lignes ?
The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. [...] Functions should not be 100 lines long. Functions should harly ever be 20 lines long. [...] — Robert C. Martin, Clean Code
Je n’ai jamais vraiment écrit de très longues méthodes, mais depuis cette lecture j’ai gardé en tête qu’une méthode de plus de 20 lignes avait probablement un problème. C’est arbitraire mais c’est ma limite, au delà je me sens mal à l’aise. Je ne dis pas qu’il faut être extrémiste, mais honnêtement les méthodes qui ont vraiment besoin d’être plus longues sont rares (j’ai bien en tête que des constructions de requêtes JDBC avec beaucoup de champs, ou de l’inline method pour optimisation sur mobiles).
The god method
Les grosses méthodes sont souvent des god methods : des fonctions qui font à peu de tout. Elles sont difficiles à nommer, normal elles en font trop ! elles ont trop de responsabilité du coup elles ont un nom vague, générique (process, handle, manage, display) ou pire un nom qui ne correspond pas complètement à ce qu’elles font. De plus elles sont difficiles à réutiliser et des morceaux se retrouvent copiés/collée.
À l’opposé, les fonctions courtes ont une complexité moindre, pas de surcharge cognitive, elles sont plus facile à lire et à appréhender puisque visible dans leur ensemble à l’écran. La modification ou le refactoring sont simplifiés. Leur objectif est clair, elles vont droit au but, le nom est donc facilement trouvé et la documentation triviale.
D’un point de vue plus macroscopique, les petites méthodes rendent le design plus élégant : les comportements sont factorisés, plus de duplication de code, les simplifications apparaissent ainsi que la mise en patterns si nécessaire.
Et enfin de telles méthodes sont évidemment plus facile à débugger et à tester.
L’outil ultime reste le refactoring extract method, il consiste à isoler un fragment de code logique et d’en faire une méthode nommée par son objectif. Extract method est souvent directement intégré dans les IDE avec prise en charge des paramètres et du retour de la nouvelle méthode.
L’allègement des méthodes est un bon prérequis pour des refactoring de plus grande envergure, il agit comme un catalyseur à une amélioration générale de la qualité du code.
Petit benchmark
On va réaliser ici un petit benchmark sur une métrique que j’apprécie particulièrement : le nombre moyen de ligne de code par méthode.
L’analyse sera lancée sur 10 projets open sources connus et reconnus. Des projets anciens, des projets récents, de tout type : librairie, outil, framework, framework web, conteneur de servlets, webapp, base de donnée, leur seul point commun ils sont codés dans le même langage Java (en effet j’ai pensé que la verbosité ou non des langages pouvait fausser les résultats mais je n’en n’ai même pas acquis l’intime conviction).
Pour être précis, il ne s’agit pas exactement du nombre de ligne de code mais du nombre de statements, c’est-à-dire d’instructions. On ne compte donc pas les lignes vides, les commentaires, le code commenté, la déclaration des méthodes, etc.

Ce qui est intéressant ici, c’est que malgré l’hétérogénéité des projets, le nombre de ligne de code moyen par méthode reste très faible (la moyenne sur ces projets est de 3.9 lignes par méthode). Évidemment il y a quelques légères fluctuations mais sans commune mesure par rapport à de nombreux projets juniors ou communément considérés comme mauvais où cette métrique atteint facilement 15, 20, 60 lignes (et donc probablement derrière cela des méthodes de plusieurs centaines de lignes).
Cet indicateur n’est pas suffisant pour dire si un projet est bon ou mauvais, mais il est toujours intéressant de savoir si il entre dans cette espèce de standard ou si au contraire il est hors norme, signe peut être d’une qualité logicielle moribonde.
Eclipse, les actions à la sauvegarde
Réaliser automatiquement des actions à la sauvegarde est une fonctionnalité qui est offerte par Eclipse depuis un bon moment. C’est tellement pratique que ça surprend toujours quand on voit un développeur qui ne la connaît pas ou ne l’active pas. De toute manière, dans une équipe, celui qui ne l’intègre pas se fait vite repérer : quand on passe derrière lui pour une simple modification, on se retrouve avec un diff sur toutes les lignes !
Car automatiser ses actions à la sauvegarde, c’est avant tout faciliter la prise en compte des standards de codage sur un projet et se simplifier la vie. Il y avait deux raccourcis magiques ctrl+maj+F pour reformater correctement toute la page de code et ctrl+maj+O pour réorganiser et cleaner les imports. C’était tellement magique qu’on avait pris l’habitude de faire sans arrêt la combo (ctrl+maj+F, ctrl+maj+O, ctrl+s). Une vraie mémoire musculaire ; le moindre changement et c’était parti (un peu comme quand on fait des ctrl+s sans réfléchir à chaque micro-pause). Je parle au passé alors que ces raccourcis existent encore, leurs réalisations possibles à chaque sauvegarde de fichier les a rendu quasiment obsolète.
Dans la fenêtre de configuration de préférences Windows > Preferences > Java > Editor > Content Assist > Save actions on peut décider, à sa convenance, de formater les lignes, organiser les imports, mais également aller plus loin comme supprimer les espaces inutiles, trier les membres, convertir les boucles for en for each quand c’est possible, ajouter ou au contraire supprimer les accolades pour les bloques à une instruction, etc, et tout ça à chaque sauvegarde.
Maven et la gestion des conflits entre dépendances
J’ai longtemps cru, à tort évidemment (je ne suis pas le seul, c’est une idée très rependue qu’on entend souvent et qu’on lit régulièrement), que en cas de conflit entre les dépendances, la dernière version prenderait le dessus. Mais ce n’est pas le cas, du moins pas toujours, car la règle de résolution est tout autre.
Prenons un projet d’exemple : mon-projet et déclarons lui une dépendance sur un autre projet d’exemple ma-dependance (pour suivre, nous utiliserons le goal maven mvn dependency:tree). À ce moment du projet l’arbre des dépendances sur mon-projet est le suivant :
> mvn dependency:tree org.test:mon-projet:jar:1.0-SNAPSHOT +- org.test:ma-dependance:jar:1.0-SNAPSHOT:compile \- junit:junit:jar:3.8.1:test
Maintenant, ajoutons une dépendance externe à la librairie ma-dependance, par exemple la version 1.0 de google-collections. Ce qui nous donne l’arbre ci-dessous :
> mvn dependency:tree org.test:mon-projet:jar:1.0-SNAPSHOT +- org.test:ma-dependance:jar:1.0-SNAPSHOT:compile \- com.google.collections:google-collections:jar:1.0:compile \- junit:junit:jar:3.8.1:test
Enfin dernière étape, provoquons un conflit en ajoutant une dépendance vers une autre version de cette même librairie dans mon-projet cette fois (la version 0.8).
> mvn dependency:tree org.test:mon-projet:jar:1.0-SNAPSHOT +- org.test:ma-dependance:jar:1.0-SNAPSHOT:compile +- com.google.collections:google-collections:jar:0.8:compile \- junit:junit:jar:3.8.1:test
On constate que la version 1.0 a disparue et qu’il ne reste que la version 0.8. Pour mieux suivre ce qu’il se passe, ajoutons l’option -Dverbose au goal mvn dependency:tree :
> mvn dependency:tree -Dverbose org.test:mon-projet:jar:1.0-SNAPSHOT +- org.test:ma-dependance:jar:1.0-SNAPSHOT:compile \- (com.google.collections:google-collections:jar:1.0:compile - omitted for conflict with 0.8) +- com.google.collections:google-collections:jar:0.8:compile \- junit:junit:jar:3.8.1:test
Comme prévu, l’ajout de deux versions différentes de la même lib a provoqué un conflit. Mais ici, c’est la version la plus ancienne qui a été conservée. Car en cas de conflit les dépendances les plus éloignées (profondes) dans l’arbre disparaissent au profit des plus proches.
De manière général, la commande mvn dependency:tree -Dverbose permet de détecter les dépendances supprimées pour cause de conflit, de redondance ou de création d’un cycle.
Ces fonctionnalités qui ne sont jamais utilisées
D’après cette étude, plus de la moitié des fonctionnalités livrées par les logiciels adoptant une gestion de projet traditionnelle sont rarement voir jamais utilisées.
Ces fonctionnalités ont un coût puisqu’il faut bien les spécifier, développer, tester, corriger, maintenir, documenter, etc. En considérant grosso-modo que le temps consacré à une fonctionnalité est indépendant du faite qu’elle soit utilisée ou non, alors ce temps ramené à un exercice annuel donnerait la proportion suivante : les 233 premiers jour de l’année seraient consacrés à des fonctionnalités non utilisées, puis à partir du 22 août et durant les 133 jours restants de l’année nous travaillerions utile. Quel gâchis !
L’exactitude et le mode de calcul de ces chiffres amèneraient probablement à des discussions animées et sans fin, alors retenons seulement qu’un certain nombre de fonctionnalités n’ont aucune finalité. En fait peu importe quelles sont les valeurs précises, l’idée c’est plutôt d’identifier les causes. Les conséquences nous les connaissons : de l’insatisfaction, d’abord chez le client qui paye pour quelque chose qu’il n’utilise pas, puis chez les développeurs pour leurs travaux qui ne servent à personne, et de manière général une complexité accrue inutilement. Ce temps gâché serait précieux pour fournir de la valeur au projet plus rapidement ou des fonctionnalités d’une qualité encore supérieure.
Le client
Dans une approche agile, le client est d’avantage impliqué dans la construction de son application ; ou plutôt plus régulièrement. Ainsi, il est possible de se rendre compte très tôt, si une fonctionnalité est utile ou non. Elle a pu être mal comprise lors de l’analyse des besoins ou durant son implémentation. Le demandeur peut percevoir que finalement elle ne sert pas ou que en l’état il faudrait l’adapter. Car l’agilité c’est se donner le droit, à l’erreur et de changer d’avis. Car ces fonctionnalités ne sont pas (toujours) tombées du ciel, elles ont été exprimées à l’origine du projet, mais les besoins ou la réflexion sur ces derniers ont pu évoluer entre temps. Si dès les premières phases du projet une fonctionnalité se révèle être superflue alors inutile de perdre son temps à l’implémenter. Demander un feedback régulier et un classement mis-à-jour des nouveaux besoins, c’est adopter une approche pragmatique et livrer un produit en adéquation avec le besoin du client.
L’utilisateur
Le problème c’est qu’il est difficile d’obtenir des feedbacks du client. D’autant que parfois, c’est lui qui s’obstine à exprimer des besoins qui n’auront aucun utilisateur à terme. Le client n’est pas toujours l’utilisateur de l’application. Il s’agit en général d’un décideur et non d’un opérationnel. Comme les développeurs, il pense mieux que quiconque connaître les besoins de l’utilisateur, mais on a souvent des surprises. Ce n’est pas facile, mais le mieux reste encore de rencontrer les utilisateurs, essayer de comprendre leurs problématiques métier et leur workflow pour les résoudre. L’observation en milieu réel et les test utilisateurs sont de bons outils pour percevoir ce qui est utiles aux utilisateurs.
Le concepteur
C’est souvent lors d’une rencontre avec un utilisateur qu’on se rend compte que si une fonctionnalité n’est pas utilisée, ce n’est pas parce qu’elle est inutile au contraire, mais plutôt parce qu’elle est mal perçue ou mal comprise. C’est incroyable le nombre de fois ou j’ai entendu des développeurs tenir un discours dédaigneux envers un client en les faisant passer pour un abruti congénital, alors qu’au fond la fonction est tellement mal conçu, illogique, incompréhensible que ça tient du hasard si d’autres ont réussi à l’utiliser. C’est toujours plus facile d’accuser les autres. À chaque fois qu’un utilisateur rapporte une anomalie (que ce soit sa compréhension ou non qui en soit la cause), le concepteur devrait le percevoir comme un constat d’échec sur son propre travail ; si la fonction est correct alors c’est sa perception auprès des utilisateurs qui est erronée.
Refactor plutôt que commentaires
Je suis partisan de l’idée paradoxale qui consiste à ne pas commenter son code. L’aphorisme populaire énonce : « Comme un programme est difficile à écrire, il doit être difficile à lire ». Mais cette phrase n’est qu’une blague pour faire prendre conscience qu’au contraire un code doit être aisément compréhensible.
Pendant ses études, on entend : « Soignez votre code, pensez à le commenter », ou encore « J’ai mis 3 points pour les commentaires, pour vous forcer à en mettre », sur les forums on lit « J’ai ajouté des commentaires pour vous aider à la lecture ». On pense faciliter le travail du lecteur ou du mainteneur, mais si l’on veut vraiment lui faciliter la vie autant directement lui écrire un code propre et simple à saisir.
Car un bon code n’a pas besoin de commentaire, un code propre est auto-documenté. Robert C. Martin (Mr. Clean Code) défini l’usage des commentaires comme « The proper use of comments is to compensate for our failure to express ourself in code », ou comment se donner bonne conscience d’avoir enfanté un code monstrueux. « In our olfactory analogy, comments aren’t a bad smell; indeed they are a sweet smell. The reason we mention comments here is that comments often are used as a deodorant » – Martin Fowler & Kent Beck.
Les langages ont déjà les briques suffisantes pour écrire un programme lisible, via les noms de variables, de méthodes, etc. Extraire les quelques lignes de code ardues dans une méthode avec un nom particulièrement bien choisi et qui transmettrait l’information est plus utile que de commenter. Une méthode courte, claire et expressive est bien plus efficace qu’une grosse méthode complexe et très documentée.
Loin est l’époque où vous appreniez les bases et que dans les livres chaque ligne était commentée pour en expliciter le sens. En réalité, seules les personnes qui savent lire le code le font effectivement. Inutile donc de commenter de manière triviale des instructions que n’importe qui aurait analysé par expérience plus vite. Ce genre de commentaire parasite la lecture et la compréhension, il n’apporte aucune valeur ajoutée ; bref une perte de temps à l’écriture, une perte de temps à les maintenir, une perte de temps à la lecture.
Maintenant que le code est suffisamment expressif pour se comprendre de lui même sans les commentaires, alors ces derniers deviennent superflus. Ils enfreignent le principe DRY : don’t repeat yourself. Si les commentaires sont redondants alors ils sont inutiles, sinon ils sont dangereux. Dans tous les cas il faut les supprimer.
Les commentaires sont dangereux parce qu’aucun compilateur ni interpréteur n’est là pour s’assurer qu’ils sont corrects, qu’ils correspondent bien au code qu’ils commentent, et qu’ils ont été maintenue en parallèle. Il est plus dur de maintenir des commentaires que du code, assez vite les deux se désynchronisent. On sait tous que rien n’est plus trompeur, qu’une documentation fausse ou mensongère. Mieux vaut aucune information qu’une mauvaise information.
En ce qui concerne, la javadoc (ou équivalent) et les commentaires du type TODO c’est un peu différent. Dans le cas de la javadoc, il s’agit de documentation pas de commentaire (le Pourquoi, pas le Comment). Tandis que pour les TODO listes, ce ne sont pas des commentaires destinés à rester, ils sont condamnés à disparaitre sous peine de crouler sous leur nombre.





RSS Feed
