Versioning sémantique lors de la correction d'un bug important

18

Je gère actuellement une bibliothèque qui a beaucoup d'utilité publique, et j'avais une question sur le versioning sémantique . Je veux refactoriser une partie assez importante de la bibliothèque qui est implémentée incorrectement - et a toujours été implémentée incorrectement. Mais cela signifierait des modifications de l'API publique, ce qui est une décision majeure.

Le changement que je veux faire tourne autour de la façon dont les itérateurs sont utilisés. Actuellement, les utilisateurs doivent faire ceci:

while ($element = $iterator->next()) {
   // ...
}

Ce qui est incorrect, du moins dans l' interface Iterator native de PHP . Je veux remplacer par ceci:

while ($iterator->valid()) {
   $element = $iterator->current();
   // ...
   $iterator->next();
}

ce qui est analogue à:

foreach ($iterator as $element) {
    // ...
}

Si vous regardez le guide de Tom sur le versioning sémantique, il déclare clairement que toute modification de l'API publique (c'est-à-dire celles qui ne sont pas rétrocompatibles), devrait justifier une version majeure. La bibliothèque passerait donc de 1.7.3 à 2.0.0, ce qui est pour moi un pas trop loin. Nous ne parlons que d'une fonctionnalité en cours de correction.

J'ai l'intention de publier la version 2.0.0, mais je pensais que c'était lorsque vous avez complètement réécrit la bibliothèque et mis en œuvre de nombreux changements d'API publics. L'introduction de ce refactoring justifie-t-elle une sortie de version majeure? Je ne vois vraiment pas comment cela se passe - je me sens plus à l'aise de le publier en tant que 1.8.0 ou 1.7.4. Quelqu'un a-t-il des conseils?

hohner
la source
Qu'est-ce qui vous empêche de conserver la compatibilité descendante?
mouviciel
À l'heure actuelle, la next()méthode est utilisée pour récupérer l'élément actuel ET déplacer le pointeur interne vers l'avant. Ce qui est faux. next()devrait déplacer le pointeur, et current()est utilisé pour récupérer ...
hohner
6
donc dans la nouvelle version, les gens ne devraient pas se soucier de la valeur de retour de next()celle qui déplace le pointeur, cela ne rompt vraiment pas la compatibilité
ratchet freak

Réponses:

29

Vous hésitez car vous ne voulez pas faire de versioning sémantique, vous voulez faire du "versioning supporting versioning". Vous vous attendez à ce qu'un numéro de version "2.0" indique au monde que vous avez maintenant un tas de nouvelles fonctionnalités intéressantes, pas que vous ayez changé l'API. C'est ok (de nombreux éditeurs de logiciels et / ou développeurs le font). À mon humble avis, vous avez les options suivantes:

  • s'en tenir au versioning sémantique et vivre avec le fait que vous devez changer le numéro de version en 2.0.0
  • changez votre schéma de versioning en 4 nombres. "1.1.7.3" est votre version maintenant, "1.2.0.0" la suivante après avoir changé l'API et "2.0.0.0" la première de la "toute nouvelle famille de produits 2.x"
  • rendre votre correctif rétrocompatible (donc ne changez pas la fonctionnalité de next, ajoutez simplement les fonctions validet current). Ensuite, vous pouvez utiliser "1.8.0" comme numéro de version suivant. Si vous pensez que changer le comportement de nextest vraiment important, faites-le dans 2.0.0.
Doc Brown
la source
Autant que la dernière option serait la solution parfaite: vous ne pouvez pas demander next()de continuer à faire ce qu'elle fait. Pour implémenter correctement la fonctionnalité, il doit faire quelque chose de différent. Donc, si je le rend rétrocompatible - la nouvelle fonctionnalité / correction sera également erronée et sapera tout le point du changement.
hohner
2
La suggestion plus large que vous faites dans votre troisième puce (rendant le correctif rétrocompatible) est une bonne chose à considérer. Cela peut ne pas fonctionner dans ce cas particulier, mais la technique globale mérite d'être considérée. La fonction finit par être plus complexe mais cela peut être une route réalisable.
Merci à tous: si je pouvais en accepter deux, je le ferais. J'ai fini par pirater la nouvelle next()méthode pour faire toutes les nouvelles fonctionnalités, plus ce qui était nécessaire pour rendre la compatibilité descendante. C'est un peu horrible de devoir ternir de nouvelles fonctionnalités comme celle-ci, mais bon ho.
hohner
10
@hohner: Il est maintenant temps également de documenter l'ancien comportement comme obsolète, vous pouvez donc le supprimer dans 2.0.0.
Jan Fabry
7

