Gestion sémantique de version 1.0.0

Dans le monde de la gestion des logiciels, il existe un endroit redouté appelé « l’enfer des dépendances » (de l’anglais dependency hell). Plus votre système se développe et plus vous intégrez de composants dans votre logiciel, plus vous êtes susceptible de vous trouver un jour dans cet abîme de désespoir.

Dans les systèmes comportant de nombreuses dépendances, publier une nouvelle version d’un composant peut vite devenir un cauchemar. Si les règles de dépendance sont trop strictes, vous risquez de verrouiller vos versions (incapacité de mettre à jour un composant sans avoir à publier une nouvelle version de chaque composant qui en dépend). Si les règles de dépendances sont trop lâches, vous allez inévitablement subir la promiscuité de version (supposer une compatibilité avec plus de futures versions que raisonnable). L’enfer des dépendances est l’endroit où vous vous trouvez lorsque vous êtes bloqué dans une version et/ou qu’une incompatibilité de version vous empêche d’avancer sans risque dans votre projet.

Comme solution à ce problème, je propose un ensemble de règles et d’exigences simples qui dictent la façon dont les numéros de version sont attribués et incrémentés. Pour que ce système fonctionne, vous devez d’abord déclarer une API publique. Il peut s’agir d’un document ou de règles imposées par le code lui-même. Quoiqu’il en soit, il est important que cette API soit claire et précise. Une fois prête, vous communiquez ses modifications par des incrémentations successives de son numéro de version. Considérons le format de version X.Y.Z où X, Y et Z identifient la version (Majeure.Mineure.Corrective). Les corrections qui n’affectent pas l’API incrémentent le dernier identifiant qui est l’identifiant de version de correction. Lors d’ajouts ou de modifications rétrocompatibles de l’API, il faut incrémenter l’identifiant de version mineure. Enfin, pour des modifications non rétrocompatibles, il faut incrémenter l’identifiant de version majeure.

J’appelle ce système « gestion sémantique de version ». Avec ce système, les numéros de version, et la façon dont ils changent, donnent du sens au code sous-jacent et à ce qui a été modifié d’une version à l’autre.

Spécification de la gestion sémantique de version (SemVer)

Les mots clés “DOIT”, “NE DOIT PAS”, “OBLIGATOIRE”, “DEVRA”, “NE DEVRA PAS”, “DEVRAIT”, “NE DEVRAIT PAS”, “RECOMMANDÉ”, “PEUT”, et “OPTIONNEL” dans ce document doivent être interprétés comme décrit dans la RFC 2119.

  1. Tout logiciel utilisant la gestion sémantique de version DOIT déclarer une API publique. Cette API peut être déclarée dans le code lui-même ou dans un document. Dans tous les cas, elle doit être précise et claire.

  2. Un numéro de version standard DOIT prendre la forme X.Y.Z où X, Y et Z sont des entiers. X représente l’identifiant de version majeure, Y représente l’identifiant de version mineure et Z l’identifiant de version de correction. Chaque élément DOIT s’incrémenter numériquement par incréments de un. Exemple : 1.9.0 -> 1.10.0 -> 1.11.0.

  3. Quand l’identifiant de version majeure est incrémenté, les identifiants de version mineure et de correction DOIVENT être remis à zéro. Quand un identifiant de version mineure est incrémenté, l’identifiant de version de correction DOIT être remis à zéro. Exemples : 1.1.3 -> 2.0.0 et 2.1.7 -> 2.2.0.

  4. Une version de pré-livraison PEUT être notée par l’ajout d’une chaîne de caractères arbitraire immédiatement à la suite de l’identifiant de version de correction et d’un trait d’union. La chaîne de caractères DOIT être seulement composée de caractères alphanumériques et/ou de traits d’union [0-9A-Za-z-]. Les versions de pré-livraison satisfont le schéma d’une version normale mais ont une plus faible priorité que la version correspondante (sans la chaîne de caractères). La priorité DEVRAIT ensuite être déterminée dans l’ordre lexicographique ASCII ascendant. Exemple : 1.0.0-alpha1 < 1.0.0-beta1 < 1.0.0-beta2 < 1.0.0-rc1 < 1.0.0.

  5. Une fois qu’un composant est publié, le contenu de sa version NE DOIT PAS être modifié. Toute modification DOIT être publiée dans une nouvelle version.

  6. L’identifiant de version majeure zéro (0.y.z) est destiné au développement initial. Tout ou partie peut être modifié à tout moment. L’API publique ne devrait pas être considérée comme stable.

  7. La version 1.0.0 définit l’API publique. La façon dont le numéro de version est incrémenté après cette publication est dépendante de cette API publique et de ses évolutions.

  8. L’identifiant de version de correction Z (x.y.Z | x > 0) DOIT être incrémenté si seules des corrections rétrocompatibles sont introduites. Une correction est définie comme un changement interne qui corrige un comportement incorrect.

  9. L’identifiant de version mineure Y (x.Y.z | x > 0) DOIT être incrémenté si de nouvelles fonctionnalités rétrocompatibles sont introduites dans l’API publique. Il PEUT être incrémenté si de nouvelles fonctionnalités ou améliorations substantielles sont introduites dans le code privé. Il PEUT inclure dans le même temps des corrections. L’identifiant de version de correction DOIT être remis à zéro lorsque l’identifiant de version mineure est incrémenté.

  10. L’identifiant de version majeure X (X.y.z | X > 0) DOIT être incrémenté si des changements non rétrocompatibles sont introduits dans l’API publique. Cela PEUT inclure dans le même temps des changements mineurs et des corrections. Les identifiants de version mineure et de correction DOIVENT être remis à zéro quand l’identifiant de version majeure est incrémenté.

