Changements cassants — MRTK2

Les consommateurs de MRTK dépendent d’une surface d’API de mise en production stable, afin qu’ils puissent prendre des mises à jour de MRTK sans avoir à chaque fois d’importantes modifications cassants.

Cette page décrit notre politique actuelle concernant les changements cassants dans le MRTK, ainsi que certains objectifs à long terme concernant la façon dont nous pouvons mieux gérer le compromis entre maintenir les changements cassants à un niveau faible et être en mesure d’apporter les modifications techniques appropriées à long terme au code.

Qu’est-ce qu’un changement cassant ?

Une modification est une modification cassant si elle satisfait à l’une des conditions de la liste A ET remplit toutes les conditions de la liste B

Liste A

  • Ajout, suppression ou mise à jour d’un membre ou d’une fonction d’une interface (ou suppression/renommage de l’interface entière).
  • La suppression, la mise à jour (modification du type/définition, mise à jour privée ou interne) de tout membre ou fonction protégé ou public de classe. (ou suppression/renommage de l’ensemble de la classe).
  • Modification de l’ordre des événements déclenchés par une classe.
  • Renommage de n’importe quelle propriété privée SerializedField (sans balise FormerlySerializedAs correspondante) ou publique sur un ScriptableObject (en particulier les modifications apportées aux profils).
  • Modification du type d’un champ sur un ScriptableObject (en particulier les modifications apportées aux profils).
  • Mises à jour à l’espace de noms ou aux asmdefs d’une classe ou d’une interface.
  • Suppression de tout préfabriqué ou suppression d’un script sur l’objet de niveau supérieur d’un préfabriqué.

Liste B

  • La ressource en question se trouve dans le package de base (c’est-à-dire dans l’un des dossiers suivants) :

    • MRTK/Core
    • MRTK/Providers/
    • MRTK/Services/
    • MRTK/SDK/
    • MRTK/Extensions
  • La ressource en question n’appartient pas à l’espace de noms expérimental.

Important

Toute ressource qui se trouve dans le package d’exemples (c’est-à-dire une partie du dossier MRTK/Examples/) est susceptible d’être modifiée à tout moment, car les ressources sont conçues pour être copiées et vues par les consommateurs comme des « implémentations de référence », mais ne font pas partie de l’ensemble principal d’API et de ressources. Les ressources de l’espace de noms expérimental (ou, plus généralement, les fonctionnalités étiquetées comme expérimentales) sont celles qui sont publiées avant que toute diligence raisonnable n’ait été effectuée (c’est-à-dire les tests, l’itération de l’expérience utilisateur, la documentation) et qui sont publiées tôt pour obtenir des commentaires plus tôt. Toutefois, comme ils n’ont pas de tests et de documentation, et que nous n’avons probablement pas cloué toutes les interactions et les conceptions, nous les publions dans un état où le public doit supposer qu’ils peuvent et vont changer (par exemple, être modifiés, complètement supprimés, etc.).

Pour plus d’informations, consultez Fonctionnalités expérimentales .

Étant donné que la surface d’exposition pour les changements cassants est très grande, il est important de noter qu’il est impossible d’avoir une règle absolue indiquant « aucune modification cassante » - il peut y avoir des problèmes qui ne peuvent être résolus que de manière saine d’esprit en ayant un changement cassant. En d’autres mots, la seule façon dont nous pourrions vraiment avoir « aucun changement cassant » est de n’avoir aucun changement du tout.

Notre politique permanente consiste à éviter d’apporter des modifications cassants si possible, et de ne le faire que si la modification a une valeur significative à long terme pour le client ou l’infrastructure.

Que faire pour les changements cassants

S’il est possible d’accomplir quelque chose sans changement cassant et sans compromettre la structure à long terme et la viabilité de la fonctionnalité, n’effectuez pas le changement cassant. S’il n’y a pas d’autre moyen, la politique actuelle consiste à évaluer chaque changement cassant individuel, afin de comprendre si l’avantage de la modification est supérieur au coût pour le consommateur d’absorber le changement. Le débat sur ce qui vaut la peine d’être fait et ce qui ne l’est pas aura généralement lieu sur la demande de tirage ou la discussion sur la question elle-même.

Ce qui peut se passer ici se divise en plusieurs compartiments :

Le changement cassant ajoute de la valeur, mais peut être écrit d’une manière qui n’est pas cassant

Par exemple, cette demande de tirage a ajouté une nouvelle fonctionnalité qui a été initialement écrite d’une manière qui était cassant - elle a modifié une interface existante - mais a ensuite été réécrite lorsque la fonctionnalité a été décomposée en tant que sa propre interface. Il s’agit généralement du meilleur résultat possible. N’essayez pas de forcer une modification dans une forme non cassant si cela compromettrait la viabilité à long terme ou la structure de la fonctionnalité.

