Ecrire du code propre – Le nommage

Après ce premier article sur les piliers qui soutiendront votre pratique du code, je vous propose de commencer par la pratique la plus simple mais bien souvent la plus négligée : le nommage. Un nommage adéquat sera la source première de sens à votre code. Quand on sait que 70% du temps d’un développeur  consiste à lire du code, il est important d’en optimiser sa compréhension.

Préambule : standards

Avant de chercher à améliorer le nommage des entités de votre code, il faut que toute votre équipe partage une même vision sur le sujet. C’est ce qui va former vos standards de nommage, que vous avez initiés au démarrage du projet et que vous enrichissez au fur et à mesure de l’avancement du projet.
En général, la revue de code est le bon moment pour enrichir vos standards. En effet, c’est à la suite de celle-ci qu’ont lieu des discussions, voir des débats sur la meilleure façon de nommer vos entités logicielles.
Le meilleur moyen de pérenniser vos standards, c’est à minima de les écrire. Que ce soit sur un mur, sur un wiki, sur un googledoc, l’important c’est que tout le monde puisse les lire et les modifier facilement. Pour les plus outillés, vous pourrez certainement automatiser le contrôle du respect d’une partie de vos standards via vos outils d’analyse de code.

Voici quelques exemples de standard de nommage:

  • Les interfaces doivent commencer par un I
  • Les interfaces ne doivent pas commencer par un I
  • Les implémentations d’interface doivent finir par Impl
  • Le nom des listes doit toujours finir par List
  • Le nom d’une liste doit indiquer ce qu’elle contient et finir par un ‘s’
  • etc…

Usage de l’anglais

L’usage de l’anglais pour nommer nos entités est plus ou moins une tradition dans la programmation informatique. L’anglais a l’avantage d’être une langue précise et technique, où les termes offrent peu d’ambiguïté… quand elle est bien maîtrisée. En effet, une mauvaise maîtrise de l’anglais va au contraire obscurcir votre code et pourra même amener des contresens.

Au démarrage de votre projet, posez-vous donc la question de savoir dans quelle langue vous êtes le moins ambigu. Si c’est l’anglais allez-y, mais pas à moitié. Évitez au maximum le franglais, même pour les termes métiers. N’hésitez pas à créer un glossaire, notamment pour les termes métiers complexes, pour conserver une uniformité de nommage tout au long du projet.

Si vous choisissez votre langue natale, vous pouvez conserver l’usage en anglais d’un certain nombre de termes conventionnels dans votre nommage, notamment l’usage des verbes d’actions pour vos méthodes (get, set, has,…) ou le nom des designs patterns usuels (cf GOF). Il est nécessaire de créer aussi un glossaire dans ce cas, afin de référencer tous les anglicismes acceptés.

L’intention

La première question à se poser avant d’initier l’écriture d’une classe, d’une méthode ou d’une variable, c’est « quelle est mon intention ? ». Pour une fonction ça peut aussi se traduire en « qu’est-ce que je cherche à faire exactement ? ». Pour une classe, ce sera « quelle est la responsabilité de cette méthode ? » Voici quelques exemples de réponse à ces questions : représenter un objet métier, être le controller me permettant l’accès à une ressource,  etc…). Enfin, pour une variable, ce sera « qu’est-ce que cette variable représente ? ».

Dans le cadre d’une revue de code, ce sont ces mêmes questions qu’il faut se poser, en comparant le nom à l’implémentation réelle. Une inadéquation entre les deux n’est peut être pas seulement une erreur de nommage mais aussi dans de nombreux cas un problème d’architecture du code. Le non respect de l’intention dans un nommage est donc souvent un révélateur d’un besoin de refactoring.

Pour exprimer cette intention, essayez d’être le plus concis possible. En effet, plus un nom est court, plus l’intention derrière est claire. Cela facilite donc grandement la lecture de votre code par le reste de l’équipe.

Les ennemis du nommage

L’ambiguïté.

