Les métriques de code expliquées : qu'est-ce que la complexité cyclomatique ?
Vous examinez une pull request et une fonction s’est étoffée pour gérer huit conditions différentes — rôles utilisateurs, feature flags, cas limites, solutions de repli. Elle passe les tests. Elle fonctionne. Mais quelque chose cloche. Ce ressenti porte un nom, et un chiffre.
La complexité cyclomatique est une métrique de qualité du code qui mesure le nombre de chemins d’exécution indépendants qui traversent une fonction. Plus le chiffre est élevé, plus votre code contient de logique de branchement — et plus il devient difficile à lire, à tester et à maintenir.
Points clés à retenir
- La complexité cyclomatique comptabilise le nombre de chemins indépendants à travers une fonction, vous offrant un signal mesurable de la logique de branchement.
- Calculez-la rapidement en partant de 1 et en ajoutant 1 pour chaque instruction de branchement comme
if,&&,||,caseou un opérateur ternaire. - Elle se distingue de la complexité cognitive, qui mesure à quel point un code est difficile à lire pour un humain plutôt que le nombre de chemins qu’il contient.
- Des outils comme ESLint et SonarQube peuvent suivre la complexité automatiquement et signaler les fonctions qui dépassent un seuil configurable.
- Réduisez la complexité grâce aux guard clauses, aux fonctions auxiliaires extraites, aux variables booléennes descriptives et aux tables de correspondance.
Comment se calcule la complexité cyclomatique
Développée par Thomas McCabe en 1976, cette métrique dérive du graphe de flux de contrôle d’une fonction. La formule pratique pour un composant connexe unique est :
M = E − N + 2P
Où E est le nombre d’arêtes, N le nombre de nœuds, P le nombre de composants connexes (généralement 1 pour une fonction unique), et M le score de complexité.
Pour la plupart des développeurs JavaScript, il n’est pas nécessaire de calculer cela manuellement. Le raccourci : partir de 1, puis ajouter 1 pour chaque instruction de branchement — if, else if, &&, ||, for, while, case, opérateurs ternaires et clauses catch. Certains outils prennent aussi en compte des constructions comme l’optional chaining, les valeurs par défaut et l’affectation logique. Les règles exactes de calcul peuvent varier légèrement entre des outils comme ESLint et SonarQube.
Un exemple JavaScript : comment le branchement augmente la complexité
// Cyclomatic complexity: 1
function getDisplayName(user: User): string {
return user.name;
}
// Cyclomatic complexity: 6
function getDisplayName(user: User | null): string {
if (!user) return "Guest"; // +1
if (user.isAdmin) return "Admin"; // +1
if (user.displayName) return user.displayName; // +1
if (user.firstName && user.lastName) // +1 (if) +1 (&&)
return `${user.firstName} ${user.lastName}`;
return user.email;
}
Chaque condition ajoute une branche. Plus de branches signifient plus de chemins à tester — et davantage de façons dont un futur changement pourrait casser quelque chose de manière inattendue.
Ce schéma se retrouve constamment dans le code frontend : logique de rendu des composants React, reducers Redux avec de nombreux types d’actions, gestionnaires de validation de formulaire, et flux d’interface basés sur les permissions.
Complexité cyclomatique vs. complexité cognitive
Ces deux métriques sont liées mais distinctes. La complexité cyclomatique comptabilise les branches structurelles — c’est un indicateur de testabilité. La complexité cognitive (popularisée par SonarQube) mesure la difficulté qu’a un humain à lire le code, en pénalisant plus lourdement l’imbrication et les flux non linéaires.
Une fonction peut obtenir un faible score de complexité cyclomatique tout en restant difficile à suivre — par exemple, des appels de méthodes profondément chaînés sans variables intermédiaires. Les deux métriques sont utiles, et aucune ne raconte à elle seule toute l’histoire.
Discover how at OpenReplay.com.
La mesurer dans votre codebase JavaScript
Deux outils pratiques pour les équipes frontend :
- Règle
complexityd’ESLint — signale les fonctions au-delà d’un seuil configurable directement dans votre éditeur - SonarQube / SonarCloud — rapporte à la fois la complexité cyclomatique et cognitive sur l’ensemble de votre codebase
Configurez ESLint ainsi :
{
"rules": {
"complexity": ["warn", { "max": 10 }]
}
}
Le seuil est configurable — et devrait l’être. Un utilitaire de validation et un reducer Redux n’ont pas besoin du même plafond. Ajustez les seuils en fonction du contexte de votre code, et non d’une règle universelle.
Façons pratiques de réduire la complexité superflue
Lorsque le score d’une fonction grimpe, ces techniques aident :
- Extraire des fonctions — déplacer la logique distincte dans des fonctions auxiliaires nommées
- Utiliser des guard clauses — retourner tôt plutôt que d’imbriquer des conditions
- Simplifier les conditionnelles — remplacer les chaînes booléennes complexes par des variables descriptives
- Utiliser des tables de correspondance — remplacer les longues instructions
switchpar des objets ou desMap
L’objectif n’est pas un faible score en soi. C’est un code plus facile à tester, plus facile à modifier, et plus facile à comprendre pour le prochain développeur.
Conclusion
La complexité cyclomatique vous donne un signal concret et mesurable sur la logique de branchement de votre code. Utilisez ESLint ou SonarQube pour la suivre, définissez des seuils adaptés à votre codebase, et traitez les scores en hausse comme une invitation à refactoriser — pas comme une crise. Associez-la à la complexité cognitive pour obtenir une vision plus complète de la maintenabilité.
FAQ
Une recommandation courante est de maintenir les fonctions à 10 ou moins. Les scores de 1 à 10 sont considérés comme gérables, ceux entre 11 et 20 suggèrent que la fonction devient complexe, et tout ce qui dépasse 20 est généralement un bon candidat à la refactorisation. Le seuil approprié dépend du type de code, donc ajustez-le au contexte de votre équipe.
La complexité cyclomatique comptabilise chaque instruction de branchement de manière égale, indépendamment de son niveau d'imbrication. Une fonction avec trois instructions if à plat et une autre avec trois instructions if imbriquées peuvent avoir le même score. C'est l'une des raisons d'être de la complexité cognitive, car elle accorde un poids supplémentaire à l'imbrication et reflète mieux la difficulté de lecture du code.
Pas toujours. Un score élevé indique qu'une fonction mérite un examen plus attentif, mais certaines logiques sont véritablement riches en branches, comme les parseurs, les machines à états ou les pipelines de validation. Utilisez la métrique comme une invitation à la revue plutôt qu'une règle stricte. Si la fonction est bien testée, clairement écrite et stable, la refactoriser peut ajouter un risque sans bénéfice réel.
Le nombre de lignes de code mesure la taille, tandis que la complexité cyclomatique mesure les points de décision. Une fonction de 200 lignes sans branche a une complexité de 1, alors qu'une fonction de 20 lignes remplie de conditions peut obtenir un score bien plus élevé. La complexité est un meilleur prédicteur de la testabilité et de l'effort de maintenance, car elle reflète le nombre de chemins que vos tests doivent couvrir.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.