Spécification de la Labellisation (SemVerTag)

Cette sous-spécification DEVRAIT être utilisée si vous utilisez un système de contrôle de version (Git, Mercurial, SVN, etc.) pour sauvegarder votre code source. Un tel système permet d’automatiser des outils pour inspecter vos composants et déterminer votre compatibilité SemVer dans les versions publiées.

  1. Lorsque vous labellisez vos publications dans un système de contrôle de version, le label pour une version donnée DOIT être “vX.Y.Z”. Exemple : “v3.1.0”.

  2. La première révision qui introduit la compatibilité SemVer DEVRAIT être labellisée “semver”. Cela permet aux projets préexistants de marquer leur compatibilité. Cela permet également aux outils automatisés de reconnaître cet événement.

Pourquoi utiliser la gestion sémantique de version ?

Il ne s’agit pas d’une idée nouvelle ou révolutionnaire. En fait, vous faites probablement déjà quelque chose d’approchant. Et le problème vient du fait que « quelque chose de proche » n’est pas suffisant. Sans conformité avec une méthode formelle, les numéros de version deviennent inutilisables pour la gestion de vos dépendances. En donnant un nom et une définition claire aux idées exposées ci-dessus, il devient facile de communiquer vos intentions aux utilisateurs de votre logiciel. Une fois que vos intentions sont claires, une spécification souple (mais pas « trop » souple) des dépendances peut être réalisée.

Un exemple simple permet de montrer comment la gestion sémantique de version peut faire de l’enfer des dépendances, une chose du passé. Considérons une bibliothèque appelée « CamionDePompier ». Elle nécessite un composant appelé « Échelle » dont la version est gérée sémantiquement. Lorsque la librairie CamionDePompier est créée, Échelle en est à sa version 3.1.0. Et puisque CamionDePompier utilise des fonctionnalités qui ont été introduites en 3.1.0, vous pouvez spécifier, sans courir le moindre risque, une dépendance vers Échelle plus grande ou égale à 3.1.0 mais inférieure à 4.0.0. Maintenant, lorsque les versions 3.1.1 et 3.2.0 de Échelle seront disponibles, vous pourrez les publier dans votre système de gestion de dépendances en sachant qu’elles seront compatibles avec les logiciels existants qui en dépendent.

En tant que développeur responsable, bien entendu, vous voudrez vérifier que toute mise à jour de composant fonctionne comme annoncée. Dans la réalité, les choses ne sont pas forcément toujours très cohérentes ; il n’y a donc rien d’autre à faire que de rester vigilant. Ce que vous pouvez cependant faire est de laisser la gestion sémantique de version vous fournir une manière saine de publier et mettre à jour vos composants et ainsi ne pas avoir besoin de déployer de nouvelles versions de vos sous-composants vous permettant ainsi d’économiser du temps et du souci.

Si tout cela vous semble intéressant, tout ce que vous avez à faire pour commencer à utiliser la gestion sémantique de version est de déclarer que vous le faites et d’en suivre les règles. Ajoutez ensuite un lien vers ce site web dans votre README pour que d’autres puissent en connaître les règles et en bénéficier.

