Seize ans après son premier commit, SQLite est le moteur de base de données le plus déployé au monde. Un projet open source comme celui-ci est une excellente ressource pour apprendre à rendre votre code facile à comprendre et à maintenir.
Découvrons quelques faits sur la base de code de SQLite, en commençant 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 parcourant le code source de SQLite, on constate la cohérence de l'implémentation. Les mêmes règles de 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 option, mais ce n'en est pas une bonne ; regrouper les données dans des structs est généralement 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 un conseil sur la longueur des fonctions, tiré de la page sur le style de codage du noyau Linux:
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 autre solution 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 peuvent être difficiles à comprendre et à maintenir. Les fonctions où NbVariables dépasse 15 sont extrêmement complexes et devraient être scindées en fonctions 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 existent pour détecter les fonctions complexes ; NbLinesOfCode, le nombre de paramètres et le nombre de variables locales en font partie des 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 dont le résultat est égal au nombre de décisions qui peuvent être prises dans une procédure.
- La profondeur d'imbrication (Nesting Depth) est une métrique définie pour les fonctions qui représente la profondeur maximale des portées imbriquées dans le corps d'une fonction.
- La profondeur maximale des boucles imbriquées (Max Nested Loop) est le niveau maximal d'imbrication des boucles dans une fonction.
La valeur maximale tolérée pour ces métriques dépend surtout des choix de l'équipe, car il n'existe pas de valeurs standard.
Cherchons les fonctions potentiellement à refactoriser :

Très peu de fonctions peuvent être considérées comme complexes.
Soyez const-correct
Le C fournit le mot-clé const pour indiquer qu'un objet ne peut pas changer et qu'une fonction ne modifie pas son paramètre. 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 appellent beaucoup d'autres peuvent être difficiles à comprendre et à maintenir. Il est recommandé de minimiser le couplage efférent de vos fonctions.
Pour 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'améliorer ses compétences en programmation. Pas besoin de télécharger et de compiler le projet — vous pouvez simplement explorer le code sur GitHub.
