Fundamentalne zmiany

Użytkownicy mrTK są zależni od stabilnej powierzchni interfejsu API od wydania do wydania, dzięki czemu mogą oni wprowadzać aktualizacje do tegotku bez konieczności za każdym razem wprowadzać istotne zmiany.

Na tej stronie opisano nasze bieżące zasady dotyczące przełomowych zmian w pniu MRTK oraz pewne długoterminowe cele dotyczące tego, jak można lepiej zarządzać kompromisem między utrzymywaniem niskich zmian przerywających a możliwością wprowadzić odpowiednie długoterminowe zmiany techniczne w kodzie.

Co to jest przełomowa zmiana?

Zmiana jest zmianą przerywaną, jeśli spełnia dowolny z warunków z listy A i spełnia wszystkie warunki na liście B

Lista A

  • Dodanie, usunięcie lub aktualizacja dowolnego członka lub funkcji dowolnego interfejsu (albo usunięcie/zmiana nazwy całego interfejsu).
  • Usuwanie, aktualizowanie (zmiana typu/definicji, tworzenie prywatnych lub wewnętrznych) dowolnego chronionego lub publicznego członka lub funkcji klasy. (lub usunięcie/zmiana nazwy całej klasy).
  • Zmiana kolejności zdarzeń wyzjęnych przez klasę.
  • Zmiana nazwy dowolnego prywatnego SerializedField (bez odpowiadającego mu tagu FormerlySerializedAs) lub właściwości publicznej w obiekcie ScriptableObject (szczególnie zmiany w profilach).
  • Zmiana typu pola w scriptableObject (zwłaszcza zmiany w profilach).
  • Aktualizacje przestrzeni nazw lub definicji asmdefs dowolnej klasy lub interfejsu.
  • Usunięcie wszelkich prefabrykacji lub usunięcie skryptu na obiekcie najwyższego poziomu prefabrykatu.

Lista B

  • Zasób, o który chodzi, znajduje się w pakiecie foundation (tj. znajduje się w jednym z następujących folderów):

    • MRTK/Core
    • MRTK/Providers/
    • MRTK/Services/
    • ZESTAW MRTK/SDK/
    • MRTK/rozszerzenia
  • Zasób, którego dotyczy ten zasób, nie należy do eksperymentalnej przestrzeni nazw.

Ważne

Każdy zasób, który znajduje się w pakiecie przykładów (tj. części zestawu danych MRTK/przykładów/folderu), może ulec zmianie w dowolnym momencie, ponieważ zasoby, które tam znajdują się, mają być kopiowane i przeglądane przez użytkowników jako "implementacje referencyjne", ale nie są częścią podstawowego zestawu interfejsów API i zasobów. Zasoby w eksperymentalnej przestrzeni nazw (lub, ogólnie rzecz biorąc, funkcje oznaczone jako eksperymentalne) to te, które są publikowane przed całą należytą starannością (tj. testami, iteracją środowiska użytkownika, dokumentacją) i są publikowane wcześniej, aby wcześniej uzyskać opinie. Jednak ponieważ nie mają testów i dokumentacji, a ponieważ prawdopodobnie nie usunęliśmy wszystkich interakcji i projektów, publikujemy je w stanie, w którym publiczna powinna założyć, że mogą i zmienią się (tj. zostaną zmodyfikowane, całkowicie usunięte itp.).

Aby uzyskać więcej informacji, zobacz Funkcje eksperymentalne.

Ponieważ obszar powierzchni dla zmian przerywania jest bardzo duży, należy pamiętać, że posiadanie bezwzględnej reguły "brak zmian istotnych" byłoby niemożliwe — mogą wystąpić problemy, które można naprawić tylko w sposób sane, przez zmianę przerywaną. Inaczej mówiąc, jedynym sposobem, w jaki naprawdę można mieć "brak zmian przełomowych", jest brak zmian.

Nasze stałe zasady mają na celu uniknięcie w miarę możliwości dokonywania istotnych zmian i robisz to tylko wtedy, gdy zmiana narosłaby znaczącą długoterminową wartość klienta lub struktury.

Co należy zrobić w przypadku zmian przełomowych

Jeśli jest możliwe osiągnięcie czegoś bez istotnej zmiany i bez naruszania długoterminowej struktury i możliwości działania funkcji, nie należy jej zmieniać. Jeśli nie ma innego sposobu, bieżące zasady oceniają każdą zmianę, aby zrozumieć, czy korzyść z jej uwzględnienia przeważa nad kosztem przechwytywanie zmiany przez konsumenta. Dyskusje na temat tego, co warto zrobić, a co nie, zwykle będą odbywać się w dyskusji dotyczącej samego tematu lub problemu.

To, co może się tutaj zdarzyć, mieści się w kilku zasobnikach:

Zmiana przerywania zwiększa wartość, ale może być zapisywana w sposób, który nie jest przerywany

Na przykład to ściągnięcie ściągnięcie dodało nową funkcję, która początkowo została napisana w sposób, który przerywał — zmodyfikował istniejący interfejs , ale została ponownie napisana, gdzie funkcja została uszkodzona jako własny interfejs. Jest to zazwyczaj najlepszy możliwy wynik. Nie należy próbować wymusić zmiany w formie niepodzielności, jeśli takie rozwiązanie naruszyć długoterminową rentowność lub strukturę funkcji.

