Icke-bakåtkompatibla ändringar – MRTK2

Användare av MRTK är beroende av att de har en stabil API-yta för lansering till version, så att de kan ta uppdateringar till MRTK utan att ha stora icke-bakåtkompatibla ändringar varje gång.

Den här sidan beskriver vår nuvarande policy för icke-bakåtkompatibla ändringar i MRTK, tillsammans med några långsiktiga mål kring hur vi bättre kan hantera kompromissen mellan att hålla icke-bakåtkompatibla ändringar låga och att kunna göra rätt långsiktiga tekniska ändringar i koden.

Vad är en icke-bakåtkompatibel ändring?

En ändring är en icke-bakåtkompatibel ändring om den uppfyller något av villkoren i lista A OCH uppfyller alla villkor i lista B

Lista A

  • Tillägg, borttagning eller uppdatering av en medlem eller funktion i ett gränssnitt (eller borttagning/byt namn på hela gränssnittet).
  • Borttagning, uppdatering (ändring av typ/definition, privat eller internt) för alla skyddade eller offentliga medlemmar eller funktioner i klassen. (eller borttagning/byt namn på hela klassen).
  • Ändringen i händelseordningen som utlöses av en klass.
  • Byt namn på ett privat SerializedField (utan en motsvarande TidigareSerializedAs-tagg) eller offentlig egenskap på ett ScriptableObject (särskilt ändringar i profiler).
  • Ändra typen av fält i ett ScriptableObject (särskilt ändringar i profiler).
  • Uppdateringar till namnområdet eller asmdefs för en klass eller ett gränssnitt.
  • Borttagning av prefab eller borttagning av ett skript på objektet på den översta nivån i en prefab.

Lista B

  • Tillgången i fråga finns i grundpaketet (dvs. i någon av följande mappar):

    • MRTK/Core
    • MRTK/Providers/
    • MRTK/Services/
    • MRTK/SDK/
    • MRTK/tillägg
  • Tillgången i fråga tillhör inte det experimentella namnområdet.

Viktigt

Alla tillgångar som finns i exempelpaketet (dvs. en del av MRTK/Exempel/-mappen) kan ändras när som helst, eftersom tillgångar där är utformade för att kopieras och ses av konsumenter som "referensimplementeringar" men inte ingår i kärnuppsättningen med API:er och tillgångar. Tillgångar i det experimentella namnområdet (eller mer allmänt funktioner som är märkta som experimentella) är de som publiceras innan all due diligence har gjorts (dvs. tester, UX-iteration, dokumentation) och publiceras tidigt för att få feedback tidigare. Men eftersom de inte har tester och dokumentation, och eftersom vi förmodligen inte har spikat alla interaktioner och mönster, publicerar vi dem i ett tillstånd där allmänheten bör anta att de kan och kommer att ändras (d.v.s. ändras, tas bort helt och hållet).

Mer information finns i Experimentella funktioner .

Eftersom ytan för icke-bakåtkompatibla ändringar är mycket stor är det viktigt att notera att det skulle vara omöjligt att ha en absolut regel som säger "inga icke-bakåtkompatibla ändringar" – det kan finnas problem som bara kan åtgärdas på ett sunt sätt genom att ha en icke-bakåtkompatibel ändring. För att uttrycka ett annat sätt är det enda sättet att verkligen ha "inga icke-bakåtkompatibla ändringar" att inte ha några ändringar alls.

Vår stående policy är att undvika att göra icke-bakåtkompatibla ändringar om det är möjligt och endast göra det om ändringen skulle ackumulera betydande långsiktiga kund- eller ramverksvärden.

Vad du ska göra med icke-bakåtkompatibla ändringar

Om det är möjligt att åstadkomma något utan en icke-bakåtkompatibel ändring och utan att äventyra funktionens långsiktiga struktur och livskraft ska du inte göra den icke-bakåtkompatibla ändringen. Om det inte finns något annat sätt är den nuvarande policyn att utvärdera varje enskild icke-bakåtkompatibel förändring, för att förstå om fördelen med att ta förändringen uppväger kostnaden för konsumenten att absorbera förändringen. Debatt om vad som är värt att göra och vad som inte är det kommer i allmänhet att äga rum om pr eller själva frågan diskussion.

Vad som kan hända här hamnar i flera bucketar:

Den icke-bakåtkompatibla ändringen tillför värde men kan skrivas på ett sätt som inte går sönder

