


Desenvolver um framework que oferece uma única API em várias linguagens de programação
Para criar um framework que gera pacotes para várias linguagens a partir de um só código. Define regras para o protocolo JSON entre processos, os tipos exportáveis, a geração de código e os testes que conferem se todas as linguagens se comportam igual.
Para usar estas regras de IA
## Manter o mesmo comportamento e a compatibilidade retroativa em todas as linguagens
Um framework entre linguagens deve priorizar um comportamento que permaneça compreensível e equivalente em todas as linguagens compatíveis, e não todos os recursos idiomáticos disponíveis na linguagem de implementação.
### Invariantes fundamentais
- Preserve a compatibilidade retroativa do formato do assembly, do protocolo de runtime e das APIs públicas.
- Exija comportamento observável equivalente em todas as linguagens-alvo compatíveis.
- Gere código que siga as convenções normais de nomenclatura, empacotamento e tipos de cada linguagem-alvo.
- Aceite apenas recursos da linguagem de origem que possam ser representados fielmente em todos os alvos.
- Prefira correção e interoperabilidade a atalhos de desempenho específicos de um alvo.
### Limites dos pacotes
- Separe o núcleo do protocolo, os runtimes hospedeiros, as definições de esquema, os geradores de código, as ferramentas de reflexão, as verificações de compatibilidade e os fixtures de teste.
- Mantenha os utilitários de build e os relatórios de conformidade fora dos pacotes de runtime.
- Trate cada runtime de linguagem e cada integração opcional como um pacote com responsabilidade independente.
- Coloque os fixtures compartilhados onde todos os testes de compiladores, geradores e runtimes possam consumir os mesmos casos de comportamento.
- Mantenha componentes que ficam em repositórios separados por trás de dependências explícitas e versionadas.
## Executar o código em um processo núcleo que conversa por JSON com cada linguagem
Use um modelo de runtime com dois lados quando as linguagens hospedeiras não puderem executar diretamente a linguagem de implementação. O processo hospedeiro se comunica com um processo de núcleo por mensagens JSON na entrada e na saída padrão, enquanto toda execução na linguagem de implementação permanece no núcleo.
### Limite entre processos
- Dê a cada solicitação um discriminador explícito de operação.
- Cubra carregamento de assemblies, ciclo de vida de objetos, chamadas de métodos, acesso a propriedades, trabalho assíncrono, estatísticas e callbacks com operações distintas do protocolo.
- Use um processo intermediário adicional somente quando um runtime hospedeiro não puder preservar os descritores de arquivo exigidos pelo processo principal.
- Mantenha a saída de diagnóstico no fluxo de erro para que ela não corrompa o fluxo do protocolo.
### Sequência de callback
Quando o código da linguagem de implementação alcançar um método ou uma propriedade sobrescrita pela linguagem hospedeira:
1. O núcleo envia uma solicitação de callback em vez da resposta original.
2. O hospedeiro executa a sobrescrita e devolve uma mensagem de conclusão com o resultado ou a falha.
3. O núcleo retoma a operação original e devolve sua resposta efetiva.
### Evolução do protocolo
- Trate os nomes das solicitações, o significado dos campos, a ordem das respostas e a sequência dos callbacks como contratos de compatibilidade.
- Atualize todos os runtimes hospedeiros em conjunto quando o protocolo mudar.
- Adicione testes de compatibilidade antes de alterar o formato de uma mensagem ou uma regra de ciclo de vida.
## Decidir se os valores passam entre linguagens por cópia ou por referência
Defina uma representação de transmissão para cada categoria de valor portável e torne explícita a semântica de propriedade. Um valor transferido por cópia não deve ser confundido com um objeto que continua sob propriedade do núcleo.
### Representação de transmissão
- Envie booleanos, números e strings por valor, usando suas representações em JSON.
- Envie listas e mapas por valor, com um envelope explícito e etiquetado quando for necessário distingui-los de outros objetos.
- Codifique enumerações com um identificador de membro totalmente qualificado.
- Codifique datas com uma representação de timestamp etiquetada e inequívoca.
- Codifique estruturas somente de dados com o tipo totalmente qualificado e os dados dos campos.
- Codifique instâncias de classe por referência, com um identificador de objeto estável.
### Regras de fronteira
- Preserve strings UTF-8, o comportamento de ponto flutuante, a nulabilidade e a semântica de cópia de coleções entre as linguagens.
- Mantenha estáveis as chaves etiquetadas da representação de transmissão depois que consumidores passarem a depender delas.
- Libere explicitamente as referências a objetos quando o protocolo for responsável pelo gerenciamento remoto do ciclo de vida.
- Teste os percursos de ida e volta por todos os runtimes hospedeiros, não apenas pelo lado da linguagem de implementação.
## Exportar só tipos que todas as linguagens conseguem representar
Aplique restrições de portabilidade às declarações exportadas pelo ponto de entrada principal do módulo, permitindo que o código interno de implementação use recursos mais amplos da linguagem.
### Superfície da API portável
- Ofereça suporte a tipos primitivos, listas, mapas, enumerações, datas, classes, interfaces, estruturas de dados, promessas e valores opacos somente quando todos os alvos tiverem uma representação sólida.
- Diferencie interfaces comportamentais de estruturas somente de dados por uma regra explícita de nomenclatura ou esquema.
- Exija que estruturas somente de dados contenham propriedades somente leitura e nenhum método.
- Permita promessas apenas em posições que todos os alvos possam expor de maneira consistente, como tipos de retorno de métodos.
- Trate valores opacos como implicitamente anuláveis quando os alvos com tipagem estática não puderem recuperar um tipo mais preciso.
- Passe classes por referência e estruturas semelhantes a valores por cópia.
### Restrições
- Rejeite construções exportadas sem suporte, como tuplas, tipos não habitados, inteiros de precisão arbitrária, símbolos e sobrecargas de métodos.
- Exija que métodos sobrescritos mantenham assinaturas exatamente compatíveis; não dependa de retornos covariantes nem de parâmetros contravariantes.
- Evite uniões quando elas se reduzirem a um objeto sem tipagem nos alvos com tipagem estática.
- Não permita que interfaces comportamentais e estruturas somente de dados herdem umas das outras.
## Gerar o pacote de cada linguagem a partir do mesmo assembly compilado
Gere cada alvo a partir do mesmo assembly compilado, para que os pacotes de destino compartilhem uma única fonte de verdade da API.
### Pipeline de geração
1. Leia o assembly do pacote compilado na linguagem de implementação.
2. Gere código-fonte idiomático para a linguagem-alvo selecionada.
3. Compile o código-fonte gerado com a cadeia de ferramentas dessa linguagem.
4. Empacote o resultado com o gerenciador de pacotes padrão do ecossistema de destino.
### Projeto dos alvos
- Dê a cada linguagem-alvo seu próprio adaptador, construído sobre uma abstração compartilhada de travessia.
- Use um alvo maduro como referência ao adicionar um novo alvo, mas preserve os padrões idiomáticos da nova linguagem.
- Inclua o artefato original da implementação somente quando a arquitetura do runtime exigir.
- Inclua os metadados do assembly necessários para ferramentas posteriores de reflexão ou geração.
- Compartilhe entre os alvos os utilitários de formatação, indentação, criação de arquivos e blocos de código.
- Atualize os snapshots do código gerado sempre que um alvo mudar.
## Compilar e testar no monorepo com as versões fixas das ferramentas
Execute instalação, builds, testes, linting, atualizações de snapshots e geração de relatórios de conformidade por meio da cadeia de ferramentas fixada no workspace do repositório.
### Ambiente e escopo
- Comece na raiz do repositório para o bootstrap completo e a validação de todo o repositório.
- Execute scripts limitados a um pacote a partir do diretório desse pacote durante iterações focadas.
- Respeite as versões declaradas do gerenciador de pacotes e das ferramentas de orquestração.
- Limite a concorrência do build quando as dependências entre pacotes tornarem insegura a execução paralela.
### Fluxo de alteração
- Identifique o pacote responsável pela alteração antes de editar ou executar comandos.
- Primeiro, faça o build e teste o pacote em foco; depois, execute uma validação mais ampla dos contratos compartilhados.
- Edite o código-fonte, e não os bundles gerados, e refaça o build pelo pipeline designado.
- Mantenha locais as substituições temporárias de runtime e as configurações do depurador.
- Atualize snapshots somente depois de confirmar que a alteração gerada é intencional.
## Testar com snapshots e um conjunto comum de testes que todas as linguagens devem passar
Combine testes rápidos no nível do pacote com verificações de regressão da saída gerada e uma suíte de conformidade entre linguagens.
### Camadas de validação
- Use testes unitários para a lógica dos pacotes e para casos extremos do protocolo.
- Use snapshots para detectar alterações não intencionais no código-fonte gerado.
- Use fixtures compartilhados que exercitem classes, interfaces, estruturas de dados, enumerações, coleções, chamadas assíncronas e sobrescritas.
- Use testes de conformidade para verificar comportamento equivalente em cada binding de linguagem.
### Fixtures e relatórios
- Compile os fixtures compartilhados com o mesmo compilador usado por consumidores reais.
- Atualize todos os snapshots afetados de geradores, reflexão e runtime quando os fixtures mudarem.
- Defina cada caso de conformidade uma única vez e exija que cada binding de linguagem o implemente ou o classifique explicitamente.
- Gere novamente e faça commit do relatório de conformidade sempre que os casos ou resultados mudarem.
- Faça a validação falhar quando um binding adicionar um caso ausente da definição da suíte ou quando o relatório estiver desatualizado.
## Lançar alterações com snapshots e relatórios em dia, sem force-push
Trate o código gerado, os formatos de protocolo, as bibliotecas de runtime e os relatórios como superfícies de lançamento coordenadas.
### Restrições inegociáveis
- Não enfraqueça as restrições de tipos exportados sem validação em todos os alvos.
- Não altere operações do protocolo nem formatos de serialização etiquetados sem atualizações coordenadas dos runtimes hospedeiros.
- Não altere os padrões de geração sem atualizar e revisar os snapshots.
- Não presuma segurança entre threads quando as bibliotecas de runtime forem intencionalmente de thread única.
- Não introduza dependências circulares entre pacotes quando o grafo de build rejeitar ciclos.
- Não modifique diretamente a saída empacotada.
### Pull request e transferência
- Use o formato de título, as convenções de escopo e a confirmação de licença de contribuição exigidos pelo repositório.
- Inclua todos os snapshots gerados e relatórios de conformidade exigidos pela alteração no código-fonte.
- Mantenha fora da alteração arquivos gerados não relacionados, alterações de lockfile e configurações locais.
- Se o branch remoto tiver avançado, execute fetch e rebase ou merge, resolva os conflitos, valide e faça push normalmente; nunca faça push forçado.
- Registre a sincronização posterior de repositórios ou documentação no checklist de lançamento, em vez de depender da memória.