Ce premier ennemi du nommage peut se présenter de différentes formes :

  • Ambiguïté de sens : les termes utilisés peuvent avoir plusieurs significations dans le contexte de l’application
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    //mauvais nommage
    public class Stream{
        public void displayVideo(String videoId){
            ...
         }
    }
     
    public class LogStream{
        public void logStream(Stream stream){
            // est-ce que c'est notre objet Stream que l'on loggue ou celui de la JDK ?
        }
    }
    // meilleur nommage
    public class VideoStream{
        public void displayVideo(String videoId){
            ...
         }
    }
  • Ambiguïté de lecture : il y a une trop forte proximité syntaxique entre différents termes, notamment au sein d’une même méthode
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    //mauvais nommage
    public void transformArticles(List contents){
        ...
        List<article>articles = new ArrayList<article>(); 
        for(Article article : contents){ 
            article = doSomething(article);
            articles.add(article); 
           // trop de proximité entre article et articles, la lecture prend plus de temps ... 
        } 
        ... 
    } 
     
    //meilleur nommage 
    public void transformArticles(List contents){ 
         ... 
         // répétition de type mais moins de proximité syntaxique 
         List<article>articleList = new ArrayList<article>(); 
         for(Article article : contents){ 
             article = doSomething(article); 
             articleList.add(article); 
             ... 
          } 
          ... 
    }
  • Des noms trop génériques : les termes n’expriment aucune particularité fonctionnelle
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    //mauvais nommage 
    public void transformArticles(List contents){ 
        ... 
        List<article>list = new ArrayList<article>(); // terme beaucoup trop générique... 
        for(Article article: contents){ 
            article = doSomething(article); 
            list.add(article); 
            ... 
        } 
        ... 
    } 
     
    //meilleur nommage 
    public void transformArticles(List contents){ 
        ... 
        List<article>transformedArticleList = new ArrayList<article>(); 
        for(Article article : contents){ 
            article = doSomething(article); 
            transformedArticleList.add(article); 
            ... 
         } 
         ... 
    }
  • Rupture du principe de moindre surprise : un terme « réservé » est utilisé pour tout autre chose que son usage commun
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    //mauvais nommage 
    public class ServiceManager{ 
        //un manager est plutôt attendu pour nommer certaines classes de coordination 
        public String name; 
        public String serviceName public List managees; 
    } 
     
    //meilleur nommage 
    public class ServiceDirector{ 
        //on s'éloigne alors un peu du terme métier pour ne plus violer le principe 
        //si c'est trop génant, il devient nécessaire de mettre en place un standard 
        //réservant le terme manager à un terme métier, et non un type de classe 
        public String name; 
        public String serviceName public List managees; 
    }

Quelle que soit la forme qu’elle prend, l’ambiguïté aura pour conséquence une difficulté de compréhension, voire pire, une mauvaise interprétation.

L’obsolescence

Suite à des évolutions ou des refactoring, les noms de nos entités ne sont plus en adéquation avec ce qu’elles font. On le constate aussi souvent sur des classes qui ont trop grossi au fur et à mesure de l’ajout de méthodes. Leurs noms ne sont plus alors que la représentation d’un sous ensemble de leurs usages. C’est d’ailleurs aussi l’indicateur qu’il faudrait peut être redécouper cette classe.

Comment améliorer le nommage ?

Pour améliorer le nommage de ses entités, ou ne serait-ce que conserver sa qualité et sa « fraicheur », il est nécessaire, selon moi, de mettre en place 3 pratiques :

Le glossaire d’équipe est en fait une sorte de dictionnaire de tous les standards de nommage qui vont au delà du simple standard de syntaxe. Par exemple, c’est au sein de celui-ci que l’on conservera toutes les désambiguïsations ou la définition des différents termes métiers complexes utilisés. Et comme je l’ai déjà dit plus haut, il est d’autant plus important quand le code est exprimé en anglais alors qu’originellement les termes métiers de notre application sont exprimés en français dans les User stories.

Conclusion

Le développement étant pour beaucoup une problématique de partage d’information entre les développeurs, il est nécessaire que celle-ci soit facilitée au maximum. L’expressivité de votre code est donc essentielle, et celle-ci passe pour beaucoup par un nommage adéquat. Cette adéquation ne peut être obtenue et maintenue que par une réflexion constante sur notre intention et sur l’absence d’ambiguïté dans sa description. Malheureusement, ce qui peut nous paraitre simple et évident ne l’est peut être pas pour un autre développeur. C’est donc pour ça que le nommage est une pratique collective de développement qui, elle aussi, nécessite beaucoup d’interactions dans l’équipe pour être de bonne qualité.