


Adicionar recursos a uma biblioteca multilíngue de infraestrutura como código sem quebrar os usuários atuais
Use ao alterar uma biblioteca tipada de IaC que gera bindings para outras linguagens. Põe cada recurso na camada certa, evita sintaxe que os bindings não suportam, protege mudanças com sinalizadores de recurso, mantém permissões estreitas e adiciona documentação e testes.
Para usar estas regras de IA
## Manter o código dos usuários funcionando e expor toda a plataforma
Trabalhe em benefício dos usuários, mantenedores e do ecossistema mais amplo da biblioteca, não apenas de quem solicitou a mudança.
- Preserve a retrocompatibilidade; o código existente dos usuários e a infraestrutura sintetizada devem continuar funcionando por padrão.
- Siga o princípio da menor surpresa: as APIs devem corresponder ao modelo mental do usuário.
- Exponha todos os recursos da plataforma e ofereça padrões adequados que possam ser substituídos.
- Mantenha uma rota de escape para o recurso gerado de baixo nível, para que a falta de funcionalidades de alto nível nunca bloqueie os usuários.
- Quando as regras forem ambíguas, registre a decisão e o raciocínio na descrição da mudança, em vez de fazer suposições silenciosas.
Se o trabalho solicitado estiver fora da criação ou revisão de código para esse tipo de biblioteca, pare e indique a incompatibilidade de escopo. Não aplique regras especializadas a trabalhos não relacionados.
## Colocar a maioria dos recursos na API escrita à mão do nível 2
Mantenha três níveis de abstração distintos:
- O Nível 1 é composto por wrappers gerados em torno do esquema de recursos do provedor. Nunca os edite manualmente.
- O Nível 2 é composto por APIs escritas à mão, orientadas à intenção e com padrões úteis. A maior parte do trabalho em funcionalidades pertence a este nível.
- O Nível 3 é composto por padrões que abrangem vários recursos. Adicione novos padrões somente quando o repositório aceitar explicitamente esse nível de abstração.
Projete o Nível 2 em torno da intenção do usuário, e não do esquema bruto do provedor. Oculte dos usuários comuns os detalhes do esquema gerado, a conexão interna, as ações de permissão e os identificadores físicos, mas continue expondo todo o conjunto de recursos do provedor.
Represente contratos de recursos como interfaces, para que implementações de terceiros possam se comportar como as nativas. Prefira a menor interface que atenda a um parâmetro: primeiro, uma referência que contenha somente o identificador; uma interface comportamental completa apenas quando forem necessárias operações de conveniência; e classes concretas somente em casos excepcionais.
## Decidir se cada recurso novo vai em um mixin, um facade ou um trait
Posicione cada funcionalidade de acordo com o que ela afeta:
1. O repasse direto de uma propriedade de baixo nível, sem lógica, pertence a um mixin de propriedades simples.
2. A lógica que altera as propriedades ou o ciclo de vida do próprio recurso pertence a um mixin independente.
3. O comportamento voltado a consumidores externos, como concessões, métricas ou eventos, pertence a uma fachada.
4. Um recurso transversal consultado por outros constructs pertence a um trait.
5. Nos demais casos, use um método no construct de Nível 2.
Não misture essas funções. Mixins são voltados para dentro, fachadas são voltadas para fora e traits anunciam recursos independentes do serviço.
Use type guards estáticos baseados em marcadores estáveis de runtime, em vez de `instanceof`, pois os objetos podem atravessar limites de pacotes ou linguagens. Mantenha bases abstratas disponíveis para implementação, mas não as aceite como parâmetros públicos.
## Nomear a API pública com coerência e evitar tipos que outras linguagens não suportam
Use nomes públicos consistentes: PascalCase para classes e enums, camelCase para propriedades e métodos, prefixo `I` para interfaces comportamentais, nenhum prefixo para estruturas de dados e letras maiúsculas separadas por sublinhado para membros de enums. Preserve a terminologia oficial da plataforma e inclua unidades quando não houver um tipo forte disponível.
Os objetos de propriedades devem ser somente leitura, planos e nomeados de acordo com o construct. Toda propriedade opcional precisa de um valor padrão documentado que explique o comportamento, em vez de apenas dizer que ele é indefinido. Prefira tipos fortes de duração, tamanho e referência de recurso a números brutos ou strings de identificador.
Não exponha tipos de baixo nível gerados por meio de propriedades de alto nível. Evite union types e outros constructs não aceitos pelo gerador de bindings; use classes semelhantes a enums, objetos de propriedades separados ou métodos fábrica.
A localização dos arquivos de tipos públicos faz parte do contrato externo. Não os mova casualmente. Evite mapped types ou conditional types, funções sobrecarregadas, namespaces específicos de linguagem e constantes de objeto exportadas quando não puderem ser representados de modo consistente nas linguagens de destino.
Não use métodos fluentes que retornem `this`; eles são mal traduzidos entre linguagens e ocultam mutações por trás do encadeamento.
## Escrever erros de validação claros e verificar antes os valores não resolvidos
Use erros de validação com escopo quando houver um contexto de construct e erros de validação sem escopo nos demais casos. Os códigos de erro devem ser literais em tempo de compilação, reutilizáveis quando a causa e a solução forem as mesmas, e as mensagens devem identificar o valor incorreto, os valores esperados e a correção necessária.
Prefira corrigir automaticamente configurações seguras em vez de lançar erros. Falhe antecipadamente em caso de uso incorreto da API, anexe uma validação pós-construção para o estado acumulado e emita diagnósticos de síntese para problemas ambientais. Não capture erros de construção irrecuperáveis; represente resultados realmente recuperáveis nos valores de retorno.
Valores não resolvidos podem representar strings, números, listas ou atributos de recursos. Verifique se um valor não foi resolvido antes de validar comprimento, intervalo, iteração ou valor lógico. Listas tokenizadas podem expor comprimentos de marcadores, e valores escalares tokenizados podem aparentar ser falsos.
Use verificações explícitas de valor indefinido para valores opcionais, uma conversão numérica ciente de tokens e nunca inclua atributos de recursos não resolvidos nos hashes usados para nomes físicos.
Para o estado acumulado após a construção, use uma caixa de valor adiado que capture os locais das chamadas de mutação. Use caixas de lista, valor, mapa ou conjunto; passe-as para propriedades de baixo nível por meio de adaptadores de token; altere-as por meio das operações das caixas; e nunca modifique a árvore de constructs dentro de um callback lazy ou de uma caixa.
## Manter permissões estreitas e proteger mudanças de comportamento com sinalizadores de recurso
Prefira ações de permissão específicas a curingas para todo o serviço, limite os identificadores de recursos ao prefixo mais restrito viável e agrupe ações relacionadas de acordo com o escopo do recurso. Use os auxiliares de concessão da biblioteca em vez de montar instruções de política manualmente.
As políticas de confiança devem incluir condições de conta e recurso de origem quando houver risco de confused deputy. As concessões de criptografia devem restringir o uso ao serviço pretendido. Emita um aviso durante a síntese sempre que uma configuração criar acesso público.
Quando o comportamento observável de uma API existente mudar, proteja a compatibilidade com uma feature flag:
- Use uma flag de correção de erro quando o comportamento antigo estiver incorreto.
- Use uma flag de padrão da API quando o comportamento antigo continuar válido, mas deixar de ser recomendado, e documente a compatibilidade com esse comportamento.
- Aplicações não configuradas devem preservar o comportamento anterior.
- Flags de segurança podem reduzir a confiança ou as permissões, mas não devem ampliá-las silenciosamente; um acesso mais amplo exige uma opção explícita da API.
Não use uma flag apenas porque existe um construct substituto. Em vez disso, descontinue o construct antigo e avise sobre mudanças de comportamento pelo mecanismo padrão de anotações.
## Compilar primeiro o pacote afetado e atualizar os snapshots de integração
Faça o build do pacote afetado antes de executar testes, lint, snapshots de integração ou verificações de exemplos da documentação. Durante a iteração, use o módulo ou objetivo de teste relevante mais restrito; antes da entrega, volte aos gates de pacote e compatibilidade exigidos pelo repositório.
Saiba quais arquivos são gerados, onde ficam os testes de integração estáveis e experimentais e onde as referências de design voltadas a agentes prevalecem sobre os guias mais extensos destinados a pessoas. Nunca edite diretamente fontes geradas de Nível 1.
Use os comandos de pacote e diretórios de trabalho locais do repositório, em vez de inventar caminhos alternativos de build. Execute correções automáticas de lint somente quando o diff resultante permanecer dentro do escopo solicitado.
Ao alterar identificadores lógicos, atualize todos os snapshots de integração afetados, inclusive snapshots entre pacotes. Um teste unitário local bem-sucedido não comprova que bindings gerados, exemplos da documentação ou templates de integração ainda compilam.
## Documentar toda API pública nova e manter os exemplos compiláveis
Documente toda nova classe, método, propriedade e interface pública. Use um resumo conciso, seguido por uma linha em branco e um corpo explicativo. Inclua tags de parâmetro, retorno, padrão, referência relacionada, exemplo e atributo gerado quando forem aplicáveis.
Os nomes de atributos devem começar pelo tipo de recurso, em vez de usar nomes ambíguos. Copie descrições oficiais da plataforma quando estiverem disponíveis, mas não duplique a documentação em overrides que a herdam de uma interface base.
Cada guia de módulo deve informar a maturidade, apresentar um exemplo simples próximo ao início e incluir exemplos dos principais casos de uso. Todos os exemplos tipados comuns devem compilar; use o marcador explícito de exclusão do repositório apenas quando um fixture realmente não puder ser aplicado.
Toda mudança de funcionalidade deve atualizar o guia do módulo relevante. Texto somente de documentação não substitui testes de API, e implementar a API não justifica exemplos desatualizados.
## Verificar propriedades específicas nos testes unitários e adicionar testes de integração
Em testes unitários, verifique as propriedades geradas específicas que importam, em vez de comparar templates inteiros ou aceitar valores arbitrários. Use deliberadamente matchers de objeto parcial, objeto exato, array, expressão regular e ausência.
Cubra valores de limite com testes orientados por dados. Testes de erro devem verificar o diagnóstico exato e identificar o comportamento de falha no nome do teste. Teste funções utilitárias separadamente dos constructs e preserve os testes de APIs descontinuadas.
Novas propriedades opcionais exigem testes de retrocompatibilidade que comprovem que o comportamento padrão não mudou. Os auxiliares de concessão devem ser testados com perfis, usuários e principais de serviço criados e importados.
Os testes de integração devem usar o construct de testes de integração do repositório sem uma chamada de síntese adicional. Assertions são preferíveis em todos os testes novos e obrigatórias para recursos personalizados. Evite pares de rede abertos, a menos que o acesso aberto seja o comportamento em teste.
Adicione cobertura de integração para novos tipos ou propriedades de recursos gerados, comportamento entre serviços, novas versões e recursos personalizados. Separe os testes por área funcional e mantenha consistentes as extensões e localizações dos snapshots.
## Escrever títulos de mudança ligados à issue e nunca mudar identificadores estáveis
Use títulos de mudança convencionais com o módulo afetado, quando aplicável. Mudanças de funcionalidade e correções devem fazer referência a uma issue. Mudanças de funcionalidade exigem testes unitários, snapshots de integração e atualizações da documentação do módulo.
Mudanças incompatíveis são permitidas somente em pacotes explicitamente experimentais e devem ser declaradas com destaque na descrição da mudança. Mantenha uma única questão por mudança; envie edições cosméticas separadamente.
Nunca adicione abstrações especulativas, altere identificadores estáveis de constructs, deixe código comentado ou morto nem silencie regras de lint para ocultar violações. Mudanças em identificadores podem substituir recursos implantados e causar perda de dados.
Antes de adicionar validação a uma API existente, determine se uma entrada aceita anteriormente poderia ser implantada com sucesso. Em caso afirmativo, proteja a nova rejeição com uma flag de compatibilidade. Se ela sempre falhava na implantação, prefira uma falha antecipada na síntese e documente por que a validação mais rigorosa é justificada.
Antes da entrega, revise o diff em busca de edições em arquivos gerados, movimentações de tipos públicos, sintaxe incompatível com bindings, snapshots desatualizados, documentação ausente, regressões de compatibilidade e mudanças não relacionadas.