Den här pull-begäran lade till exempel till en ny funktion som ursprungligen skrevs på ett sätt som bröts – den ändrade ett befintligt gränssnitt – men skrevs sedan om där funktionen bröts ut som sitt eget gränssnitt. Detta är i allmänhet det bästa möjliga resultatet. Försök inte tvinga fram en ändring till ett icke-icke-icke-bakåtkompatibelt format om det skulle äventyra funktionens långsiktiga livskraft eller struktur.

Den icke-bakåtkompatibla ändringen ger kunden ett tillräckligt värde som det är värt att göra

Dokumentera vad de icke-bakåtkompatibla ändringarna är och ge bästa möjliga riskreducering (dvs. förebyggande steg för hur du migrerar, eller ännu bättre verktyg som automatiskt migreras för kunden). Varje version kan innehålla en liten mängd ändringar som inte fungerar – de bör alltid dokumenteras i dokument som i den här pull-begäran. Om det redan finns en migreringsguide för 2.x.x→2.x+1.x+1 lägger du till instruktioner eller verktyg i dokumentet. Om den inte finns skapar du den.

Den icke-bakåtkompatibla ändringen tillför värde men kundens smärta skulle vara för hög

Inte alla typer av icke-bakåtkompatibla ändringar skapas lika – vissa är betydligt mer smärtsamma än andra, baserat på vår erfarenhet och baserat på kundupplevelser. Ändringar i gränssnitt kan till exempel vara smärtsamma, men om den icke-bakåtkompatibla ändringen är en ändring där det är osannolikt att en kund har utökat/implementerat tidigare (till exempel diagnostikvisualiseringssystemet) är den faktiska kostnaden förmodligen låg till ingenting. Men om ändringen är typen av ett fält i ett ScriptableObject (till exempel på en av MRTK:s kärnprofiler) kommer detta sannolikt att orsaka massiv kundsmärta. Kunder har redan klonat standardprofilen, sammanslagning/uppdatering av profiler kan vara mycket svårt att göra manuellt (dvs. via en textredigerare under sammanslagningstiden), och att kopiera om standardprofilen och konfigurera om allt för hand är mycket sannolikt att leda till svåra att felsöka regressioner.

Dessa ändringar måste vi lägga tillbaka på hyllan tills det finns en gren som tillåter betydande icke-bakåtkompatibla ändringar (tillsammans med betydande värde som ger kunderna en anledning att uppgradera). En sådan gren finns för närvarande inte. I våra framtida iterationsplaneringsmöten kommer vi att granska den uppsättning ändringar/frågor som var "för icke-bakåtkompatibla" för att se om vi nådde en kritisk massa för att göra det rimligt att genomföra en uppsättning ändringar på en gång. Observera att det är farligt att starta en gren för "allt är tillåtet" utan att due diligence görs på grund av de begränsade tekniska resurser vi har, och det faktum att vi skulle behöva dela upp testning och validering mellan dessa två. Det måste finnas ett tydligt syfte och väl kommunicerat start- och slutdatum för en sådan gren när den finns.

Långsiktig hantering av icke-bakåtkompatibla ändringar

På lång sikt bör vi försöka minska omfattningen av vad som är en icke-bakåtkompatibel ändring genom att öka uppsättningen villkor i lista B. Framöver kommer uppsättningen saker i lista A alltid tekniskt sett att brytas för den uppsättning filer och tillgångar som vi anser vara i den "offentliga API-ytan". Det sätt som vi kan få lite mer frihet för iteration (dvs. att ändra den interna implementeringsinformationen, vilket möjliggör enklare refaktorisering och delning av kod mellan flera klasser osv.) är att vara mer explicit om vilka delar av koden som är officiella ytor, snarare än implementeringsdetaljer.

En sak som vi redan har gjort är att introducera begreppet "experimentell" funktion (den hör hemma i det experimentella namnområdet, den kanske inte har tester/dokumentation och är offentligt proklamerad att existera men kan tas bort och uppdateras utan varning). Detta har gett frihet att lägga till nya funktioner tidigare för att få tidigare feedback, men inte omedelbart bindas till dess API-yta (eftersom vi kanske inte helt har tänkt ut API-ytan).

Andra exempel på saker som kan vara till hjälp i framtiden

  • Användning av det interna nyckelordet. Detta skulle göra det möjligt för oss att ha delad kod i våra egna sammansättningar (för att minska koddupliceringen) utan att göra saker offentliga för externa konsumenter.
  • Skapa ett "internt" namnområde (dvs. Microsoft.MixedReality.Toolkit.Internal.Utilities), där vi offentligt dokumenterar att allt som finns i det interna namnområdet kan ändras när som helst och kan tas bort osv. Detta liknar hur C++-huvudbibliotek använder ::interna namnrymder för att dölja implementeringsinformationen.