I. Introduction

Faisons un sondage rapide, combien de fois avez-vous déjà maudit ou entendu maudire les "normes de codage" ? Des dizaines, des centaines, des miliers de fois ? Encore faut il que vous ayez déjà entendu cette expression. Si c'est le cas, il y a de grandes chances que vous travailliez sur du développement informatique et cet article peut vous intéresser. Car finalement c'est quoi ces "normes de codage" ?

Passons les préjugés communs :

  • Une façon d'uniformiser les individus ? ("Je ne suis pas un numéro !")
  • Une entrave à l'imagination posé par votre hiérarchie pour limiter vos prises d'initiatives et les rassurer ?

Elle est loin l'époque des génies dans leurs garages où l'informatique s'apparentait plus à l'artisanat. Aujourd'hui La création logicielle tend désormais vers un processus industriel bien rodé.

L'heure n'étant plus à l'artisanat, l'informatique tend à normaliser ces processus : codage, méthodologies de test, de conduite de projet etc...

Souvent mal perçu, car mal expliqué, on oublie l'objectif des normes de codages qui sont apparus suite à un constat simple dont on retrouve un condensé sur le site de Sun

  • 80% du temps passé dans le cycle de vie d'un logiciel intervient pendant le cycle de maintenance
  • Il est très rare qu'un code soit maintenu par son auteur original
  • De bonnes conventions adoptées par tous permettent à un développeur de s'y retrouver plus facilement dans un code inconnu

Et c'est tout le but de ces normes, simplifier la maintenance et faciliter une compréhension universelle. On remarqueras notamment dans cet article que l'un des premiers conseils pour améliorer du code que l'on maintient est avant tout de le remettre aux normes.

La remise aux normes est un préalable aux opérations de refactoring !

Attention cependant à la cohérence de codage. Restez cohérent par rapport à l'existant, au minimum par module. Et sachez aussi quand il ne faut pas être dans les normes. C'est d'ailleurs l'un des conseils sur le site des normes de codage Pythonnormes de codage Python. Parfois il faut enfreindre les standards si cela peut dégrader la lisibilité ou si ces standards sont différents de ceux de votre vieille application qui a 20 ans et dont vous avez hérité avec plaisir.

A vous de juger du juste équilibre entre le refactoring nécessaire et le temps disponible, en gardant à l'esprit l'objectif : améliorer la maintenance future.

A charge d'exemple, je détaillerais un ensemble des rêgles simples. Le but n'étant pas d'édicter des rêgles absolues mais bien d'expliquer leurs raisons d'être.

Les chiffres donnés plus bas et suivi d'un (*) sont des métriques établies sur la base de l'expérience et peuvent ne pas s'appliquer à tout les cas possibles. A vous de les adapter selon vos projets et votre expérience. En aucun cas je ne prétends donner de rêgles d'or.

II. Normes générales d'organisation du code

Dans ce premier chapitre, nous aborderons des conventions relatives à l'organisation et au découpage de votre code.

II-A. Taille des fichiers

En rêgle générale, on s'accorde sur le fait qu'un fichier ne doit pas dépasser 1000 (*) lignes de codes. On peut évidemment souligner qu'un fichier est difficile à relire dépassé cette limite mais au delà de ça, pour un programme voulant respecter le paradigme objet, des fichiers très longs (plusieurs milliers de lignes) sont souvent l'expression d'un mauvais découpage des traitements, i.e. d'une mauvaise compréhension de la conception/développement orienté objet. De tels fichiers traduisent généralement la présence de classes « gestionnaires », ayant accès à toutes les données possibles et imaginables et connaissant l'essentiel du fonctionnel. Or chaque classe devrait être responsable des traitements qui lui sont propres.

II-B. Taille des méthodes

Il est déconseillé d'écrire des méthodes de plus de 100 (*) lignes. Certains traitements peuvent néanmoins justifier un dépassement de ce nombre de lignes, mais des méthodes trop longues traduisent un mauvais découpage d'algorithme et conduisent généralement à l'obtention d'un code incompréhensible, suites aux diverses actions correctives qui y seront apportées.

II-C. Entête documentaire des classes et méthodes

Les entêtes documentaires permettent de spécifier le comportement de votre applicatif. Ne perdez jamais à l'esprit que votre code sera un jour lu par une autre personne, l'absence de documentation obligera vos successeurs à faire de la rétro conception à partir du code.

Voici une petite liste d'outils pour la documentation :

