Durante décadas, a reutilização de software foi discutida com mais frequência do que realmente era. Hoje a situação é oposta: os desenvolvedores reutilizam os programas de outras pessoas todos os dias na forma de dependências de software, e o problema em si permanece quase inexplorado.
Minha própria experiência inclui uma década de trabalho com
o repositório interno do Google , em que as dependências são definidas como um conceito de prioridade, além de desenvolver
um sistema de dependência para a linguagem de programação Go .
As dependências acarretam sérios riscos que são frequentemente negligenciados. A transição para a reutilização simples dos menores pedaços de software ocorreu tão rapidamente que ainda não desenvolvemos as práticas recomendadas para a seleção e o uso eficazes de dependências. Mesmo para tomar decisões quando elas são apropriadas e quando não são. O objetivo deste artigo é avaliar riscos e estimular a busca de soluções nessa área.
O que é vício?
No desenvolvimento moderno,
dependência é um código adicional chamado de um programa. A adição de uma dependência evita a repetição do trabalho já realizado: design, gravação, teste, depuração e suporte a uma unidade específica de código. Chamamos essa unidade de código de
pacote , embora em alguns sistemas outros termos, como uma biblioteca ou módulo, sejam usados no lugar de um pacote.
Aceitar dependências externas é uma prática antiga: a maioria dos programadores baixou e instalou a biblioteca necessária, seja PCRE ou zlib do C, Boost ou Qt do C ++, JodaTime ou Junit do Java. Esses pacotes possuem código depurado de alta qualidade que requer experiência considerável para ser criado. Se um programa precisar da funcionalidade desse pacote, é muito mais fácil baixar, instalar e atualizar manualmente o pacote do que desenvolver essa funcionalidade do zero. Mas grandes custos iniciais significam que a reutilização manual é cara: pacotes pequenos são mais fáceis de escrever.
Um gerenciador de dependências (às vezes chamado de gerenciador de pacotes) automatiza o download e a instalação de pacotes de dependências. Como os gerenciadores de dependência facilitam o download e a instalação de pacotes individuais, a redução de custos fixos torna os pequenos pacotes econômicos para publicação e reutilização.
Por exemplo, um gerenciador de dependência do Node.j chamado NPM fornece acesso a mais de 750.000 pacotes. Um deles,
escape-string-regexp
, contém uma única função que escapa os operadores de expressão regular dos dados de entrada. Toda implementação:
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; module.exports = function (str) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } return str.replace(matchOperatorsRe, '\\$&'); };
Antes que os gerentes de dependência aparecessem, era impossível imaginar a publicação de uma biblioteca de oito linhas: muita sobrecarga e muito pouco benefício. Mas o NPM reduziu a sobrecarga para quase zero, com o resultado de que funcionalidade quase trivial poderia ser empacotada e reutilizada. No final de janeiro de 2019, a dependência
escape-string-regexp
foi incorporada a quase mil outros pacotes NPM, sem mencionar todos os pacotes que os desenvolvedores escrevem para seu próprio uso e não publicam em domínio público.
Agora, os gerentes de dependência apareceram para quase todas as linguagens de programação. Maven Central (Java), Nuget (.NET), Packagist (PHP), PyPI (Python) e RubyGems (Ruby) - cada um deles possui mais de 100.000 pacotes. O advento dessa reutilização generalizada de pequenos pacotes é uma das maiores mudanças no desenvolvimento de software nas últimas duas décadas. E se não tomarmos mais cuidado, isso levará a sérios problemas.
O que poderia dar errado?
No contexto desta discussão, um pacote é um código baixado da Internet. Adicionar uma dependência confia o trabalho de desenvolver esse código - design, gravação, teste, depuração e suporte - a outra pessoa na Internet que você geralmente não conhece. Usando esse código, você expõe seu próprio programa aos efeitos de todas as falhas e falhas da dependência. A execução do seu software agora
depende literalmente do código de um estranho da Internet. Para colocar dessa maneira, tudo parece muito inseguro. Por que alguém concordaria com isso?
Concordamos, porque é fácil, porque tudo parece funcionar, porque todo mundo também o faz, e o mais importante, porque parece ser uma continuação natural de uma prática estabelecida há séculos. Mas há uma diferença importante que ignoramos.
Décadas atrás, a maioria dos desenvolvedores também confiava em outros para escrever programas dos quais dependiam, como sistemas operacionais e compiladores. Este software foi comprado de fontes conhecidas, geralmente com algum tipo de contrato de suporte. Ainda há espaço para
erros ou destruição definitiva . Mas pelo menos sabíamos com quem estávamos lidando e, como regra, podiam usar medidas comerciais ou legais de influência.
O fenômeno do software de código aberto, distribuído gratuitamente pela Internet, suplantou amplamente a antiga prática de compra de software. Quando a reutilização ainda era difícil, poucos projetos introduziram essas dependências. Embora suas licenças geralmente dispensem quaisquer "garantias de valor comercial e adequação a um propósito específico", os projetos construíram uma boa reputação. Os usuários levaram essa reputação em consideração ao tomar suas decisões. Em vez de intervenções comerciais e legais, veio o apoio à reputação. Muitos pacotes comuns dessa época ainda gozam de boa reputação: por exemplo, BLAS (publicado em 1979), Netlib (1987), libjpeg (1991), LAPACK (1992), HP STL (1994) e zlib (1995).
Os gerentes de lote reduziram o modelo de reutilização de código à extrema simplicidade: agora os desenvolvedores podem compartilhar o código com precisão para funções individuais em dezenas de linhas. Esta é uma grande conquista técnica. Existem inúmeros pacotes disponíveis, e um projeto pode incluir um grande número deles, mas mecanismos de confiança de código comercial, jurídico ou de reputação são coisa do passado. Confiamos em mais código, embora haja menos razões para confiar.
O custo de criar um vício ruim pode ser visto como a soma de todos os possíveis resultados ruins em uma série do preço de cada resultado ruim multiplicado por sua probabilidade (risco).
O preço de um resultado ruim depende do contexto em que a dependência é usada. Em uma extremidade do espectro, há um projeto de hobby pessoal, em que o preço da maioria dos resultados ruins é próximo de zero: você se diverte, os erros não têm impacto real, exceto por um pouco mais de tempo gasto, e a depuração deles pode até ser divertido. Assim, a probabilidade de risco é quase irrelevante: é multiplicada por zero. No outro extremo do espectro está o software de produção, que deve ser suportado por anos. Aqui, o custo da dependência pode ser muito alto: os servidores podem cair, os dados confidenciais podem ser divulgados, os clientes podem sofrer, as empresas podem até falir. Na produção, é muito mais importante avaliar e minimizar o risco de uma falha grave.
Independentemente do preço esperado, existem algumas abordagens para avaliar e reduzir os riscos de adicionar dependências. É provável que os gerenciadores de pacotes sejam otimizados para reduzir esses riscos, enquanto até agora eles se concentraram em reduzir o custo de download e instalação.
Verificação de dependência
Você não contrataria um desenvolvedor que você nunca ouviu falar e que não conhece. Primeiro, você aprenderá algo sobre ele: verifique os links, realize uma entrevista e assim por diante. Antes de depender do pacote encontrado na Internet, também é aconselhável aprender um pouco sobre esse pacote.
Uma verificação básica pode dar uma idéia da probabilidade de problemas ao tentar usar esse código. Se forem encontrados pequenos problemas durante a inspeção, você poderá tomar medidas para eliminá-los. Se a verificação revelar problemas sérios, pode ser melhor não usar o pacote: você pode encontrar um mais adequado ou talvez precise desenvolvê-lo. Lembre-se de que os pacotes de código aberto são publicados pelos autores na esperança de que sejam úteis, mas sem garantir a usabilidade ou o suporte. No caso de uma falha de produção, é sua responsabilidade depurá-la. Como a primeira
Licença Pública Geral GNU advertiu, “todo o risco associado à qualidade e desempenho do programa está com você. Se o programa estiver com defeito, você arcará com os custos de toda manutenção, reparo ou correção necessária. ”
A seguir, descrevemos algumas considerações para verificar o pacote e decidir se deve ou não depender dele.
Desenho
A documentação do pacote está clara? A API tem um design claro? Se os autores puderem explicar bem a API e o design para uma pessoa, isso aumentará a probabilidade de que eles também expliquem bem a implementação do computador no código-fonte. Escrever código para uma API clara e bem projetada é mais simples, rápido e provavelmente menos propenso a erros. Os autores documentaram o que esperam do código do cliente para ser compatível com futuras atualizações? (Exemplos incluem documentos de compatibilidade
C ++ e
Go ).
Qualidade do código
O código está bem escrito? Leia alguns trechos. Os autores parecem ser cuidadosos, conscientes e consistentes? Parece o código que você deseja depurar? Você pode ter que fazer isso.
Desenvolva suas próprias maneiras sistemáticas de verificar a qualidade do código. Algo simples, como compilar em C ou C ++ com avisos importantes do compilador ativados (por exemplo,
-Wall
), pode dar uma idéia de quão seriamente os desenvolvedores trabalharam para evitar vários comportamentos indefinidos. Idiomas recentes, como Go, Rust e Swift, usam a palavra-chave
unsafe
para denotar código que viola o sistema de tipos; veja quanto código inseguro existe. Ferramentas semânticas mais avançadas, como
Infer ou
SpotBugs, também são úteis. Linters são menos úteis: você deve ignorar dicas padrão sobre tópicos como estilo entre parênteses e se concentrar em questões semânticas.
Não se esqueça dos métodos de desenvolvimento com os quais você talvez não esteja familiarizado. Por exemplo, a biblioteca SQLite vem como um único arquivo com 200.000 códigos e um cabeçalho de 11.000 linhas - como resultado da mesclagem de vários arquivos. O tamanho desses arquivos imediatamente levanta uma bandeira vermelha, mas uma investigação mais aprofundada levará ao código fonte real do desenvolvimento: uma árvore de arquivos tradicional com mais de cem arquivos C de origem, testes e scripts de suporte. Acontece que a distribuição de arquivo único é criada automaticamente a partir das fontes originais: isso é mais fácil para os usuários finais, especialmente aqueles que não possuem gerenciadores de dependência. (O código compilado também funciona mais rápido porque o compilador vê mais opções de otimização).
Teste
Existem testes no código? Você pode controlá-los? Eles passam? Os testes estabelecem que a principal funcionalidade do código está correta e sinalizam que o desenvolvedor está tentando mantê-lo seriamente. Por exemplo, a árvore de desenvolvimento SQLite contém um conjunto de testes incrivelmente detalhado com mais de 30.000 casos de teste individuais. Há
documentação para desenvolvedores explicando a estratégia de teste. Por outro lado, se houver poucos ou nenhum teste, ou se os testes falharem, isso é uma bandeira vermelha séria: futuras alterações no pacote provavelmente levarão a regressões que poderiam ser facilmente detectadas. Se você insistir em testes no seu código (certo?), Você deve fornecer testes para o código que passar para outras pessoas.
Supondo que os testes existam, executem e passem, você pode coletar informações adicionais executando ferramentas para analisar a cobertura do código,
detectar condições de corrida , verificar a alocação de memória e detectar vazamentos de memória.
Depuração
Encontre o rastreador de erros para este pacote. Existem muitas mensagens de erro em aberto? Há quanto tempo eles estão abertos? Quantos bugs foram corrigidos? Há algum erro corrigido recentemente? Se houver muitas perguntas em aberto sobre erros reais, especialmente não fechadas por muito tempo, isso é um mau sinal. Por outro lado, se os erros são raros e rapidamente corrigidos, isso é ótimo.
Suporte
Veja a história dos commits. Há quanto tempo o código é mantido ativamente? É suportado ativamente agora? Os pacotes que foram ativamente suportados por um longo período de tempo provavelmente continuarão sendo suportados. Quantas pessoas estão trabalhando no pacote? Muitos pacotes são projetos pessoais que os desenvolvedores criam para entretenimento em seu tempo livre. Outros são o resultado de milhares de horas de trabalho para um grupo de desenvolvedores pagos. Em geral, os pacotes do segundo tipo geralmente corrigem os erros mais rapidamente, introduzem constantemente novas funções e, em geral, são mais bem suportadas.
Por outro lado, algum código é realmente "perfeito". Por exemplo,
escape-string-regexp
do NPM talvez nunca precise ser alterado novamente.
Use
Quantos pacotes dependem desse código? Os gerentes de pacotes geralmente fornecem essas estatísticas, ou você pode ver na Internet quantas vezes outros desenvolvedores mencionam esse pacote. Um número maior de usuários significa pelo menos o fato de que para muitos o código funciona muito bem, e os erros nele serão notados mais rapidamente. O uso generalizado também é uma garantia parcial de serviço continuado: se um pacote amplamente usado perder seu mantenedor, é muito provável que o usuário interessado assuma sua função.
Por exemplo, bibliotecas como PCRE, Boost ou JUnit são incrivelmente usadas. Isso torna mais provável - embora certamente não garanta - que os erros que você pode ter encontrado já foram corrigidos porque outras pessoas os encontraram antes de você.
Segurança
Este pacote funcionará com entrada não segura? Em caso afirmativo, qual a resistência a dados maliciosos? Ele tem bugs mencionados no
National Vulnerability Database (NVD) ?
Por exemplo, quando em 2006, Jeff Dean e eu começamos a trabalhar no
Google Code Search (
grep
para bases de código públicas), a popular biblioteca de expressões regulares PCRE parecia ser a escolha óbvia. No entanto, em uma conversa com a equipe de segurança do Google, descobrimos que o PCRE tem um longo histórico de problemas, como estouros de buffer, especialmente no analisador. Nós mesmos estávamos convencidos disso, procurando por PCRE em NVD. Essa descoberta não nos levou a abandonar imediatamente o PCRE, mas nos fez pensar com mais cuidado sobre testes e isolamento.
Licenciamento
O código está licenciado corretamente? Ele tem licença? A licença é aceitável para o seu projeto ou empresa? Uma parte incrível dos projetos do GitHub não possui uma licença clara. Seu projeto ou empresa pode colocar restrições adicionais nas licenças de dependência. Por exemplo, o Google
proíbe o uso de código sob licenças como AGPL (muito apertado) e tipo WTFPL (muito vago).
Dependências
Este pacote tem suas próprias dependências? Deficiências em dependências indiretas são tão prejudiciais quanto desvantagens em dependências diretas. Os gerenciadores de pacotes podem listar todas as dependências transitivas de um determinado pacote, e cada uma delas deve, idealmente, ser verificada conforme descrito nesta seção. Um pacote com muitas dependências exigirá muito trabalho.
Muitos desenvolvedores nunca examinaram a lista completa de dependências transitivas de seu código e não sabem do que dependem. Por exemplo, em março de 2016, a comunidade de usuários do NPM descobriu que muitos projetos populares - incluindo Babel, Ember e React - dependem indiretamente de um pequeno pacote chamado
left-pad
de uma função de 8 linhas. Eles descobriram isso quando o autor do
left-pad
removeu o pacote do NPM,
interrompendo inadvertidamente a maioria dos conjuntos de usuários do Node.js. E o
left-pad
não
left-pad
excepcional a esse respeito. Por exemplo, 30% dos 750.000 pacotes no NPM dependem - pelo menos indiretamente - do
escape-string-regexp
. Adaptando a observação de Leslie Lamport dos sistemas distribuídos, o gerenciador de pacotes cria facilmente uma situação em que uma falha no pacote, cuja existência você nem sabia, poderia tornar seu próprio código inutilizável.
Teste de dependência
O processo de verificação deve incluir a execução de seus próprios testes de pacote. Se o pacote passou no teste e você decide tornar seu projeto dependente, o próximo passo deve ser escrever novos testes focados especificamente na funcionalidade do seu aplicativo. Esses testes geralmente iniciam como programas independentes curtos para garantir que você possa entender a API do pacote e fazer o que pensa (se você não consegue entender ou se não faz o que precisa, pare imediatamente!). Vale a pena o esforço extra para transformar esses programas em testes automatizados que serão executados com novas versões do pacote. Se você encontrar um erro e tiver uma correção em potencial, poderá reiniciar facilmente esses testes para um projeto específico e verifique se a correção não quebrou mais nada.
Atenção especial deve ser dada às áreas problemáticas identificadas durante a revisão da linha de base. Para a Pesquisa de código, por experiência anterior, sabíamos que o PCRE às vezes leva muito tempo para executar certas expressões regulares. Nosso plano inicial era criar conjuntos de encadeamentos separados para expressões regulares "simples" e "complexas". Um dos primeiros testes foi uma referência que comparou o
pcregrep
com várias outras implementações
grep
. Quando descobrimos que o
pcregrep
era 70 vezes mais lento que o
grep
mais rápido para um caso de teste básico, começamos a repensar nosso plano de usar o PCRE. Apesar do fato de que finalmente abandonamos completamente o PCRE, esse teste permanece em nossa base de código hoje.
Abstração de Dependência
A dependência de pacote é uma solução da qual você pode optar por não participar no futuro. Talvez as atualizações levem o pacote para uma nova direção. Problemas graves de segurança podem ser encontrados. Talvez a melhor opção apareça. Por todos esses motivos, vale a pena o esforço para simplificar a migração do projeto para uma nova dependência.
, . , API , , API, . , , . , , , , . , , . , .
Para a Pesquisa de código, desenvolvemos uma classe abstrata Regexp
que define a interface de Pesquisa de código necessária a partir de qualquer mecanismo de expressão regular. Eles escreveram um invólucro fino em torno do PCRE que implementa essa interface. Esse método facilitou o teste de bibliotecas alternativas e impediu a introdução acidental de conhecimento dos componentes internos do PCRE no restante da árvore de origem. Isso, por sua vez, garante que, se necessário, será fácil mudar para outra dependência.Isolamento de dependência
Também pode ser apropriado isolar a dependência no tempo de execução para limitar o possível dano causado por erros nela. Por exemplo, o Google Chrome permite que os usuários adicionem dependências ao código de extensão do navegador. Quando o Chrome foi lançado pela primeira vez em 2008, ele introduziu uma função crítica (agora padrão em todos os navegadores) para isolar cada extensão em uma sandbox em execução em um processo separado do sistema operacional. Uma exploração potencial em uma extensão mal gravada não tinha acesso automático a toda a memória do próprio navegadore não pôde fazer chamadas inadequadas ao sistema. Para a pesquisa de código, até que eliminássemos completamente o PCRE, o plano era pelo menos isolar o analisador de PCRE em uma caixa de areia semelhante. Hoje, outra opção seria uma sandbox leve baseada em hipervisor, como o gVisor . O isolamento de dependência reduz os riscos associados à execução desse código.- . , . , C C++, , , Java JNI, Go, Rust Swift, unsafe. , JavaScript, , . 2018 . , npm
event-stream
( API JavaScript)
adicionado dois meses e meio atrás. O código coletou carteiras de bitcoin de usuários do aplicativo móvel Copay, ganhou acesso a recursos do sistema completamente não relacionados ao processamento de fluxos de eventos. Uma das muitas maneiras possíveis de se proteger contra esses tipos de problemas seria um melhor isolamento de dependência.Abandono do vício
, .
, PCRE, Google Code Search « PCRE » « PCRE, », « , PCRE», « , ». , , :
RE2 .
Se você precisar de apenas uma pequena parte da dependência, a maneira mais fácil é fazer uma cópia do que você precisa (é claro, mantendo os direitos autorais relevantes e outros avisos legais). Você assume a responsabilidade pela correção de erros, manutenção etc., mas também está completamente isolado de riscos maiores. Há um ditado na comunidade de desenvolvedores Go : "Um pouco de cópia é melhor do que um pouco de dependência".Atualização de Dependência
Por um longo tempo, a sabedoria geralmente aceita no software foi: "Se funcionar, não toque em nada". A atualização corre o risco de introduzir novos erros; sem recompensa - se você não precisa de um novo recurso, por que correr o risco? Essa abordagem ignora dois aspectos. Primeiro, o custo de uma atualização gradual. No software, a complexidade de fazer alterações no código não é dimensionada linearmente: dez pequenas alterações são menos trabalhosas e mais fáceis do que uma grande alteração correspondente. Em segundo lugar, a dificuldade de detectar erros já corrigidos. Especialmente no contexto de segurança, onde os erros conhecidos são explorados ativamente, todos os dias, sem atualização, aumenta os riscos de que os invasores possam tirar proveito de bugs no código antigo.Por exemplo, considere a história de Equifax de 2017, que os executivos contaram em detalhes em testemunhos perante o Congresso. Em 7 de março, uma nova vulnerabilidade foi descoberta no Apache Struts e uma versão corrigida foi lançada. Em 8 de março, a Equifax recebeu uma notificação do US-CERT da necessidade de atualizar qualquer uso do Apache Struts. A Equifax lançou uma verificação do código fonte e da rede em 9 e 15 de março, respectivamente; nem uma única verificação encontrou servidores Web vulneráveis abertos na Internet. Em 13 de maio, os invasores encontraram servidores que os especialistas da Equifax não encontraram. Eles usaram a vulnerabilidade Apache Struts para invadir a rede Equifax e roubaram informações pessoais e financeiras detalhadas sobre 148 milhões de pessoas nos próximos dois meses. Finalmente, em 29 de julho, Equifax notou um hack e o anunciou publicamente em 4 de setembro. No final de setembro, o CEO da Equifax, assim como o CIO e o CSO, renunciaram e uma investigação foi iniciada no Congresso.A experiência da Equifax leva ao fato de que, embora os gerenciadores de pacotes saibam as versões que eles usam durante a construção, você precisa de outros mecanismos para rastrear essas informações durante a implantação na produção. Para o idioma Go, estamos experimentando incluir automaticamente o manifesto manifesto em cada binário, para que os processos de implantação possam varrer os binários em busca de dependências que exijam atualização. O Go também disponibiliza essas informações em tempo de execução, para que os servidores possam acessar bancos de dados de erros conhecidos e reportar independentemente ao sistema de monitoramento quando precisarem ser atualizados.Uma atualização rápida é importante, mas atualizar significa adicionar um novo código ao projeto, o que deve significar atualizar a avaliação de risco do uso de dependência com base na nova versão. No mínimo, você deseja ver as diferenças mostrando as alterações que estão sendo feitas da versão atual para as versões atualizadas ou, pelo menos, ler as notas de versão para identificar as áreas problemáticas mais prováveis no código atualizado. Se um monte de código for alterado, portanto as diferenças são difíceis de entender, essas também são informações que você pode incluir na atualização de sua avaliação de risco.Além disso, você deve executar novamente os testes escritos especificamente para o projeto para garantir que o pacote atualizado seja pelo menos tão adequado para o projeto quanto a versão anterior. Também faz sentido executar novamente seus próprios testes de pacotes. Se o pacote tiver suas próprias dependências, é possível que a configuração do projeto use outras versões dessas dependências (mais antigas ou mais recentes) que aquelas usadas pelos autores do pacote. A execução de seus próprios testes de pacote permite identificar rapidamente problemas específicos da configuração.Novamente, as atualizações não precisam ser totalmente automáticas. Antes de implantar versões atualizadas , verifique se elas são apropriadas para o seu ambiente ., , .
. Equifax , (, ) Apache Struts 10 , , .
whoami
.
. - .
-, , . , . , , .
event-stream
, injeta automaticamente código malicioso na versão 3.3.5 já lançada. Em vez disso, o invasor teve que criar uma nova versão 3.3.6 e aguardar a atualização das pessoas (sem observar cuidadosamente as alterações).Também é importante monitorar o surgimento de novas dependências indiretas: as atualizações podem facilmente introduzir novos pacotes, dos quais agora depende o sucesso do seu projeto. Eles também merecem sua atenção. No caso, o event-stream
código malicioso estava oculto em outro pacote flatMap-stream
, que event-stream
foi adicionado como uma nova dependência na nova versão .Dependências rastejantes também podem afetar o tamanho do projeto. Durante o desenvolvimento do Google Sawzall — JIT- — , JIT Sawzall, () PostScript, Python JavaScript. , - Sawzall, , Google . Go .
— . ,
está mudando. Parece plausível que não haja problemas de segurança ou outros erros a serem corrigidos? O projeto é abandonado? Talvez seja hora de planejar uma substituição para essa dependência.Também é importante verificar duas vezes o log de segurança de cada dependência. Por exemplo, o Apache Struts revelou sérias vulnerabilidades na execução remota de código em 2016, 2017 e 2018. Mesmo que você tenha muitos servidores que o iniciam e atualizam rapidamente, esse histórico sugere se vale a pena usá-lo.Conclusão
A era da reutilização de software finalmente chegou, e eu não quero subestimar os benefícios: trouxe uma transformação extremamente positiva para os desenvolvedores. No entanto, adotamos essa transformação sem considerar plenamente as possíveis consequências. Os motivos anteriores para confiar nas dependências perdem relevância ao mesmo tempo em que temos mais dependências do que nunca.A análise crítica de dependências específicas que descrevi neste artigo representa uma quantidade significativa de trabalho e permanece a exceção e não a regra. Mas duvido que existam desenvolvedores que estão realmente trabalhando duro para fazer isso para cada novo vício possível. Fiz apenas parte deste trabalho para algumas de minhas próprias dependências. Basicamente, toda a solução se resume ao seguinte: "vamos ver o que acontece". Muitas vezes, algo mais parece muito esforço.Mas os ataques de Copay e Equifax são alertas claros de problemas reais na maneira como usamos as dependências de software atualmente. Não devemos ignorar avisos. Eu ofereço três recomendações gerais.- . , , , . , .
- . , , . , , . , , , .
- . . . , . , , . , , API. .
Existem muitos softwares bons. Vamos trabalhar juntos e descobrir como usá-lo com segurança.