Zmiana przełomowa dodaje klientowi wystarczającą wartość, że warto to zrobić

Udokumentuj, czym są istotne zmiany, i zapewnij najlepsze możliwe środki zaradcze (tj. nakazowe kroki migracji lub lepsze narzędzia, które będą automatycznie migrowane dla klienta). Każde wydanie może zawierać niewielką liczbę zmian, które przerywają — powinny one być zawsze udokumentowane w dokumentach, jak to zrobiono w tym pniu ściągnięcie. Jeśli istnieje już przewodnik migracji do wersji 2.x.x→2.x+1.x+1, dodaj instrukcje lub narzędzia do tego doc. Jeśli nie istnieje, utwórz go.

Zmiana przełomowa zwiększa wartość, ale ból klienta byłby zbyt duży

Nie wszystkie rodzaje przełomowych zmian są tworzone tak samo — niektóre są znacznie bardziej przykrewne, że inne, w oparciu o nasze doświadczenia i doświadczenia klientów. Na przykład zmiany w interfejsach mogą być kosztowne, ale jeśli przełomowa zmiana to ta, w której klient najprawdopodobniej nie zostałby rozszerzony/zaimplementowany w przeszłości (na przykład system wizualizacji diagnostycznych), rzeczywisty koszt jest prawdopodobnie niski lub nie ma żadnego. Jeśli jednak zmiana dotyczy typu pola w scriptableObject (na przykład w jednym z podstawowych profilów mrTK), może to spowodować ogromny ból klienta. Klienci sklonowali już profil domyślny, scalanie/aktualizowanie profilów może być niezwykle trudne ręcznie (tj. za pośrednictwem edytora tekstu w czasie scalania), a ponowne skopiowanie domyślnego profilu i ręczne ponowne skonfigurowanie wszystkiego może bardzo prowadzić do regresji debugowania.

Te zmiany należy umieścić z powrotem na półce aż do momentu, gdy będzie istniała gałąź, która umożliwi istotne zmiany istotne (wraz ze znaczącą wartością, która da klientom powód do uaktualnienia). Taka gałąź obecnie nie istnieje. W naszych przyszłych spotkaniach dotyczących planowania iteracji przejmiemy zestaw zmian/problemów, które były "zbyt przerywane", aby sprawdzić, czy osiągnęliśmy masę krytyczną, aby uzasadnione było kontynuowanie całego zestawu zmian jednocześnie. Należy pamiętać, że niebezpieczne jest rozgałęzienie gałęzi "wszystko jest dozwolone" bez konieczności starannego zachowania staranności ze względu na ograniczone zasoby inżynieryjne, które mamy, oraz fakt, że konieczne jest podzielenie testowania i walidacji między te dwie części. Musi być jasne przeznaczenie oraz dobrze przekazana data rozpoczęcia i zakończenia takiej gałęzi, gdy taka istnieje.

Długoterminowe zarządzanie zmianami przełomowymi

W dłuższej perspektywie powinniśmy dążyć do ograniczenia zakresu zmian, które są istotne, zwiększając zestaw warunków na liście B. W przyszłości zestaw elementów na liście A będzie zawsze stanowił przełom dla zestawu plików i zasobów, które uważamy za "publiczne interfejsy API". Sposobem na to, aby uzyskać nieco większą swobodę iteracji (tj. zmiana szczegółów implementacji wewnętrznej, umożliwienie łatwiejszej refaktoryzacji i udostępniania kodu między wieloma klasami itp.), jest bardziej jawne, niż w przypadku szczegółów implementacji.

Jedną z rzeczy, które już wprowadziliśmy, jest wprowadzenie koncepcji funkcji "eksperymentalnej" (należy ona do eksperymentalnej przestrzeni nazw, może nie mieć testów/dokumentacji i jest publicznie zasłaniana jako istnieje, ale może zostać usunięta i zaktualizowana bez ostrzeżenia). Dzięki temu można wcześniej dodawać nowe funkcje, aby uzyskać wcześniejsze opinie, ale nie można ich natychmiast powiązać z powierzchnią interfejsu API (ponieważ być może nie w pełni przemyślaliśmy powierzchni interfejsu API).

Inne przykłady rzeczy, które mogą pomóc w przyszłości

  • Użycie wewnętrznego słowa kluczowego. Pozwoliłoby to na współużytkowanie kodu w naszych własnych zestawach (w celu ograniczenia duplikowania kodu) bez publicznemu udostępnianiu go użytkownikom zewnętrznym.
  • Tworzenie "wewnętrznej" przestrzeni nazw (tj. Microsoft.MixedReality.Toolkit.Internal.Utilities), w której publicznie dokumentuje się, że wszystkie elementy zawarte w tej wewnętrznej przestrzeni nazw mogą ulec zmianie w dowolnym momencie i mogą zostać usunięte itp. Jest to podobne do sposobu, w jaki biblioteki nagłówków języka C++ będą używać przestrzeni nazw ::internal do ukrywania szczegółów implementacji.