A noter que l'on peut trouver un certain nombre d'adversaires à la documentation dans le code argumentant que

  • le code doit être suffisament explicite pour se comprendre tout seul
  • si un développeur apporte une modification, la documentation est souvent mise de côté, ce qui entraine des commentaires faux et source d'erreur à l'avenir.

Personnellement je vous encouragerais à maintenir une documentation de qualité dans le code. Effectivement le code doit pouvoir se comprendre, mais sur un applicatif de plus de 50 000 lignes de code vous aurez sans doute mieux à faire ces 5 prochaines années que de tout relire. Je vous encourage à lire cet article d'IBM sur le sujet.

La documentation de l'entête décrit une boite noire dont vous n'avez pas l'obligatoire de tout connaître, elle doit être complète et mise à jour. Elle contient au minimum pour les méthodes :

  • La liste des paramètres et leur signification si besoin
  • Le retour attendu
  • Les exceptions/erreurs renvoyées (selon les languages)
  • Un court descriptif de la méthode

Pour les classes, une description, même succinte. Une description complète étant bien sûr préférable.

Exemple : Javadoc de la méthode add de ArrayUtils

II-D. Pourcentage de commentaires dans le code

Le pourcentage de commentaires dans le code devrait être à hauteur de 20% (*) des lignes écrites. Les commentaires permettent de suivre des algorithmes complexes ou de suivre le cheminement logique d'une chaîne de traitement. S'ils peuvent paraître superflus lors de la phase de développement, ils seront indispensables lors de la phase de maintenance pour les personnes qui prennent le relais d'un code qu'ils n'ont pas écrit eux-mêmes. Ce point fait sans doute parti des points les plus difficiles à faire accepter. Pourquoi écrire des commentaires puisque j'ai déjà une Javadoc. Et d'ailleurs beaucoup des projets open source que j'utilise ne commente son code. Attention justement à ne pas confondre la Javadoc qui décrit la boite noire que constituent votre méthode et le commentaire interne qui décrit son fonctionnement. De plus, si vous utilisez des librairies open source et que vous avez déjà du débugger certaines d'entre elles, il y a fort à parier que justement vous auriez bien aimé plus de commentaires parfois.

III. Normes générales de nommage

Dans la suite de ce chapitre, nous reprendrons les principaux points développés par SUN et suivies par la communauté Java.

De façon générale, tous les noms doivent être en anglais. Certains points peuvent s'appliquer à d'autres languages mais nous décrirons ici des conventions qui peuvent être spécifiques. Les conventions de nommage font sans doute partie des conventions de codage les plus liées à la lisibilité. Veuillez vous référer à la partie références pour les sources ayant permis de rédiger les paragraphes suivant.

On pourra remarquer dans certaines sociétés des conventions supplémentaires issus bien souvent d'autres langages. Par exemple :

Préfixage des variables par le type, la portée etc... (language : C, C++, python, PHP, etc...). A noter que ces conventions se justifient pour des languages non typées comme le python ou le PHP et pour des équipes qui n'ont pas la possibilité d'utiliser des IDE performants (avec mise en couleur selon la portée et infos bulles sur les variables). Cependant si vous utilisez un language typé avec un bon IDE ce type de notation surchargera inutilement votre code.

Préfixage des méthodes privées par des "__". (language : python). Parfait exemple de conventions à déconseiller pour les langages dont ce n'est pas la norme. Vouloir retranscrire les normes que l'on connaît dans un autre language a bien souvent pour résultat de produire l'effet inverse à l'effet escompté, ce n'est plus une norme puisque vous êtes le(s) seul(s) à l'utiliser.

III-A. Packages

  • Tout en minuscule
  • Utiliser uniquement [a-z][0-9] et le point.
  • Tout package doit avoir comme root : com, edu, gov, mil, net, org ou les deux lettres identifiants un pays (code ISO).

III-B. Classes

  • Première lettre en majuscule
  • Mélange de minuscule, majuscule avec la première lettre de chaque mot en majuscule
  • Donner des noms simples et descriptifs
  • Eviter les acronymes hormis les communs (Xml, Url, Html)
  • N'utiliser que des lettres [a-z][A-Z] et [0-9]

Il s'agit de la même convention en C++en C++.

Le nom d'une classe n'est jamais un verbe, ou plus généralement une action. On a donc une classe qui peut s'appeler "BiduleBuilder", car c'est un constructeur de bidule. Mais pas BiduleBuild. On utilise la grammaire anglaise, soit donc "BiduleBuilder" et pas "BuilderBidule".

