Le suivi de la qualité du code par équipe

La qualité d’un logiciel s’évalue selon plusieurs critères : est-ce qu’il répond bien aux besoins de l’utilisateur ? Est-il simple d’utilisation ? Plante-t-il toute les cinq minutes ? Est-ce qu’il garantit la sécurité des données qu’il manipule ? Comment se comporte-t-il en cas de pic de charge ? Son code respecte-t-il les bonnes pratiques de développement, est-il maintenable et évolutif ?

Cet article concerne les dernières questions, celles relatives à la qualité du code. Il présente la façon dont nous utilisons des outils comme SonarQube et Power BI pour que chaque équipe de développeurs puisse surveiller la qualité du code de ses applications.

Mesurer la qualité du code — la qualimétrie

Nous utilisons SonarQube, un outil d’analyse statique de code, pour obtenir différentes mesures et informations sur le code source de nos applications, telles que :

  • La liste des violations de nos règles de codage (les « issues »). Chaque violation a une sévérité qui aide à la prioriser : de « Blocker » (la plus sévère) à « Info » (la moins sévère).
  • La dette technique : ici il s’agit du temps estimé par l’outil pour corriger toutes les violations détectées.
  • Le taux de duplication de code dans l’application.
  • Le taux de couverture de code par les tests unitaires : le pourcentage de lignes de code de l’application effectivement exécutées lors des tests unitaires.
  • La complexité du code : une valeur élevée de cet indicateur permet de détecter les méthodes contenant beaucoup de structures de contrôles (blocs if/else et autres switchs), ce qui engendre plus de chemins d’exécution possibles et les rend plus compliquées à tester et à maintenir.

Nous avons programmé pour chacune de nos applications l’exécution d’un build qui effectue la compilation des sources, l’exécution des tests unitaires et l’analyse SonarQube, et ceci toutes les nuits. De cette façon nous avons une mesure quotidienne de la qualité de chaque application, dont nous pouvons suivre l’évolution dans le temps.

Le build de l’application Dematerialization.Api qui effectue l’analyse SonarQube toutes les nuits à 3h30

Le résumé de l’analyse de code d’une application par SonarQube

Le contexte de Younited Credit

Notre système d’information est composé de plus d’une centaine d’applications. Nous voulons qu’elles soient de la meilleure qualité possible, en nous concentrant initialement sur deux indicateurs : la dette technique, et le taux de couverture de code par les tests unitaires. La première doit être aussi faible que possible, tandis que le second doit être « suffisamment élevé » (nous n’avons pas défini de pourcentage cible, il dépend de chaque application). Nous voulons surtout vérifier que ces indicateurs ne se détériorent pas dans le temps : le nombre d’issues ne doit pas exploser, tandis que le taux de couverture de code ne doit pas s’effondrer.

Chez Younited Credit, chaque équipe de développeurs est responsable d’un sous-ensemble d’applications. Un même développeur n’est donc vraiment concerné que par la qualité de ses applications, et il est important qu’il puisse avoir une vue agrégée de la qualité de leur code.

En cherchant à répondre à ce besoin nous avons atteint une des limites actuelles de la version gratuite de SonarQube : elle ne permet pas de créer des groupes d’applications pour obtenir des mesures consolidées. Le plugin Governance présent dans l’édition Enterprise de SonarQube propose bien cette fonctionnalité, mais nous n’avons pas le budget nécessaire pour nous l’offrir.

Permettre à chaque équipe de suivre la qualité de ses applications

Heureusement pour nous, SonarQube expose une web API riche qui permet d’extraire simplement des données de sa base, et donc l’historique des analyses de chaque application.

Nous avons utilisé l’API de SonarQube pour récupérer :

  • La liste de tous les projets analysés.
  • Pour chaque projet analysé : l’historique des mesures qui nous intéressent (la dette technique et la couverture de code) depuis un mois.

A l’aide de Power BI, nous avons ensuite regroupé les mesures des applications par équipe et construit un rapport à destination des développeurs. Ce rapport Power BI est programmé pour se rafraîchir tous les matins afin de consolider les données des analyses SonarQube de la nuit.

Création d’un utilisateur SonarQube dédié au reporting

