Alterações interruptivas — MRTK2

Os consumidores do MRTK dependem de ter uma superfície estável de API de versão para lançamento, para que possam fazer atualizações no MRTK sem ter grandes alterações interruptivas a cada vez.

Esta página descreve nossa política atual sobre alterações interruptivas no MRTK, juntamente com algumas metas de longo prazo sobre como podemos gerenciar melhor a compensação entre manter as alterações interruptivas baixas e ser capaz de fazer as alterações técnicas de longo prazo certas no código.

O que é uma alteração interruptiva?

Uma alteração será uma alteração interruptiva se atender a qualquer uma das condições na Lista A E atender a todas as condições na lista B

ListaR A

  • A adição, remoção ou atualização de qualquer membro ou função de qualquer interface (ou remoção/renomeação de toda a interface).
  • A remoção, a atualização (alterando o tipo/definição, tornando-se privada ou interna) de qualquer membro ou função de classe protegida ou pública. (ou remoção/renomeação de toda a classe).
  • A alteração na ordem dos eventos disparados por uma classe.
  • A renomeação de qualquer SerializedField privado (sem uma marca FormerlySerializedAs correspondente) ou propriedade pública em um ScriptableObject (especialmente alterações nos perfis).
  • Alterando o tipo de um campo em um ScriptableObject (especialmente alterações em perfis).
  • Atualizações para o namespace ou asmdefs de qualquer classe ou interface.
  • Remoção de qualquer pré-fabricado ou remoção de um script no objeto de nível superior de um pré-fabricado.

Lista B

  • O ativo em questão está no pacote de base (ou seja, ele está em uma das seguintes pastas):

    • MRTK/Core
    • MRTK/Provedores/
    • MRTK/Services/
    • MRTK/SDK/
    • MRTK/Extensões
  • O ativo em questão não pertence ao namespace experimental.

Importante

Qualquer ativo que esteja no pacote de exemplos (ou seja, parte da pasta MRTK/Examples/ ) está sujeito a alterações a qualquer momento, pois os ativos lá foram projetados para serem copiados e exibidos pelos consumidores como "implementações de referência", mas não fazem parte do conjunto principal de APIs e ativos. Os ativos no namespace experimental (ou, em geral, recursos rotulados como experimentais) são os que são publicados antes que toda a diligência prévia tenha sido feita (ou seja, testes, iteração de UX, documentação) e são publicados mais cedo para receber comentários mais cedo. No entanto, como eles não têm testes e documentação e, como provavelmente não corrigimos todas as interações e designs, os publicamos em um estado em que o público deve assumir que eles podem e mudarão (ou seja, serão modificados, completamente removidos etc.).

Confira Recursos experimentais para obter mais informações.

Como a área de superfície para alterações interruptivas é muito grande, é importante observar que ter uma regra absoluta que diz "nenhuma alteração interruptiva" seria impossível - pode haver problemas que só podem ser corrigidos de forma sã por ter uma alteração interruptiva. Para colocar de outra forma, a única maneira de realmente ter "nenhuma alteração interruptiva" é não ter nenhuma alteração.

Nossa política permanente é evitar fazer alterações interruptivas, se possível, e só fazê-lo se a alteração acumular um valor significativo de longo prazo do cliente ou da estrutura.

O que fazer sobre alterações interruptivas

Se for possível realizar algo sem uma alteração interruptiva e sem comprometer a estrutura de longo prazo e a viabilidade do recurso, não faça a alteração interruptiva. Se não houver outra maneira, a política atual será avaliar cada alteração interruptiva individual, para entender se o benefício de fazer a alteração supera o custo para o consumidor de absorver a alteração. Debate sobre o que vale a pena fazer e o que não é geralmente ocorrerá sobre a PR ou a discussão em si.

O que pode acontecer aqui cai em vários buckets:

A alteração interruptiva agrega valor, mas pode ser escrita de uma forma que não esteja falhando

Por exemplo, essa PR adicionou um novo recurso que foi inicialmente escrito de uma maneira que estava quebrando - ele modificou uma interface existente - mas depois foi reescrito onde o recurso foi dividido como sua própria interface. Geralmente, esse é o melhor resultado possível. Não tente forçar uma alteração em uma forma não interruptiva se isso comprometer a viabilidade ou a estrutura de longo prazo do recurso.