FAQ

Comment dois-je gérer les révisions dans la phase initiale de développement 0.y.z ?

La chose la plus simple à faire est de commencer vos développements avec une version initiale à 0.1.0 puis d’incrémenter l’identifiant de version mineure pour chaque nouvelle publication.

Comment savoir quand publier la version 1.0.0 ?

Si votre logiciel est utilisé en environnement de production ou que vous avez une API stable de laquelle des utilisateurs ont commencé à dépendre, vous devriez probablement déjà être en version 1.0.0. Et si vous vous faites déjà du souci pour la rétro-compatibilité, vous devriez également avoir dépassé la 1.0.0.

N’est-ce pas décourager le développement rapide et les itérations courtes ?

La version majeure zéro est faite pour un développement rapide. Si vous changez votre API tous les jours, vous devriez toujours être en version 0.y.z ou sur une branche de développement séparée en préparant la prochaine version majeure.

Si le moindre changement non rétrocompatible de l’API publique nécessite une incrémentation de l’identifiant de version majeure, ne vais-je pas me retrouver à la version 42.0.0 très rapidement ?

C’est une question de développement responsable et d’anticipation. Les changements incompatibles ne doivent pas être introduits à la légère dans du logiciel dont beaucoup de code source dépend. Le coût d’une mise à jour vers une nouvelle version peut être important. Le besoin de faire évoluer la version majeure pour publier des changements non rétrocompatibles signifie que vous aurez mesuré les implications de vos modifications et évalué le rapport entre leur coût et leurs bénéfices.

Documenter l’ensemble de l’API publique demande trop de travail !

Il est de votre responsabilité en tant que développeur professionnel de documenter correctement le logiciel qui est destiné à être utilisé par d’autres. Gérer la complexité d’un logiciel est un élément extrêmement important pour maintenir son projet efficacement. Cela devient difficile à faire quand personne ne sait comment utiliser votre logiciel ou ne connaît les bonnes méthodes à appeler. Sur le long terme, la gestion sémantique de version et les efforts dans la conservation d’une API publique bien définie permettront à tout le monde d’avancer sans problème.

Que faire si j’ai accidentellement publié un changement non rétrocompatible dans une version mineure ?

Dès que vous réalisez que vous avez cassé votre gestion sémantique de version, corrigez le problème et publiez une nouvelle version mineure qui rétablit la compatibilité avec les versions précédentes. Souvenez-vous que même dans de telles circonstances, il est inacceptable de modifier une version déjà publiée. Mettez à jour la documentation en signalant la version défectueuse et informez vos utilisateurs de ce problème.

Que dois-je faire lorsque je mets à jour mes propres dépendances sans changer l’API publique ?

Cela peut être considéré comme compatible dans la mesure où cela n’affecte pas l’API publique. Les logiciels qui dépendent des mêmes librairies que votre composant devraient avoir leur propre spécification de dépendances et l’auteur remarquera ainsi tout conflit. Pour déterminer si la modification est de niveau correctif ou mineur, il faut vous poser la question de savoir si vous avez mis à jour vos dépendances pour corriger un bug ou pour introduire une nouvelle fonctionnalité. Je considère généralement l’ajout de nouveau code comme la deuxième option ce qui sous-entend évidemment un incrément de l’identifiant de version mineure.

Que dois-je faire si l’anomalie qui est en cours de correction remet le code en conformité avec l’API publique (alors que le code n’était déjà plus synchronisé avec la documentation) ?

C’est à vous de décider. Si vous avez une large audience qui sera considérablement affectée par un retour à ce que l’API publique prévoyait, alors il est peut-être préférable de publier une version majeure, même si la livraison pourrait être uniquement considérée comme un correctif. Souvenez-vous encore que la gestion sémantique de version consiste essentiellement à transmettre du sens dans la façon dont le numéro de version change. Si ces changements sont importants pour vos utilisateurs, utilisez les numéros de version pour les en informer.

À propos

La spécification de la gestion sémantique de version est écrite par Tom Preston-Werner, inventeur de Gravatars et cofondateur de GitHub.

Si vous souhaitez laisser des commentaires, veuillez ouvrir un ticket sur GitHub.

Licence

Creative Commons ― CC BY 3.0 http://creativecommons.org/licenses/by/3.0/