La web API de SonarQube est sécurisée, vous aurez besoin d’ajouter à chaque requête HTTP les informations authentifiant un utilisateur autorisé à appeler l’API.

Plutôt qu’utiliser votre propre login, il vaut mieux créer un nouvel utilisateur dédié dans l’interface d’administration de SonarQube.

Création d’un rapport Power BI alimenté par la web API de SonarQube

Power BI est un outil de reporting de Microsoft, il peut se connecter à de nombreux types de sources de données (base de données, fichiers, web API, etc.). Il nous permettra de créer un rapport qui récupère l’historique des métriques qui nous intéressent dans SonarQube, et de construire des vues de ces données agrégées par équipe.

Création de la source de données

La source de données que l’on souhaite doit correspondre à un tableau dont chaque ligne contient les valeurs des métriques pour une application et une date données.

Exemple :

Créer une fonction retournant l’historique des mesures d’une application

Dans l’éditeur de requêtes, nous allons d’abord créer une fonction qui prend en paramètres les éléments suivants :

  • projectKey : l’identifiant d’une application dans SonarQube
  • metrics : un tableau représentant la liste des métriques dont nous voulons l’historique. Dans notre cas, le tableau contiendra « sqale_index », « lines_to_cover » et « unconvered_lines ».
  • monthsAgo : le nombre de mois d’historique que nous voulons récupérer.

La fonction implémentera l’appel à l’API de SonarQube pour télécharger l’historique des mesures des métriques renseignées pour le projet donné. Chaque ligne retournée représentera la valeur d’une métrique du projet à une date donnée.

let history = (projectKey, metrics, monthsAgo) =>
let
DateTimeFrom=Date.AddMonths(DateTime.Date(DateTime.LocalNow()), -monthsAgo),
Source = Json.Document(
Web.Contents(
"https://<URL de votre service SonarQube>/api",
[
RelativePath="measures/search_history",
Query=
[
component=projectKey,
metrics=Text.Combine(metrics, ","),
from=Date.ToText(DateTimeFrom, "yyyy-MM-ddT00:00:00+0100")
]
])
),
measures = Source[measures],
#"Converti en table" = Table.FromList(measures, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Column1 développé" = Table.ExpandRecordColumn(#"Converti en table", "Column1", {"metric", "history"}, {"Column1.metric", "Column1.history"}),
#"Column1.history développé" = Table.ExpandListColumn(#"Column1 développé", "Column1.history"),
#"Column1.history développé1" = Table.ExpandRecordColumn(#"Column1.history développé", "Column1.history", {"date", "value"}, {"Column1.history.date", "Column1.history.value"})
in
#"Column1.history développé1"
in history

Et voici le résultat :

Exemple de récupération de mesures de qualité d’une application

Créer une requête retournant l’historique des mesures de toutes les applications

Maintenant qu’on dispose d’une fonction capable de retourner l’historique des mesures d’une application donnée, il reste à l’appeler pour toutes les applications.

Voici les grandes étapes de la requête :

1. Appeler la web API de SonarQube pour récupérer la liste de tous les projets (un projet représente une application analysée dans notre cas).

Résultat :

2. Pour chaque projet, appeler la fonction « project-measures-history » définie précédemment. Le résultat est un tableau dont chaque ligne représente la valeur d’une métrique pour une application et une date données.

Résultat :

3. La suite de la requête consiste à transformer ce tableau pour que chaque ligne contienne la valeur de toutes les métriques pour une application et une date données.