III-C. Variables

  • Première lettre en minuscule
  • Mélange de minuscule, majuscule avec la première lettre de chaque mot en majuscule
  • Donner des noms simples et descriptifs
  • Variable d'une seule lettre à éviter au maximum sauf dans des cas précis et locaux (tour de boucle)
  • N'utiliser que des lettres [a-z][A-Z] et [0-9]

III-D. Constantes

  • Tout en majuscule
  • Séparer les mots par des underscore
  • Donner des noms simples et descriptifs
  • N'utiliser que des lettres [a-z][A-Z] et [0-9]

III-E. Artefact (jar)

Cette convention n'est pas hérité de SUN mais de la communauté open source en générale :

  • Tout en minuscule
  • Séparer les mots par des -
  • N'utiliser que des lettres [a-z][A-Z] et [0-9]
  • Le numéro de version apparaît à la fin du nom séparé par un -
  • La version est numérique
  • On peut utiliser des acronymes selon les cas : alpha, beta, RC, ga etc...

III-F. Standard Directory Layout (projet maven)

Cette convention est essentiellement applicable au monde Java. Je la décris ici car elle donne un bon exemple de l'avantage de suivre des conventions. Maven est un outil de build utilisé principalement dans le monde Java. L'un de ces leitmotiv est : "conventions over configurations", autrement dit préférer les conventions à une pléthore de configurations : si tant est que vous respectiez des conventions générales, l'outil doit être facile à mettre en place. Son adoption grandissante dans la communauté Java vous garantit qu'en changeant de projet vers un autre projet qui utilise maven vous ne serez pas dépaysé par l'organisation.

Pour plus d'infos en français sur maven et l'organisation des fichiers : Utiliser maven2Utiliser maven2

La description ci dessous n'est pas complète, référez vous au site officiel pour plus d'informations.

Répertoire Description
src/main/java Source
src/main/resources Ressources
src/main/webapp Source d'une webapp
src/test/java Source de test
src/test/resources Ressources de test

IV. Conventions relatives à la lisibilité

Dans ce chapitre nous allons aborder les règles de codage lié dont certaines sont liées avec l'éditeur utilisé. En prenant mon expérience professionnelle en compte, j'ai modifié légèrement les conventions utilisées dans eclipse. Comme pour les autres chapitres, je vous invite à rester proche des conventions officielles mais cela n'empêche pas de rester critique à leur encontre

IV-A. Indentation du code

L'ensemble du code devrait être indenté de façon similaire. De plus, les éditeurs de code devraient être configurés de manière à remplacer les tabulations par des blancs. De cette façon, des éditeurs différents peuvent être utilisés en fonction des affinités de chacun tout en obtenant un résultat identique. Prenons un cas de figure simple. Ci-dessous un bout de code indenté avec des tabulations. Sous eclipse j'aai précisé qu'une tabulation représentait un espacement de 8 caractères

Programme correctement indenté avec tabulations
Sélectionnez

public class MyApp 
{
		public static void main (String[] args)
		{
				System.out.println("Hello World");
		}
}

Sous xemacs j'ai configuré mon éditeur pour mettre afficher des tabulations de 4 caractères et. Cependant j'ai aussi précisé que l'indentation du code s'effectuait avec des espaces et non des caractères tabulations. Je modifie une ligne et ca apparait correctement chez moi puisque j'affiche 4 espaces pour les tabulations.

Programme correctement modifié sous xemacs
Sélectionnez

public class MyApp 
{
    public static void main (String[] args)
    {
        System.out.println("Hello World");
    }
}

Je rouvre sur eclipse, la ligne modifiée comporte désormais 4 espaces alors que mes tabulations de 8 sont restées.

Programme ouvert de nouveau avec eclipse
Sélectionnez

public class MyApp 
{
		public static void main (String[] args)
		{
    System.out.println("Hello World");
		}
}

Dans cette configuration, les sources écrits avec eclipse et ouverts sous xemacs sont tassés à gauche, alors que les sources écrits avec xemacs et ouverts sous eclipse sont déportés trop à droite. L'effet rendu est très désagréable sur des fichiers ayant été édités par plusieurs éditeurs.

Certains languages sont encore plus stricts sur les identations, c'est notamment le cas de Python qui n'acceptera pas le mix des tabs et des espaces si vous utilisez certaines options de compilation. N'oubliez pas, en Python, l'indentation est le seul repère permettant de déterminer le début et la fin d'un bloc !

IV-B. Taille des lignes