Le changement cassant ajoute suffisamment de valeur au client qu’il vaut la peine de faire

Documentez les modifications cassantes et fournissez la meilleure atténuation possible (c’est-à-dire des étapes prescriptives sur la façon de migrer ou, mieux encore, des outils qui migreront automatiquement pour le client). Chaque version peut contenir une petite quantité de modifications qui sont cassants . Celles-ci doivent toujours être documentées dans la documentation, comme cela a été fait dans cette demande de tirage. S’il existe déjà un guide de migration 2.x.x→2.x+1.x+1, ajoutez des instructions ou des outils à ce document. S’il n’existe pas, créez-le.

Le changement cassant ajoute de la valeur, mais la douleur du client serait trop élevée

Tous les types de changements cassants ne sont pas égaux - certains sont beaucoup plus douloureux que d’autres, en fonction de notre expérience et de l’expérience client. Par exemple, les modifications apportées aux interfaces peuvent être douloureuses, mais si la modification cassante est une modification dans laquelle il est peu probable qu’un client ait étendu/implémenté dans le passé (le système de visualisation de diagnostic, par exemple), le coût réel est probablement faible à rien. Toutefois, si la modification est le type d’un champ sur un ScriptableObject (par exemple, sur l’un des profils principaux du MRTK), cela est susceptible de causer des douleurs considérables au client. Les clients ont déjà cloné le profil par défaut, la fusion/mise à jour des profils peut être extrêmement difficile à effectuer manuellement (c’est-à-dire via un éditeur de texte au moment de la fusion), et la recopiation du profil par défaut et la reconfiguration manuelle du profil sont très susceptibles d’entraîner des régressions difficiles à déboguer.

Ces modifications doivent être remises en rayon jusqu’à ce qu’il existe une branche qui permettra des modifications significativement cassants (avec une valeur significative qui donnera aux clients une raison de mettre à niveau). Une telle branche n’existe pas actuellement. Dans nos prochaines réunions de planification des itérations, nous examinerons l’ensemble des modifications/problèmes qui étaient « trop cassants » pour voir si nous avons atteint une masse critique pour rendre raisonnable la poursuite d’un ensemble de modifications en même temps. Notez qu’il est dangereux de créer une branche « tout est autorisé » sans qu’une diligence raisonnable soit effectuée en raison des ressources d’ingénierie limitées dont nous disposons et du fait que nous devrons fractionner les tests et la validation entre ces deux branches. Il doit y avoir un objectif clair et une date de début et de fin bien communiquées d’une telle branche lorsqu’elle existe.

Gestion à long terme des changements cassants

À long terme, nous devrions chercher à réduire l’étendue de ce qui est un changement cassant en augmentant l’ensemble des conditions de la liste B. À l’avenir, l’ensemble des éléments de la liste A sera toujours techniquement cassant pour l’ensemble de fichiers et de ressources que nous considérons comme étant dans la « surface d’API publique ». La façon dont nous pouvons obtenir un peu plus de liberté pour l’itération (par exemple, modifier les détails d’implémentation interne, permettre une refactorisation et un partage plus faciles du code entre plusieurs classes, etc.) consiste à être plus explicite sur les parties du code qui sont une surface officielle, plutôt que sur les détails de l’implémentation.

Une chose que nous avons déjà faite est d’introduire le concept d’une fonctionnalité « expérimentale » (elle appartient à l’espace de noms expérimental, elle n’a peut-être pas de tests/documentation, et est publiquement proclamée pour exister, mais peut être supprimée et mise à jour sans avertissement). Cela a permis d’ajouter de nouvelles fonctionnalités plus tôt pour obtenir des commentaires plus tôt, mais pas d’être immédiatement lié à sa surface d’API (car nous n’avons peut-être pas entièrement pensé à la surface de l’API).

Autres exemples de choses qui pourraient aider à l’avenir

  • Utilisation du mot clé interne. Cela nous permettrait d’avoir du code partagé au sein de nos propres assemblys (pour réduire la duplication de code) sans rendre les choses publiques aux consommateurs externes.
  • Création d’un espace de noms « interne » (par exemple, Microsoft.MixedReality.Toolkit.Internal.Utilities), où nous documentons publiquement que tout ce qui se trouve dans cet espace de noms interne peut être modifié à tout moment et peut être supprimé, etc. Cela est similaire à la façon dont les bibliothèques d’en-têtes C++ utilisent les espaces de noms ::internes pour masquer leurs détails d’implémentation.