let
Source = Json.Document(
Web.Contents(
"https://<URL de votre service SonarQube>/api",
[
RelativePath="projects/index"
])),
#"Converti en table" = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Column1 développé" = Table.ExpandRecordColumn(#"Converti en table", "Column1", {"id", "k", "nm", "sc", "qu"}, {"Column1.id", "Column1.k", "Column1.nm", "Column1.sc", "Column1.qu"}),
#"Fonction personnalisée appelée" = Table.AddColumn(#"Column1 développé", "project-measures-history", each #"project-measures-history"([Column1.k], metrics, monthsAgo)),
#"project-measures-history développé" = Table.ExpandTableColumn(#"Fonction personnalisée appelée", "project-measures-history", {"Column1.metric", "Column1.history.date", "Column1.history.value"}, {"project-measures-history.Column1.metric", "project-measures-history.Column1.history.date", "project-measures-history.Column1.history.value"}),
#"Colonnes supprimées" = Table.RemoveColumns(#"project-measures-history développé",{"Column1.id", "Column1.nm", "Column1.sc", "Column1.qu"}),
#"Colonnes renommées" = Table.RenameColumns(#"Colonnes supprimées",{{"Column1.k", "Project"}, {"project-measures-history.Column1.history.date", "Timestamp"}, {"project-measures-history.Column1.metric", "Metric"}, {"project-measures-history.Column1.history.value", "Value"}}),
#"Type modifié" = Table.TransformColumnTypes(#"Colonnes renommées",{{"Timestamp", type datetimezone}, {"Value", type number}}),
#"Colonne dynamique" = Table.Pivot(#"Type modifié", List.Distinct(#"Type modifié"[Metric]), "Metric", "Value", List.Sum),
#"Colonne en double" = Table.DuplicateColumn(#"Colonne dynamique", "Timestamp", "Timestamp - Copie"),
#"Colonnes renommées1" = Table.RenameColumns(#"Colonne en double",{{"Timestamp - Copie", "date"}}),
#"Colonne en double1" = Table.DuplicateColumn(#"Colonnes renommées1", "Timestamp", "Timestamp - Copie"),
#"Colonnes renommées2" = Table.RenameColumns(#"Colonne en double1",{{"Timestamp - Copie", "time"}}),
#"Heure extraite" = Table.TransformColumns(#"Colonnes renommées2",{{"time", DateTime.Time}}),
#"Date extraite" = Table.TransformColumns(#"Heure extraite",{{"date", DateTime.Date}})
in
#"Date extraite"

Visualisation des mesures

L’essentiel de la création du rapport est de définir sa source de données. Une fois que celle-ci est terminée, Power BI permet de créer rapidement différents types de visualisations sur l’ensemble des données (toutes les métriques de toutes les applications), ou une sous-partie (les métriques des applications d’une équipe en particulier).

Dans notre cas, nous voulons pour chaque équipe suivre l’évolution de la dette technique et du taux de couverture de code dans le temps, et identifier rapidement les applications dont la dette est importante ou dont le taux de couverture de code est trop faible.

La figure suivante présente un exemple d’une page d’un tel rapport. Elle est dédiée aux applications de notre équipe « Finance/Prêteurs/Clients » et contient les graphiques illustrant les variations de la dette technique et du taux de couverture de code depuis un mois, et des graphiques affichant les dernières valeurs de la dette technique et du taux de couverture de code par application.

Quelques observations :

  • On constate un creux de la dette technique autour du 26 juin (de 31,62 jours à 31,11 jours). Elle peut s’expliquer par :
  • La correction de quelques issues, suivie par l’arrivée d’autres issues dans les modifications de code suivantes.
  • Ou alors, par un problème de compilation d’un des projets dont la dette technique est de 31,62–31,11 = 0,51 jours, qui n’aurait donc pas pu être analysé par SonarQube.
  • Le graphe en haut à droite permet de voir que les applications FundsManagement.Web et Batchapp sont les plus endettées.
  • Celui au milieu à droite indique que les applications FundsManagement et Accounting.Web sont les moins testées.

Les données proviennent de la source créée précédemment, filtrée pour n’afficher que les informations des applications de l’équipe « Finance/Prêteurs/Clients ». Les autres pages du rapport sont construites sur le même principe et à partir de la même source de données, et chacune peut être personnalisée pour afficher les données de la façon qui convient le mieux à l’équipe.

Pour conclure

En programmant des analyses du code de nos applications toutes les nuits et en utilisant l’API de SonarQube, nous avons pu automatiser la construction de rapports de qualité de code personnalisés. Mais le fait de mesurer et de suivre la qualité du code est juste un outil, il ne suffit pas en lui-même à l’améliorer.

Enfin, la qualité du code seule ne garantit pas forcément la qualité globale d’une application. Un logiciel dont le code est impeccable peut être malgré tout inutilisable ou truffé de bugs, et la progression dans ces domaines-là nécessite d’autres outils que ceux présentés ici.


Originally published at blog-tech.younited-credit.com.