破壊的変更 - MRTK2

MRTK のコンシューマーが、リリースのたびに大幅な破壊的変更なしに MRTK の更新プログラムを取得できるかどうかは、リリース間で安定した API サーフェスを維持できるかどうかにかかっています。

このページでは、MRTK の破壊的変更に関する現在のポリシーについて説明します。また、破壊的変更の回数を抑えることと、コードに対して長期的に適切な技術的変更を加えることができることの間のトレードオフをより適切に管理する方法に関連した長期的目標についても説明します。

破壊的変更とは

その変更がリスト A のいずれかの条件を満たし、かつリスト B のすべての条件を満たしている場合、それを破壊的変更と呼びます

リスト A

  • いずれかのインターフェイスのいずれかのメンバーまたは関数の追加、削除、または更新 (あるいはインターフェイス全体の削除や名前変更)。
  • クラスの保護されたメンバーまたはパブリック メンバーや関数の削除、更新 (型や定義の変更、プライベート化や内部化)。 (または、クラス全体の削除や名前変更)。
  • クラスによって開始されるイベントの順序の変更。
  • ScriptableObject 上のプライベート SerializedField (対応する FormerlySerializedAs タグなし) またはパブリック プロパティの名前変更 (特にプロファイルに対する変更)。
  • ScriptableObject 上のフィールドの型の変更 (特にプロファイルに対する変更)。
  • 任意のクラスまたはインターフェイスの名前空間または asmdef の更新。
  • 任意のプレハブの削除、またはプレハブの最上位オブジェクト上のスクリプトの削除。

リスト B

  • 対象のアセットが Foundation パッケージに含まれている (つまり、次のフォルダーのいずれかに含まれている)。

    • MRTK/Core
    • MRTK/Providers/
    • MRTK/Services/
    • MRTK/SDK/
    • MRTK/Extensions
  • 対象のアセットが、実験的な名前空間に属していない。

重要

サンプル パッケージ (つまり、MRTK/Examples/ フォルダーの一部) に含まれるアセットはすべて、"リファレンス実装" としてコンシューマーがコピーおよび表示することを目的に設計されていますが、そこにあるアセットは API とアセットのコア セットには含まれないため、いつでも変更される可能性があります。 実験的な名前空間内にあるアセット (より一般的には、Experimental とラベル付けされている機能) は、すべての適正評価 (つまり、テスト、UX イテレーション、文書化) が行われる前に公開されていたり、より早期にフィードバックを得るために早い段階で公開されていたりするアセットです。 ただし、テストとドキュメントが含まれていないため、また、すべての対話式操作と設計が確定しているとは限らないため、Microsoft ではこれらを、今後変更される可能性がある、あるいは実際に変更される (つまり、修正や完全な削除など) ものであると一般のユーザーが想定するべき状態で公開しています。

詳細については、「試験的な機能」を参照してください。

破壊的変更のサーフェス領域は非常に広いため、"破壊的変更がないこと" というような絶対的なルールを採用することは不可能です。破壊的変更を採用することによってのみ良識的な方法で修正できるような問題は常に存在するものです。 別の言い方をすれば、実際に "破壊的変更がない" ことを可能にするには、変更を一切行わないようにする以外にありません。

Microsoft の永続的なポリシーは、可能な限り破壊的変更の実施を避けつつ、その変更によって顧客またはフレームワークにとって長期的かつ大幅な価値が生じる場合にのみ変更を行うというものです。

破壊的変更への対応

破壊的変更を行わなくても、機能の長期的な構造と実行可能性を損なうことなく一定の機能を実現できる場合は、破壊的変更を行わないでください。 他に方法がない場合は、現在のポリシーでは、個々の破壊的変更を評価し、変更を行うことから得られる利点が、変更を受け入れるコンシューマーのコストを上回るかどうかを把握します。 実施する価値があることとないことに関する検討は、通常、PR つまり問題についての議論自体において行います。

その際に起こる可能性があることは、いくつかに分類できます。

破壊的変更によって価値が付加されるが、破壊的でない方法でも記述できる