Tenez-vous au guide de Tom pour le versioning sémantique.

Toute modification importante d'une API publique doit être effectuée à l'un des deux points:

  1. Jamais
  2. Lors d'une mise à jour majeure

Soit dit en passant, mon vote est le premier. Mais je reconnais que cela ne convient qu'aux choses insignifiantes.

Le problème est de maintenir la compatibilité descendante et de vous assurer de ne pas casser les choses pour les utilisateurs précédents de votre API.

En substance, vous créez une erreur d'indexation pour vos utilisateurs qui ne connaissent pas la modification. Forcer une modification comme celle-ci oblige tous vos utilisateurs à effectuer les opérations suivantes:

  1. Codez le correctif pour utiliser la nouvelle approche
  2. Validez le correctif et assurez-vous qu'il n'a rien cassé
  3. Expédier les nouvelles versions de leur produit à leurs utilisateurs finaux

Cela peut potentiellement demander beaucoup d'efforts, surtout si l'on considère le peu de projets qui ont des cas de test en place afin de valider des changements comme celui-ci. La quantité d'efforts augmente lorsque vous considérez le nombre d'utilisateurs en aval de vos utilisateurs qui devront également mettre à jour leurs installations.

Pour quelque chose d'aussi petit, je le laisserais partir et ne m'embêterais pas avec.
Si cela vous dérange vraiment (ce qui semble ou si vous ne l'auriez pas demandé), je ferais ce qui suit.

  1. Créez la branche v2.0.0 dans votre arborescence de code
  2. Faire la première contribution à la branche v2.0.0, qui est ce changement
  3. Envoyez un aperçu à l' Release Notesavance de la diffusion du changement

Et puis soyez patient car il faudra un certain temps pour accumuler d'autres éléments qui justifient la mise à niveau du numéro de version vers une nouvelle version majeure. La notification avancée (partie 3) vous donne le temps de recevoir les commentaires des utilisateurs finaux pour savoir à quel point l'impact de ce changement va être.


Une solution alternative consiste à ajouter une nouvelle fonction qui fonctionne comme vous le souhaitez.

Si vous l'avez, vous devez foo()créer fooCorrect()afin de fournir le correctif, mais aussi de préserver pleinement la compatibilité descendante. Et à un moment donné, vous pouvez déconseiller foo()de faire savoir aux autres de ne pas l'utiliser.

Le défi est que vous trouverez quelque chose d'autre dans fooCorrect()lequel nécessite une mise à jour et que vous vous retrouvez avec fooCorrectedCorrect()ou un autre non-sens stupide.

Si vous voulez vraiment que cela soit corrigé maintenant, cette approche alternative est probablement la meilleure solution. Soyez conscient et méfiez-vous de créer de nombreuses fonctions supplémentaires de cette façon car cela rend l'API plus difficile à travailler. Et cette prise de conscience peut être suffisante pour éviter le pire de ces types de problèmes.

Mais cela pourrait être l'approche "la moins mauvaise" à considérer pour quelque chose de petit.


la source
Je suis d'accord avec toi. Le problème auquel je suis confronté est que je veux réécrire complètement la bibliothèque pour v2.0.0 (car il y a beaucoup de ces problèmes qui doivent être corrigés); donc je ne veux pas qu'un si petit changement comme itérateurs forme la base de ce grand changement. Donc mes options sont soit: ignorer ce bug, soit corriger le bug et le mettre dans une nouvelle version majeure?
hohner
@hohner - réponse mise à jour pour fournir une approche alternative avec la création de nouvelles fonctions. Gardez à l'esprit que de nombreuses nouvelles fonctions portant le même nom sont presque aussi mauvaises que de changer l'API elle-même.
3
@hohner: Toujours incorrect> incorrectement cohérent dans ce cas. Le comportement fonctionne toujours, il n'est tout simplement pas idiomatique. Considérez que si vous effectuez cette modification, vous cassez le code client. Faire cela sans avertissement ne sera pas apprécié.
Phoshi
@ GlenH7 Dans ce cas, l'utilisation d'une méthode nommée alternativement ne fonctionnera pas. L'itérateur natif de PHP s'appuie sur ces méthodes (c'est-à-dire next()non nextCorrect()). Je vais voir si je peux modifier next () afin qu'il soit rétrocompatible ET fonctionne lors de la mise en œuvre de l' Iteratorinterface.
hohner
1
@Phoshi Vous êtes sur place - je suis complètement d'accord maintenant. Il est maintenant temps d'essayer de coder l'impossible: D
hohner