La lecture du code lors des phases de maintenance se fait de bas en haut et l'utilisation d'une barre de défilement horizontal est pénalisante à la lecture. C'est pourquoi on limite en général la taille d'une ligne à 180 caractères qui correspondent à la largeur d'un écran.

A noter que plusieurs languages encouragent une longueur de ligne maximale à 80 caractères. Par exemple le style d'édition par défaut dans Eclipse limite à 80 caractères la taille des lignes. 80 caractères correspondent à la taille des écrans si toutes les vues Eclipse sont affichées. Pour PythonPython, il s'agit de prendre en compte d'autres types de devices limité à 80 caractères.

La taille limite est donc à déterminer en fonction de vos besoins, des outils utilisés par les membres de votre équipe ainsi que de la taille de leurs écrans.

En tant qu'exemple personnel, je travaille dans une équipe qui utilise Eclipse sur des écrans 22 pouces. Nous avons décidé de ne pas utiliser la limitation à 80 caractères pour deux raisons principales :

  • Le reformatage automatique d'Eclipse établit des césures automatiques en dépit de la logique
  • 80 caractères gâchent une grande place à l'écran non nécessaire en mode édition plein écran.

Etablir une limite à 300 dans Eclipse permet de désactiver ces césures automatiques. A charge du développeur de les faire intelligemment et lisiblement. Les césures automatiques combiné avec l'utilisation d'accolades en fin de ligne (oui je suis minoritaire sur ce point mais je déteste les accolades en fin de ligne ^^) peuvent donner un code de ce type :

Méthode toMap de ArrayUtils avec les conventions sun
Sélectionnez

 
    public static Map toMap(final Object[] array) {
	if (array == null) {
	    return null;
	}
	final Map map = new HashMap((int) (array.length * 1.5));
	for (int i = 0; i < array.length; i++) {
	    Object object = array[i];
	    if (object instanceof Map.Entry) {
		Map.Entry entry = (Map.Entry) object;
		map.put(entry.getKey(), entry.getValue());
	    } else if (object instanceof Object[]) {
		Object[] entry = (Object[]) object;
		if (entry.length < 2) {
		    throw new IllegalArgumentException("Array element " + i
			    + ", '" + object + "', has a length less than 2");
		}
		map.put(entry[0], entry[1]);
	    } else {
		throw new IllegalArgumentException("Array element " + i + ", '"
			+ object
			+ "', is neither of type Map.Entry nor an Array");
	    }
	}
	return map;
    }
 
la même sans les césures automatiques
Sélectionnez

 
    public static Map toMap(final Object[] array)
    {
        if (array == null)
        {
            return null;
        }
        final Map map = new HashMap((int) (array.length * 1.5));
        for (int i = 0; i < array.length; i++)
        {
            Object object = array[i];
            if (object instanceof Map.Entry)
            {
                Map.Entry entry = (Map.Entry) object;
                map.put(entry.getKey(), entry.getValue());
            } 
            else if (object instanceof Object[])
            {
                Object[] entry = (Object[]) object;
                if (entry.length < 2)
                {
                    throw new IllegalArgumentException("Array element " + i + ", '" + object + "', has a length less than 2");
                }
                map.put(entry[0], entry[1]);
            } else
            {
                throw new IllegalArgumentException("Array element " + i + ", '" + object + "', is neither of type Map.Entry nor an Array");
            }
        }
        return map;
    }
 

A vous de juger...

IV-C. Encodage des fichiers

La lecture du code peut être grandement pénalisée si les fichiers sont édités avec des encodages différents. En effet les accents et autres caractères particuliers « ç » apparaissent sous la forme de carré ou autres bizarreries. Ces différences peuvent être pénalisantes pour les fichiers de ressource, les fichiers sql etc... Quelque soit la convention que vous utilisez, faites en sorte d'avoir la même pour tout le monde.

IV-D. Retour à la ligne Windows/unix

Afin d'éviter les Ctrl+M dans les fichiers, il est nécessaire de spécifier le type de retour à la ligne dans l'éditeur de fichier.

V. Conclusion

Cet article aura permis je l'espère d'expliquer certaines conventions de nommage et leur utilité. Les exemples dans ce document ne sont pas à prendre au pied de la lettre et je vous invite à prendre un certain recul avant de les adopter. Il est bien évidemment possible de les adapter mais gardez toujours à l'esprit l'objectif de ces normes : leur universalité !

V-A. Remerciements

Je tiens à remercier *** pour leurs corrections et leur aide à la publication de cet article.

V-B. Références