L'agence
WanadevStudio
Un historique de modifications clair pour tout le monde : changelog et conventional commits
Il y a 17 heures
Un changelog recense les modifications d'un projet version par version. Les conventional commits normalisent les messages de commits pour les rendre lisibles par tous, humains et outils. Voici comment mettre en place les deux, avec Changelogify, outil open source créé chez WanadevDigital.
Sommaire
- Introduction : le changelog en clair
- Mettre en place un changelog avec Changelogify
- Conventional commits
- Changelog + conventional commits : un historique lisible par tous
- Liens utiles
Introduction : le changelog en clair
Tout d’abord, un changelog (littéralement “journal des modifications”) représente une liste de changements apportés à un projet, ordonnés par version et, pour être plus précis encore, par type de modification. Cela permet de recenser toutes les modifications et nouveautés, quelles qu’elles soient, apportées au code et au projet au fil du temps.
Un changelog est utile pour tous les participants à un projet, de l’équipe de développement (et contributeurs externes éventuels) aux utilisateurs finaux, puisqu’il représente cet historique des évolutions dans un format compréhensible par tout le monde, de manière datée et versionnée.
Mettre en place un changelog avec Changelogify
Il existe des systèmes de changelog comme par exemple Changelogify pour gérer l’écriture progressive du changelog dans votre projet et éviter de faire les choses à la main. C’est ce système, conçu chez WanadevDigital, que nous utilisons dans le projet 3D XP pour Adeo. Ce qui était à la base un besoin d’expliquer clairement toutes les nouveautés et les partager à tous les utilisateurs est devenu une library open source utilisée quotidiennement sur notre projet, et utilisable partout, par tout le monde. Nous allons détailler par étapes son utilisation, le principe restant assez similaire à d’autres systèmes.
Comment fonctionne Changelogify
Tout d’abord, après installation de la lib, la commande npm run changelog init permet de créer la configuration initiale. Ensuite, en lançant npm run changelog add1, le terminal demande d’entrer un message textuel (indiquant le sujet de la modification), un type (de quel type de modification il s’agit, parmi plusieurs types prédéfinis) et un numéro de ticket/issue (rempli par défaut selon le préfixe de votre branche de travail courante).
Cela génère une entrée de changelog dans un petit fichier json. Il se présente dans un dossier changelogs/unreleased à la racine du projet, simplement sous la forme suivante :
{
"message": "Upgrade ESLint to version 10",
"type": "Meta",
"issue": "47"
}
Petit bonus que je recommande : on peut configurer la lib pour que le commit se fasse automatiquement, et bien entendu on peut définir un message de commit par défaut2, paramétrable avec le numéro de ticket/issue, par exemple.
Comme évoqué en introduction, le changelog est utile pour tout le monde, et en bout de chaîne il permet de notifier les clients ou utilisateurs finaux de toutes les nouveautés ou améliorations.
Prenons un exemple bien concret. Vous avez certainement déjà mis à jour des applications sur votre smartphone, il y a un petit encart “Nouveautés” pour chaque application dans la vue des mises à jour. Beaucoup d’applications ne mentionnent presque aucune information dans ces données-là : soit le changelog n’est pas mis à jour pendant très longtemps et c’est le même message qui apparaît à chaque mise à jour de l’appli (quand bien même cela corresponde à une montée de version bien plus ancienne), soit le message est très évasif (du genre “bug fixes and improvements”, autrement dit ça n’apporte aucune information particulière), soit c’est carrément vide. Pour l’utilisateur final, c’est quand même plus pratique de savoir quelles sont les nouveautés, les différences avec la version précédente, etc. Heureusement, on trouve aussi de vrais changelogs bien remplis, souvent dans les repositories GitHub, et aussi certains jeux vidéo ou logiciels (souvent payants), qui mettent beaucoup plus en valeur les nouveautés ou corrections. Dans le cas des dépôts GitHub, c’est bien pratique d’avoir un changelog précis lorsqu’on met à jour un package pour voir quels sont les éventuels breaking changes, par exemple.
1. Vous pouvez omettre le “add” si la commande par défaut dans la config est bien définie sur “add” (ce qui est le cas de base).
2. C’est encore mieux si le message est sous la forme d’un conventional commit, cf. partie suivante, ce qui est le cas par défaut.
Quand ajouter une entrée de changelog ?
Le plus souvent, il y aura une entrée de changelog par merge request/pull request, autrement dit un changelog par changement/évolution. Si une PR couvre plusieurs sujets distincts, on pourra ajouter autant d’entrées de changelog que nécessaire. Souvenez-vous que l’idée derrière cela est de recenser tous les sujets de modifications, donc ce n’est pas intrinsèquement lié au nombre de PRs ou de commits, même si la plupart du temps une PR se focalise sur un seul sujet (ou alors c’est pas super sympa pour les reviewers 😛).
Le message du changelog est censé être compréhensible par tout le monde, d’une équipe technique ou non, en tout cas le mieux possible (on peut toujours avoir des cas de refactoring ou d’aide au développement qui resteront plus axés technique, mais il faut rester clair dans tous les cas sur la description du message).
On peut noter quelques cas particuliers à garder en tête :
- une PR qui corrige ou complète une PR déjà mergée sur la même itération (la PR initiale a donc déjà ajouté un changelog sur le sujet) : dans ce cas, pas besoin de nouveau changelog
- tout patch doit avoir une entrée de changelog (au passage, évitez de pusher dans main, svp), sinon on risque d’avoir des “trous” dans le changelog : le fichier markdown global divise le changelog par versions du projet, donc on risquerait d’avoir un encart vide pour la version correspondant au patch.
Déterminer le Type
Les types de modification peuvent varier selon les projets, et vous pouvez bien sûr utiliser ceux que vous souhaitez, avec un degré de précision au choix. Pour rester générique, on peut suivre cette liste pour choisir le type dans le changelog :
- Added : nouvelle fonctionnalité, tout ce qui est ajouté et qui n'existait pas avant
- Changed : modification d'un comportement existant, d'une UI existante, d'une façon de faire. Aussi, refactoring.
- Removed : comportement ou feature supprimée, code retiré ; attention à noter cela potentiellement comme un breaking change
- Deprecated : code qui devient déprécié, plus utilisé, sera retiré dans un futur proche
- Fixed : correction d'un bug, d'un souci dans une UI, etc
- Security : patch lié à la sécurité (modification d'une regex foireuse ou mise à jour d'une library qui présentait une vulnérabilité critique, par exemple)
- Tests : ajout de tests, quels qu'ils soient (unitaires, end-to-end, etc) ; ou modification de tests
- Doc : ajout ou modification de fichiers de documentation
- Meta : mise à jour de packages, changements de configs pour le développement (Vite par exemple), correction de typos…
Encore une fois, on peut avoir d’autres types de modifications, mais ceux-ci couvrent déjà un bon éventail de possibilités.
Publication
Ensuite, lors de la création de tags pour créer une nouvelle version de votre projet (que ce soit un patch, une version mineure ou majeure), tous les fichiers de changelog individuels créés depuis la version précédente seront agglomérés automatiquement en un fichier global de changelog. Ce fichier, au format markdown, contiendra donc la liste de toutes les évolutions intégrées à cette nouvelle version, classées par type. La date est également ajoutée automatiquement pour chaque version. C’est ce fichier qui apparaîtra par exemple sur GitHub dans les logs des releases. C’est particulièrement utile pour toutes les personnes qui utilisent des libraries publiques, en particulier pour gérer les breaking changes des versions majeures, et voir tout de suite si ceux-ci impliqueraient des modifications dans le projet.
Alors, qu’attendez-vous pour avoir un vrai historique de modifications dans votre projet ?
Conventional commits
La syntaxe d'un conventional commit
Si le changelog permet de décrire pour tout le monde les nouveautés et modifications, ce n’est pas le seul outil pouvant décrire l’historique des modifications dans un projet. Côté code, et pour être bien plus précis et détaillé que le changelog, les commits font office d’historique à la fois daté, descriptif et concret, puisqu’ils sont associés directement au code modifié ainsi qu’au contenu du message.
L’idée des conventional commits consiste à décrire le sujet du commit en suivant une syntaxe précise, afin d’indiquer le type de modification en préfixe, la fonctionnalité concernée ainsi qu’une description, et quelques détails supplémentaires si besoin. On retrouve le même principe simple que pour le changelog pour décrire ce qui a été fait, la différence étant principalement qu’on ira plus dans le détail pour les commits, comme évoqué précédemment. Un conventional commit peut également mentionner un breaking change explicitement. On peut l’écrire par exemple sous la forme :
fix(api): handle error when 404 is encountered
feat(mailing): use id to find customer in database
BREAKING CHANGE: use of pseudo-ids is not available from now on.
Vous remarquerez que mes exemples sont particulièrement recherchés…
Dans la convention de base, on aura feat et fix comme types de modifications, utilisables dans les mêmes cas que pour le changelog, mais dans la convention commitlint, on pourra utiliser également :
- docs, test, avec les mêmes usages que pour le changelog
- revert : pour indiquer qu’on revert un commit effectué précédemment
- refactor : pour les sujets de refactoring sans impact sur le fonctionnel
- ci : pour tout changement sur les jobs de la CI, leur config, etc
- perf : concernant les changements impliquant une optimisation des performances
- style : typo, formatage du code (on trouve parfois une autre interprétation, probablement une confusion sur le terme, qui concernerait toute modification de rendu visuel, de CSS, sans impact sur le fonctionnel)
- build : pour un changement concernant les dépendances externes ou le système de build (comme une config Vite)
- chore : changement sur les outils, des fichiers annexes (ni code ni tests, type .gitignore) et tout ce qui ne correspondrait pas à un des types précédents
Gardons en tête que les conventional commits garantissent un standard sur le format du message, mais pas nécessairement sur une signification universelle pour chaque type proposé : même si la plupart du temps il n’y a pas trop d'ambiguïté, il peut y avoir parfois quelques confusions ou interprétations qui vont diverger, ou éventuellement plusieurs types qui correspondraient, auquel cas on choisit le plus pertinent. L’usage hésite parfois pour la distinction build/chore (chore n’étant pas présent dans la convention Angular sur laquelle commitlint est basée). Il faudra surtout que cela reste cohérent au sein d’un même projet, c’est le plus important.
Il est possible de ne pas se plier précisément à toutes les recommandations des conventional commits, mais mieux vaut instaurer une règle commune sur un même projet de façon à harmoniser les noms et messages de commits, pour mieux s’y retrouver. Dans tous les cas je recommande de conserver au moins la première ligne sous la forme <préfixe>(<feature_name>): <description> (les breaking changes pouvant de manière simple être indiqués dans le changelog et par une montée de version majeure).
Au-delà des commits
On pourrait générer un changelog à partir de commits, mais dans ce cas il faudrait que les commits correspondent à un change précis, ce qui n’est pas nécessairement, voire rarement, le cas ; donc à mon sens il est préférable de décorréler les deux. Le changelog décrit le résumé, le résultat final d’un développement, alors que les commits décrivent les étapes, le cheminement pour arriver à ce résultat. Le changelog ne s’attarde pas vraiment sur les détails d’implémentation, les commits peuvent en revanche décrire cela précisément.
Concernant le débug, par exemple dans le cas d’un git bisect3, lorsqu’on arrive sur le résultat avec le commit ayant introduit l’erreur, c’est toujours plus simple de tomber sur un message clair et précis, plus facile à remettre dans son contexte (au passage, c’est encore mieux si les commits sont unitaires, c’est-à-dire bien découpés et ciblés sur une modification en particulier : un git bisect a beaucoup moins d’efficacité si le commit trouvé contient des modifications sur une vingtaine de fichiers avec des centaines de lignes de code changées). Pour effectuer la correction, avoir le contexte ainsi que la raison des changements apportés, tout en étant sur un morceau de code précis, est un atout. Ainsi l’importance de la combinaison d’un message clair et d’une division des commits tendant vers l’unitaire reste présente dans le temps.
De plus, le fait d’avoir des noms de commits qui suivent un format précis et qui décrivent en quoi consistent réellement les modifications permet à des systèmes automatiques comme des modèles d’IA de mieux comprendre l’historique des modifications. Quand le changelog s’adresse principalement aux humains, les commits peuvent être utilisés aussi bien par des humains que des systèmes automatiques.
3. Commande git permettant de tester des versions antérieures et intermédiaires d’un projet par dichotomie de façon à retrouver le premier commit ayant introduit une erreur spécifique.
Changelog + conventional commits : un historique lisible par tous
La combinaison d’un système de changelog et de messages de commits suivant une même guideline permet d’avoir au final un historique de modifications clair pour tout le monde (c’est marrant, c’est le titre de l’article, bien foutu tout ça 😁). Il est donc facile d’identifier les différentes évolutions du projet et d’aller chercher des informations sur telle ou telle version, qu’on fasse partie du développement ou simplement utilisateur (breaking changes, nouveautés, corrections…). On peut également identifier précisément quand et comment certaines modifications ont été introduites, ce qui peut être bien pratique pour du débug, que ce soit via git bisect ou autrement.
Retrouvez Changelogify et nos autres projets open source sur le GitHub Wanadev.