Quand a-t-on besoin d’un audit de code C++ ?
Par exemple lorsque la durée de correction des bogues, ou du développement de nouvelles fonctionnalités prend de plus en plus de temps, et vous ne comprenez pas pourquoi.
La question se pose: continue-t-on avec le code historique, ou alors recommençons nous depuis zéro ? Un audit de code C++ peut vous aider à répondre à cette question. Il vous dira notamment si:
- Tout le code est bon à jeter, autant repartir à zéro
- Il n’y a que certaines parties du code qui gagneraient à être refactorisées
- Votre code est OK au regard des standards de qualité.
Le périmètre
La première chose à vérifier est que votre code C++ a un périmètre adapté. Dans le monde de l’embarqué c’est important car:
- Un langage compilé tel que C++ n’a d’intérêt que pour les parties de votre application
- soumises à des contraintes temps réel,
- nécessitant l’utilisation de librairires spécifiques disponibles, uniquement en C ou C++.
- Hors de ces contraintes, la difficulté à déboguer du C++ devrait vous faire préférer un langage interprété
- Le meilleur code est le code déjà écrit. En d’autres termes, cela ne sert à rien de réinventer la roue pour des fonctionnalités dont les implémentations ont déjà été validées, et fonctionnent déjà sur des millions de dispositifs mobiles
La question du périmètre en appelle donc deux:
- Quel le code réellement métier, celui qui est spécifique à votre application, votre dispositif embarqué ?
- Quels traitements spécifiques de votre application sont soumis à des exigences temps réel, ou à d’autres contraintes (mémoire vive limitée, librairie spécifique, etc.)
La conception
Une fois la question du périmètre tranchée, vient celle des bonnes pratiques de conception. La première, puisqu’un bon code est un code testé, est de savoir si la conception a rendu le code testable.
En effet, à partir du moment où le code est divisé en classes, on devrait s’attendre à pouvoir tester unitairement les fonctions les plus compliquées. Dans la théorie oui, mais en pratique si la fonction interagit avec des objets de classes trop volumineuses, trop compliquées, il va être difficile de mettre en place lesdits tests, puisque ces derniers nécessiteraient des conditions parfois irréalisables.
Les bonnes pratiques pour éviter ces situations sont les suivantes:
- peu de méthodes publiques dans les classes
- proscrire les dépendances cycliques
- limiter les tailles d’héritage: de manière générale la composition est préférable à l’héritage
Un chapître à part concernant la conception est celui des fils d’exécutions, ou threads. Si la multiplication des threads est quelque chose d’inévitable dans un contexte de calcul parallèle ou de serveur Internet, il faut bien être consicent que leur prolifération incontrôlée va rendre votre code difficilement testable.
Un plus dans la conception est l’utilisation des patrons de conceptions, ou design pattern, mais ce n’est pas obligatoire pour avoir un code de qualité.
L’implémentation
Le critère de qualité d’un code est le plus souvent le fait que ses fonctions sont courtes, et surtout qu’elles sont simples. C’est-à-dire que leur complexité cyclomatique est faible.
Ainsi, couverture de test et analyse statique du code donnent une bonne image à l’auditeur de la qualité de votre code.
La documentation
La documentation c’est important bien entendu, mais ce qui l’est encore plus c’est l’endroit où elle se trouve:
- pour un exécutable, c’est une très bonne pratique qu’il puisse être appelé avec une option –help, afin que l’utilisateur du programme sache ce qu’il peut en attendre
- la documentation utile aux développeurs qui modifient le code doit se trouver avec le code, dans les mêmes répertoires
- l’historique du code doit être détaillé dans les commentaires de commit. Idéalement on devrait comprendre le pourquoi de chaque ligne de code.