Use Dependências de Forma Idiomática
Toda dependência foi construída para um formato específico de problema. Sua API codifica esse modelo: o que ela possui, o que deixa para você, o que garante e onde espera ser estendida. Escreva a solução idiomática, aquela que os próprios documentos e exemplos canônicos da biblioteca mostrariam, e quando o problema tiver um formato diferente (um desajuste de impedância), escolha a ferramenta certa para o trabalho. As falhas a evitar são reinventar a roda, lutar contra o framework e subutilizar silenciosamente uma funcionalidade construída para o caso.
Atrito é um cheiro: um cast, patch ou workaround no caminho óbvio significa que o modelo foi mal interpretado ou a ferramenta está errada. As violações mais custosas não têm atrito algum: elas passam na verificação de tipos e parecem locais enquanto derrotam uma garantia que a biblioteca existe para fornecer, então procure por elas propositalmente.
Escrevendo código que usa uma dependência
O caminho padrão. Revisão, auditoria, adição e atualização partem dele (abaixo).
-
Reformule o problema. Cuidado com o problema XY: a tarefa frequentemente chega como uma solução, vinda de você ou do usuário. Reformule-a como o problema geral:
- "Repetir o upload três vezes" é uma política de repetição.
- "Preciso de
row_number() over (partition by account_id)para as faturas mais recentes de cada conta" é top-N por grupo, que um ORM pode modelar sem função de janela.
"A biblioteca não consegue fazer X" é uma afirmação a verificar no passo 3, independentemente de quem a faça.
Concluído quando você consegue nomear o problema nas palavras que os documentos da biblioteca usariam.
-
Encontre onde já está resolvido, primeiro o mais próximo:
- O framework no qual você está inserido: políticas de repetição e temporizadores de um mecanismo de fluxo de trabalho, paginação e atualizadores de armazenamento de um cliente GraphQL, temporizadores falsos de um executor de testes.
- As dependências declaradas do projeto (
package.json,pyproject.toml,go.mod,Cargo.toml) e como a base de código as importa. O uso existente mostra a convenção da casa, que pode diferir do design da biblioteca. - Os pacotes compartilhados do projeto, que frequentemente encapsulam uma dependência da maneira que o projeto deseja usá-la.
- A biblioteca padrão, para a versão do runtime em uso (
engines,.nvmrc,requires-python). As bibliotecas padrão abrangentes cobrem mais do que as pessoas lembram:itertools,collections,functools,pathlibdo Python;fetch,AbortSignal.timeout,util.parseArgs,node:testdo Node moderno.
Concluído quando cada fonte é verificada e você tem uma lista de candidatos, possivelmente vazia.
-
Aprenda o modelo a partir da documentação da versão instalada. Considere a versão do arquivo de bloqueio; uma fonte conta apenas se corresponder. Leia, em ordem:
- A orientação do projeto para esta dependência, encontrada e mantida em GUIDANCE.md. Ela é a autoridade sobre decisões deliberadas da casa e um cache do modelo da biblioteca: quando sua versão registrada difere do arquivo de bloqueio, ou uma afirmação decide sua abordagem, confirme-a nas fontes abaixo.
- Um checkout de referência: o repositório da dependência clonado no projeto ou obtido por uma ferramenta como
opensrc, geralmente com um ponteiro emAGENTS.mdouCLAUDE.md. Verifique sua tag em relação ao arquivo de bloqueio. Seus documentos, exemplos e testes são o melhor registro do uso pretendido. - O pacote instalado: tipos exportados e seus comentários de documentação, frequentemente sem os documentos e exemplos.
- Online, quando nenhuma das duas últimas tem documentos: a documentação oficial para aquela versão principal, páginas de conceito e guia, bem como a referência da API; em seguida, o repositório upstream na tag correspondente.
Construa apenas sobre a API pública: o que os documentos descrevem, alcançado por meio de pontos de entrada declarados (um mapa
exports,__all__, nomes sem sublinhado inicial). Leia o código-fonte para entender o comportamento; qualquer coisa que você encontrou apenas lá é privada. O Semver protege a API pública e nada mais. Sua memória da biblioteca é uma hipótese a ser verificada contra os documentos, e arquivos de runtime que você por acaso abre não substituem a documentação.Concluído quando você consegue responder a partir dos documentos, antes de escrever ou julgar código que a utiliza:
- Para que foi construída e o que ela deliberadamente deixa de fora?
- O que ela possui (armazenamento, cache, ciclo de vida, repetições, transações), o que ela gera ou pede que você declare (saída de codegen, esquemas, tipos), e para qual parte do seu código cada peça é?
- Qual garantia ela mantém (mascaramento de dados, determinismo de fluxo de trabalho, isolamento de transações), entre quais partes do sistema, e o que a derrotaria silenciosamente? Frequentemente, ela fica entre seus próprios módulos.
- Qual API pública e pontos de extensão documentados (opções, plugins, middleware, hooks, escotilhas de escape) cobrem isso, e como é o exemplo canônico, até quais módulos importam o quê?
- Quais verificações ela fornece (um plugin de lint, validação de compilador ou codegen, um modo de tipo estrito), e o projeto as executa?
-
Escolha o ajuste:
- O problema corresponde ao modelo: use-o.
- Parte dele fica fora da API principal: use a escotilha de escape documentada para essa parte (SQL bruto em um ORM, um componente sem cabeça, um resolvedor personalizado). Escotilhas de escape fazem parte do design.
- A versão instalada não possui o recurso: se o changelog mostrar que uma versão mais recente o tem, proponha a atualização conforme UPGRADING.md.
- Desajuste de impedância: use uma dependência cujo modelo se ajuste, ou código simples que fique longe desta, atrás de uma costura que possuímos. Estar instalada, ou usada nas proximidades, não torna uma biblioteca a ferramenta certa.
Concluído quando você escolheu uma e pode justificá-la a partir do modelo.
-
Escreva do jeito da biblioteca, em sua API pública. A primeira versão que funciona frequentemente não é a idiomática. Onde o código vizinho e o exemplo canônico discordarem, siga o exemplo canônico e informe o usuário sobre a deriva. Execute as próprias verificações da biblioteca enquanto avança; se o projeto as desabilitar, diga o que ativá-las pegaria.
Concluído quando as verificações da biblioteca passam, nenhum cheiro abaixo se aplica (ou cada um que se aplica é uma escolha que você relatará), e os mantenedores da biblioteca reconheceriam o código como a biblioteca usada conforme o design.
-
Quando o atrito persistir, primeiro adapte nosso design ao modelo da biblioteca; suas restrições frequentemente codificam uma lição (regras de determinismo de um mecanismo de fluxo de trabalho, mascaramento de dados de um cliente GraphQL). Em seguida, revisite o passo 4. Trabalhe contra a biblioteca apenas com o acordo do usuário, nomeando o comportamento não documentado no qual você confia na proposta e em um comentário no workaround. Um conflito entre o modelo e um requisito de produto é uma decisão do usuário.
Cheiros: lutando contra a biblioteca
Cada um o envia de volta ao passo 4. Classifique pelo que a biblioteca existe para fornecer: uma garantia derrotada supera todo o resto, mesmo quando nada está quebrado hoje, e uma regra da casa que codifica o modelo assume a gravidade do modelo.
- Garantia derrotada: código gerado usado fora do módulo para o qual foi gerado (tipos de fragmento de outro componente), leitura de dados mascarados de outro módulo, um fragmento ou esquema compartilhado entre chamadores não relacionados, não determinismo dentro de um fluxo de trabalho determinístico. Eles passam na verificação de tipos silenciosamente, então procure por eles deliberadamente.
- Casts ou supressões de verificador de tipos (
as,any,@ts-expect-error,# type: ignore) onde nosso código encontra os tipos da biblioteca. - API privada: caminhos internos (
pkg/dist/…,pkg._internal), nomes com sublinhado, exportações não documentadas, casts em campos privados de um objeto da biblioteca, comportamento encontrado apenas no código-fonte. Uma API instável ou experimental é documentada, mas fora do semver: use-a quando os documentos a recomendarem aqui, e diga isso. - Um patch que altera o comportamento da biblioteca (patch do gerenciador de pacotes, monkey-patching, fork vendido). Um patch que a faz fazer o que seus documentos já prometem é uma correção de bug e a ação correta: mantenha-o focado na correção e vincule o problema upstream.
- Estado duplicado: uma segunda fonte de verdade para algo que a biblioteca possui, como um cache GraphQL espelhado no estado da store ou contagens de repetição que o framework já rastreia.
- Temporizadores, flags ou cadeias de efeitos que executam código "após" a biblioteca, onde ela oferece um callback, evento ou hook.
- Um padrão substituído em todo ponto de chamada, ou um wrapper que desfaz um comportamento em toda chamada.
- Um wrapper raso que basicamente renomeia a API que encapsula. Aplique o teste de exclusão: se excluí-lo apenas move suas chamadas de volta para a biblioteca, ele não está valendo seu custo.
- Parte de um recurso da biblioteca reconstruída porque o todo parecia mais do que você precisava.
Cheiros: subutilizando a biblioteca
O código funciona na API pública, mas uma funcionalidade construída para este caso o tornaria mais curto ou mais declarativo. Para encontrá-la, leia o tipo de cada objeto de opções que você passa e o guia da biblioteca para o problema do passo 1.
- Várias chamadas fazendo uma operação: find-then-create onde há um upsert, escritas em loop onde há uma API em lote.
- Código cola remodelando a saída, onde a consulta ou hook aceita uma opção de seleção ou projeção.
- Estado que a biblioteca rastreia, reconstruído manualmente: flags de carregamento, pendente, sujo ou repetição.
- As mesmas opções em todo ponto de chamada, onde há padrões, configuração compartilhada, um tema ou uma fábrica.
- Código imperativo para algo que a biblioteca permite declarar: um esquema, uma variante, uma relação, uma política.
A proporção funciona nos dois sentidos: quando algumas linhas claras de código simples fazem o trabalho, o código simples é idiomático.
Revisando, auditando, adicionando e atualizando
- Revisando a alteração de outra pessoa: leia REVIEWING.md.
- Auditando o uso de uma dependência em uma base de código: leia AUDITING.md.
- Adicionando um pacote, ou atualizando uma dependência ou o runtime: leia UPGRADING.md primeiro.
- Encontrando, escrevendo ou atualizando a orientação do projeto para uma dependência: leia GUIDANCE.md.
Responda: os recursos da biblioteca que carregam o comportamento que você não escreveu; para código de propósito geral que você escreveu, o que você verificou e por que nada se encaixou; cada dependência que você ignorou e a parte de seu modelo que não correspondia; e cada cheiro que você manteve, com o motivo. Quando você aprendeu algo duradouro sobre a dependência, adicione a orientação que propõe, conforme GUIDANCE.md.