A alteração interruptiva adiciona valor suficiente ao cliente que vale a pena fazer

Documente quais são as alterações interruptivas e forneça a melhor mitigação possível (ou seja, etapas prescritivas sobre como migrar ou ferramentas melhores ainda que migrarão automaticamente para o cliente). Cada versão pode conter uma pequena quantidade de alterações que estão falhando – elas sempre devem ser documentadas em documentos, como foi feito nesta PR. Se já houver um guia de migração 2.x.x→2.x+1.x+1, adicione instruções ou ferramentas a esse documento. Se ele não existir, crie-o.

A alteração interruptiva agrega valor, mas a dor do cliente seria muito alta

Nem todos os tipos de alterações interruptivas são criados iguais - alguns são significativamente mais dolorosos que outros, com base em nossa experiência e com base nas experiências do cliente. Por exemplo, as alterações nas interfaces podem ser dolorosas, mas se a alteração interruptiva for aquela em que é improvável que um cliente tenha sido estendido/implementado no passado (o sistema de visualização de diagnóstico, por exemplo), o custo real provavelmente será baixo para nada. No entanto, se a alteração for o tipo de um campo em um ScriptableObject (por exemplo, em um dos perfis principais do MRTK), isso provavelmente causará uma enorme dor do cliente. Os clientes já clonaram o perfil padrão, mesclar/atualizar perfis pode ser extremamente difícil de fazer manualmente (ou seja, por meio de um editor de texto durante o tempo de mesclagem) e copiar novamente o perfil padrão e reconfigurar tudo manualmente é extremamente provável que leve a regressões difíceis de depurar.

Essas alterações temos que colocar de volta na prateleira até que exista uma ramificação que permita alterações significativamente interruptivas (juntamente com um valor significativo que dará aos clientes um motivo para atualizar). Esse branch não existe no momento. Em nossas futuras reuniões de planejamento de iteração, revisaremos o conjunto de alterações/problemas que foram "muito recentes" para ver se alcançamos uma massa crítica para tornar razoável buscar um conjunto de alterações de uma só vez. Observe que é perigoso criar um branch "tudo é permitido" sem que a diligência seja feita devido aos recursos de engenharia limitados que temos e ao fato de que teríamos que dividir testes e validação entre esses dois. É necessário que haja uma finalidade clara e uma data de início e término bem comunicadas desse branch quando ele existir.

Gerenciamento de longo prazo de alterações interruptivas

A longo prazo, devemos buscar reduzir o escopo do que é uma alteração interruptiva aumentando o conjunto de condições na Lista B. Daqui para frente, o conjunto de coisas na Lista A sempre será tecnicamente quebrado para o conjunto de arquivos e ativos que consideramos estar na "superfície da API pública". A maneira como podemos obter um pouco mais de liberdade para iteração (ou seja, alterar os detalhes da implementação interna, permitindo uma refatoração e compartilhamento mais fácil de código entre várias classes, etc. ) é ser mais explícito sobre quais partes do código são superfície oficial, em vez de detalhes de implementação.

Uma coisa que já fizemos é introduzir o conceito de um recurso "experimental" (ele pertence ao namespace experimental, pode não ter testes/documentação e é proclamado publicamente para existir, mas pode ser removido e atualizado sem aviso). Isso deu liberdade para adicionar novos recursos mais cedo para obter comentários anteriores, mas não ser imediatamente vinculado à sua superfície de API (porque talvez não tenhamos pensado totalmente na superfície da API).

Outros exemplos de coisas que podem ajudar no futuro

  • Uso da palavra-chave interna. Isso nos permitiria ter código compartilhado em nossos próprios assemblies (para reduzir a duplicação de código) sem tornar as coisas públicas para consumidores externos.
  • Criação de um namespace "interno" (ou seja, Microsoft.MixedReality.Toolkit.Internal.Utilities), em que documentamos publicamente que qualquer coisa contida nesse namespace interno está sujeita a alterações a qualquer momento e pode ser removida etc. Isso é semelhante a como as bibliotecas de cabeçalho do C++ usarão ::namespaces internos para ocultar os detalhes da implementação.