16 ans après son premier commit, SQLite est le moteur de base de données le plus déployé au monde. Un tel projet open source est une excellente ressource pour apprendre à rendre le code facile à comprendre et à maintenir.
Explorons quelques faits sur la base de code de SQLite. Pour cela, commençons par l'extrait de code suivant :

Voici quelques observations sur cette fonction :
- La fonction est déclarée static.
- La fonction retourne un code d'erreur.
- La fonction n'a que quelques paramètres.
- La fonction sort le plus tôt possible.
- Des assertions sont utilisées pour vérifier certaines conditions.
- Aucune variable globale n'est utilisée.
- Les noms de variables sont faciles à comprendre.
- La méthode est courte.
- Le corps ne contient aucun commentaire superflu.
- Le corps de la fonction est bien indenté.
En explorant le code source de SQLite, on remarque la cohérence de l'implémentation. Les mêmes bonnes pratiques sont appliquées à chaque fonction.
Voici quelques bonnes pratiques à tirer de la base de code de SQLite :
Utilisez des structs pour stocker votre modèle de données
En programmation C, les fonctions utilisent des variables pour effectuer leurs traitements ; ces variables peuvent être :
- Des variables statiques.
- Des variables globales.
- Des variables locales.
- Des variables issues de structs.
Chaque projet possède son modèle de données, qui peut être utilisé par de nombreux fichiers sources. Utiliser des variables globales est une solution, mais pas une bonne ; utiliser des structs pour regrouper les données est préférable.
Cherchons à l'aide de CQLinq et de CppDepend les structs définies :

De nombreuses structs sont utilisées pour spécifier le modèle de données.
Gardez des fonctions courtes et efficaces
Voici, tiré de la page sur le style de codage du noyau Linux, un conseil sur la longueur des fonctions :
Functions should be short and sweet, and do just one thing. They should
fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24,
as we all know), and do one thing and do that well.
The maximum length of a function is inversely proportional to the
complexity and indentation level of that function. So, if you have a
conceptually simple function that is just one long (but simple)
case-statement, where you have to do lots of small things for a lot of
different cases, it's OK to have a longer function.Cherchons les fonctions dont le nombre de lignes de code est inférieur à 30.

Plus de 90 % des fonctions comptent moins de 30 lignes de code.
Encapsulation
L'encapsulation consiste à masquer les fonctions et les données internes à une implémentation. En C, l'encapsulation s'effectue à l'aide du mot-clé static. Ces entités sont appelées fonctions et variables à portée de fichier.
Cherchons toutes les fonctions statiques en exécutant la requête CQLinq suivante.

Comme on peut le constater, de nombreuses fonctions sont déclarées static.
Le nombre de paramètres des fonctions
Les fonctions où NbParameters > 8 peuvent être pénibles à appeler et dégrader les performances. Une alternative consiste à fournir une structure dédiée au passage des arguments.

Seules quelques fonctions ont plus de 8 paramètres.
Le nombre de variables locales
Les fonctions où NbVariables est supérieur à 8 sont difficiles à comprendre et à maintenir. Les fonctions où NbVariables dépasse 15 sont extrêmement complexes et devraient être scindées en méthodes plus petites (sauf si elles sont générées automatiquement par un outil).

Seules quelques fonctions ont plus de 15 variables locales.
Évitez de définir des fonctions complexes
De nombreuses métriques permettent de détecter les fonctions complexes ; NBLinesOfCode, le nombre de paramètres et le nombre de variables locales sont les plus basiques.
Il existe d’autres métriques intéressantes pour détecter les fonctions complexes :
- La complexité cyclomatique est une métrique logicielle procédurale populaire, égale au nombre de décisions pouvant être prises dans une procédure.
- La profondeur d'imbrication (Nesting Depth) est une métrique définie sur les méthodes qui correspond à la profondeur maximale de la portée la plus imbriquée dans le corps d'une méthode.
- Max Nested Loops est égal au niveau maximal d’imbrication des boucles dans une fonction.
La valeur maximale acceptable pour ces métriques dépend des choix de l'équipe ; il n'existe pas de valeurs standard.
Cherchons les fonctions candidates à la refactorisation :

Très peu de fonctions peuvent être considérées comme complexes.
Soyez const-correct
Le C fournit le mot-clé const pour permettre de passer en paramètres des objets qui ne peuvent pas changer, et pour indiquer quand une méthode ne modifie pas son objet. Utiliser const à tous les bons endroits s'appelle la « const-correctness ». C'est difficile au début, mais l'usage de const resserre vraiment votre style de codage.
Cherchons les fonctions ayant au moins un paramètre const :

Le couplage des fonctions
Les fonctions qui en utilisent beaucoup d'autres sont très difficiles à comprendre et à maintenir. Il est conseillé de minimiser le couplage efférent de vos fonctions.
Dans SQLite, très peu de fonctions présentent un couplage efférent élevé :

Sortez d'une fonction le plus tôt possible
Les sorties précoces d'une fonction, en particulier via des clauses de garde en haut de celle-ci, sont à privilégier, car elles simplifient la logique plus bas dans la fonction.
Dans le code source de SQLite, cette bonne pratique est appliquée à la quasi-totalité des fonctions.
Conclusion
Explorer des projets open source bien connus est toujours un bon moyen d'élever son niveau en programmation. Pas besoin de télécharger et de compiler le projet — vous pouvez simplement explorer le code sur GitHub.
