Maintenance - Développez réutilisable
Quelques bonnes pratiques avant de coder afin de faciliter votre future maintenance.
Industrialisation - Abstraction (Partie 1)
Petite revue sur l'industrialisation des développements. On voit ici l'étape fondamentale de l'abstraction.
Quand on développe une fonctionnalité ou un module qui doit être réutilisable il y a différentes notions à prendre en compte. En effet, la réutilisabilité, la maintenance ainsi que ce qu'on peut appeler la modularité de vos sources sont des points qui permettent à votre module de passer d'un développement qui fonctionne à un développement incontournable. Autant la réutilisabilité dépend plutôt de la conception de votre module (je vous renvoie vers l'article concernant l'abstraction), autant la "maintenabilité" et la modularité sont plutôt liées à des bonnes pratiques techniques et sont liées à votre code.
Je vais décrire dans cet article une liste d'éléments qui sont importants pour vos développements en terme de maintenabilité et qui sont plus ou moins liés à un langage. Je vais donc les écrire en php mais tout n'est pas limité à ce langage.
Je ferai un autre article dans le futur avec le même principe sur le sujet de la modularité.
Maintenabilité ?
L'une des choses qu'on aimerait ne pas avoir à penser quand on crée quoique ce soit c'est la possibilité que ce qu'on fait ne soit pas parfait et que des retours soient nécessaires après fabrication. Tous les retours de maintenance ne sont jamais une partie de plaisir, c'est un peu un constat d'échec sur la précédente livraison. Cependant il faut rester optimiste, cela veut aussi dire que votre développement est utilisé et donc qu'il vit.
Le problème c'est qu'un dysfonctionnement, une fois repéré, doit être traité et ce temps de traitement n'est pas rentable. C'est pourquoi dès la conception, il faut veiller à rendre son code maintenable pour réduire le temps de correction. Pour cela il y a de bonnes pratiques à mettre en place qui permettront dans le futur de faciliter votre maintenance par vous ou un autre développeur.
Le naming
Le point le plus important de votre code est le naming de vos fonctions, classes, méthodes, variables etc... C'est évidemment la première chose qui permet de décrire un élément, c'est son nom. Peu importe le système utilisé (Camel case, snake case, ou autre) le nom d'un élément doit indiquer sa fonction.
a. Nommer les méthodes, variables et classes fonctionnelles en anglais par convention et pour garder une certaine cohérence
/*...*/
foreach ($this->getUsers() as $user) {
$this->initUser($user);
}
/*...*/
foreach ($this->getUsers() as $data) {
$this->initData($data);
}
b. Nommer les méthodes de manière humaine
public function isEnabled(){
return $this->enabled;
}
public function enabled(){
return $this->enabled;
}
c. Eviter de traduire les éléments descriptifs métiers.
Si votre client parle de 'Maison', votre classe doit s'appeler 'Maison' et non 'House'. Cela permet de vous écarter de votre logique propre (avec toute la supposée expérience que vous vous faites personnellement sur le domaine) et de vous rapprocher de la logique client.
d. Evoquer les paramètres dans le nom des fonctions.
Ca peut-être très utile, cette technique issue du langage Objective-C, permet d'identifier rapidement les besoins de la fonction (même si dans l'idéal il ne faut pas oublier les doc blocks).
public function getUserById($userId){
// ...
}
public function getElementByCategoryAndStatus(Category $category, $status){
// ...
}
public function getUser($userId){
// ...
}
public function getElement(Category $category, $status){
// ...
}
L'objet
Je ne vais pas faire un long paragraphe à ce sujet. La base de la segmentation logique c'est l'objet. Il existe encore un débat avec les pros procédural, mais j'avoue qu'à part l'argument des performances (que je trouve extrêmement faible) je ne vois aucun atout au procédural. Ce dernier ne facilite pas la segmentation et pour vous y retrouver dans la masse de données accessibles, vous devez avoir un bon IDE...
L'orienté objet permet de structurer sa logique et de la segmenter. Cela vous permet d'agir sur un élément précisément, en réduisant votre contexte d'intervention. En gros, non seulement vous savez qu'un objet nommé contient précisément des propriétés et méthodes qui lui sont liées et rien d'autre mais aussi, et surtout, vous savez qu'en modifiant une classe vous intervenez sur toutes les entités de ce même type de manière sécurisée. Vous centralisez les points d'entrée et limitez votre contexte de compréhension. Autant dire qu'en cas de bug spécifique, vous ne perdez pas de temps à savoir où sont les fonctions qui traitent le sujet du dysfonctionnement, vous allez directement intervenir dans une classe qui traite la fonctionnalité.
La taille des fonctions
Plus une fonction est longue plus elle pose problème. En effet une fonction (comme son nom l'indique) doit effectuer un traitement particulier ou une liste de sous-traitements. Or quand une fonction comprend un trop grand nombre d'exécutions c'est probablement qu'elle effectue plusieurs traitements différents. Dans ce cas, il est nécessaire de l'exploser en différentes fonctions. Vous gagnerez ainsi en lisibilité et compréhension grâce au naming efficace. Vous permettrez en plus les interventions ponctuelles via des surcharges plus ciblées.
Il n'existe pas de règle absolue mais j'estime qu'à plus de 10 lignes d'exécutable (hors commentaires) il est très probable qu'une fonction puisse être explosée. Avec l'expérience, on se rend vite compte que la question ne se pose plus et la définition en fonction "courtes" devient naturelle et intuitive.
/**
* Je néglige volontairement le naming, les commentaires
* le typage et les dockbolcks pour vous concentrer sur l'atomisation.
**/
class Something{
/* ... */
public function doSomething(){
$query = $this->getBaseQuery();
$this->initQueryFilters($query);
$result = $this->getQueryResult($query);
$this->showResult($result);
}
public function getBaseQuery(){
$query = new Query();
$query->setTable('my_table', 't');
$query->addFields('t', ['field_1', 'field_2'])
return $query;
}
protected function initQueryFilters($query){
if( $this->filtersAreEnabled()){
foreach( $this->getFilters() as $filter){
$query->addCondition($filter->field, $filter->value);
}
}
}
protected function getQueryResult($query){
$query->sortBy('field_1');
return $query->execute();
}
protected function showResult($result){
foreach($result as $row){
echo $row->field_1 . ' : ' . $field_2;
}
}
}
class Something{
/* ... */
public function doSomething(){
$query = new Query();
$query->setTable('my_table', 't');
$query->addFields('t', ['field_1', 'field_2'])
if( $this->filtersAreEnabled()){
foreach( $this->getFilters() as $filter){
$query->addCondition($filter->field, $filter->value);
}
}
$query->sortBy('field_1');
$result = $query->execute();
foreach($result as $row){
echo $row->field_1 . ' : ' . $field_2;
}
}
}
Les commentaires
On ne vous le dira jamais assez, il faut commenter son code. Même si comprendre du code devient de plus en plus rapide avec l'expérience, il reste toujours plus long à déchiffrer que le langage naturel. D'autant plus qu'un bout de code ne dit pas tout, c'est souvent le "pourquoi" implicite qu'il est nécessaire d'ajouter en commentaires.
Il existe des commentaires formatés notamment pour les DocBlocks que je vous encourage à rendre exhaustifs, et qui, selon les langages, vous permettent de renseigner techniquement un élément (méthode, classe, variable). Ils ne sont pas pour autant suffisant dans la plupart des cas et ajouter une (ou plusieurs) ligne(s) de commentaire dans une méthode ou à un endroit particulier permet d'identifier rapidement le fonctionnement.
La plupart des développeurs commentent en anglais. C'est une pratique qui a un intérêt lorsqu'on développe pour un contexte large : un projet repris à l'internationale, un module proposé à une communauté. Attention toutefois, si votre développement a une portée limitée, et que vous êtes certains que tous les développeurs qui reprendront votre code utilise votre langue natale couramment, il est peut-être judicieux de préférer celle-ci. En effet, parfois les traductions sont approximatives et la lecture en devient floue pour le développeur tiers. De la même manière, il ne s'agit pas de perdre du temps en traduction approximative si vous n'êtes pas à l'aise avec l'anglais, le commentaire doit être clair, et ne doit pas desservir ce pour quoi il est fait : une meilleure compréhension du code.
Les coding standards
Il existe des codings standards dans la plupart des cas. Ces standards permettent d'uniformiser les syntaxes selon les langages ou les frameworks et permettent ainsi d'uniformiser la lecture du code, toujours dans l'optique de comprendre rapidement. Ils peuvent être particulièrement contraignants (notamment lorsqu'il s'agit de majuscule dans les commentaires) mais il vaut mieux les respecter si on veut proposer un développement cohérent dans un environnement donné.
Les espaces, les retours à la ligne, le nombre d'espace pour une tabulation, tout ceci peut paraitre illusoire dans le fonctionnement cependant, on se rend vite compte qu'on génère une petite frustration lorsqu'on arrive sur un code qui ne correspond pas à nos habitudes. Alors plutôt que de forcer les autres développeurs à utiliser vos habitudes, habituez vous à celles de la communauté en utilisant les codings standards. D'autant plus qu'il est simple de les mettre en place de manière automatique quand on a un bon IDE.
Commentaires
"
/**
* Je néglige volontairement le naming, les commentaires
* le typage et les dockbolcks pour vous concentrer sur l'atomisation.
**/
class Something
"
Heureusement qu'il y a le commentaire au dessus, que j'ai forcement pas vu en première lecture, j'allais plaisanter sur le naming :D.
Super article en tout cas :).
Effectivement :) J'essaie de concentrer mes exemples sur les points importants et donc je néglige certains aspects. Mais, oui, moi aussi je me trouve dégueu parfois ;)
Moi aussi j'aime exploser les fonctions !
Notamment en Drupal 8 ou beaucoup de methodes contiennent un petit paquet de code... je pense a toi Form API :)
Bravo pour ce super article en tous cas