たとえば、この PR によって、当初、破壊的変更を伴うやり方で記述された新しい機能が追加されました (これにより、既存のインターフェイスが変更されました) が、後でその機能が書き直されて独自のインターフェイスとして切り離されました。 これは一般に、考え得る最良の結果です。 変更によって機能の長期的な実行可能性や構造が損なわれる場合は、そのような変更を非破壊的な形式に強制しようとしないでください。

破壊的変更により、それに見合うだけの十分な価値が顧客にもたらされる

破壊的変更の内容を文書化し、考え得る最適な軽減策 (つまり、移行方法に関する規範的手順や、可能であれば顧客に代わって自動的に移行を実行するツール) を提供します。 各リリースには、ごくわずかながら破壊的変更が含まれている可能性があります。この PR で行われているように、これらの変更は必ず文書化する必要があります。 2.x.x から 2.x+1.x+1 への移行ガイドが既にある場合は、そのドキュメントに手順またはツールを追加します。存在しない場合は作成します。

破壊的変更によって価値は高まるが、顧客が直面する問題が大きすぎる

すべての種類の破壊的変更が等しく作成されるわけではありません。Microsoft の経験や顧客の経験に基づけば、著しく大きな問題を引き起こす破壊的変更もあります。 たとえば、インターフェイスの変更には手間がかかるものですが、顧客が過去に拡張や実装を行っている可能性が低いインターフェイス (診断視覚化システムなど) への破壊的変更の場合、おそらく実際のコストはあまりかからないか、まったくかからないでしょう。 ところが、変更が ScriptableObject のフィールド (たとえば、MRTK のコア プロファイルの 1 つ) の型である場合、これは顧客に大きな問題を引き起こす可能性があります。 顧客は既に既定のプロファイルを複製しているため、プロファイルのマージや更新を手動で (つまり、マージの際にテキスト エディターを使用して) 行うことが非常に困難になる可能性があります。また、既定のプロファイルを再コピーし、すべてを手動で再構成する場合は、不具合のデバッグが困難になる可能性が非常に高くなります。

(顧客にアップグレードの根拠を与える重要な価値と共に) 破壊的変更を可能にするブランチが存在するようになるまで、これらの変更は棚上げにする必要があります。 現在、このようなブランチが存在しない場合、 今後のイテレーション計画会議では、一連の "破壊的すぎる" 変更や問題について再検討し、一連の変更を一度に追求するのが妥当な臨海状態に達しているかどうかを確認します。 エンジニアリング リソースが限られており、また、テストと検証の 2 つに分割する必要があるため、適正評価を行うことなく "全権が委任された" ブランチを立ち上げることは危険です。 そのようなブランチが存在する場合は、明確な目的を定め、開始日と終了日について十分に意思疎通を図る必要があります。

破壊的変更の長期的な管理

長期的には、リスト B の条件セットを増やすことによって、破壊的変更の範囲を狭めることを目指す必要があります。将来的には、リスト A の項目セットは、"パブリック API サーフェス" に存在すると思われる一連のファイルおよびアセットについては常に技術的に破壊的なものとなります。イテレーションの自由度を少し上げること (内部実装の詳細を変更する、複数のクラス間でコードのリファクタリングや共有を容易に行えるようにするなど) ができるようにするには、実装の詳細ではなく、コードのどの部分が公式的なサーフェスであるかをより明確にする必要があります。

既に実施されていることの 1 つとして、"実験的な" 機能 (実験的な名前空間に属し、テストおよびドキュメントがない場合があり、その存在が公に宣言されてはいるが、警告なしに削除および更新される可能性がある) という概念の導入があります。 これにより迅速に新機能を追加する自由度が高まるため、それだけ早くフィードバックを得ることができますが、API サーフェスにすぐに関連付けることはしません (API サーフェス全体について十分に考え抜いたというわけではないため)。

将来役立つ可能性のあるその他の例

  • 内部キーワードの使用。 これにより、外部のコンシューマーに公開することなく、独自のアセンブリ内で共有コードを作成できます。その結果、コードの重複が減ります。
  • "内部" 名前空間 (つまり、Microsoft.MixedReality.Toolkit.Internal.Utilities) の作成。この内部名前空間に含まれるものは、いつでも変更されたり、削除されたりする可能性が高いことを公に文書化しています。これは、C++ ヘッダー ライブラリで ::internal 名前空間を使用して実装の詳細を非表示にする方法と似ています。