Taverna /dev/All

Cuidado com as suas dependências - pensando sobre o que inserimos em nossos projetos

image

Reuso de código é algo muito difícil de ser obtido mas que temos hoje graças aos mecanismos de gestão de dependência tais como Maven, NPM, NuGet, RubyGems, etc.

E usamos estas bibliotecas sem pensar muito nas suas consequências: então ontem, durante o almoço, iniciamos uma discussão a respeito destas questões.

  • O custo de termos dependências em nossos projetos - especialmente as dependências indiretas, que muitas vezes não são levadas em consideração.
  • A possibilidade de haver conflitos entre diferentes versões de dependências em nossos projetos.
  • Questões envolvendo segurança: você pode “acidentalmente” baixar um pacote batizado e cair em uma série de golpes.
  • O fato de poder estar inserindo bugs em seu projeto proveniente destas dependências.

E realmente, se formos parar pra pensar, notamos um comportamento bastante curioso em praticamente toda equipe de desenvolvimento. Enquanto pensamos um milhão de vezes antes de contratar um programador (checamos seu currículo, aplicamos testes, o entrevistamos, etc), não o fazemos para as bibliotecas que incluímos normalmente em nossos projetos.

Fato é que você não tem certeza a respeito da qualidade do código que está nas suas dependências: nós esperamos que ele esteja ok, mas é raro você realizar uma análise profunda nestas dependências.

image

Acessando o site da ACM Queue vi que a matéria de capa da última edição é justamente este assunto: “Surviving Software Dependencies”, de Russ Cox. Infelizmente não tenho uma assinatura ativa da revista, mas no blog do autor encontrei um texto que com certeza é bastante parecido com este e que vocês podem ler gratuitamente neste link: https://research.swtch.com/deps

É uma leitura fascinante, por que além de mostrar os riscos da inclusão de dependências em nossos projetos, também descreve diversas estratégias que nós podemos tomar em nossos sistemas para minimizar estes riscos.

Excelente leitura. Gostaria de saber a opinião de vocês a respeito.

Eu entendo a dependência como um código que não é meu, que eu não tenho controle, mas que é de responsabilidade minha.

A dependência pode ajudar muito mas também pode ser como um legacy code que você tem que manter, caso o projeto pare de ter contribuições.

Pode ser também um peso no código, tanto em termos de tamanho (dependências internas e quantidade de código), como também em velocidade.

E, talvez o pior dos problemas, é saber se essa dependência não te traz uma brecha de segurança. É quase impossível ter certeza.

1 Curtida

Sobre brechas de segurança há algo que pode ser feito: algumas soluções, como o Nexus, te possibilitam verificar alguns pontos, te fornece o ferramental pra isto.

Oferece inclusive validação de licenças, o que é vital e pode evitar muita dor de cabeça.

Algo que recomendamos a todos os nossos clientes é que tenham o seu próprio gestor de dependências, como o Nexus, Artifactory, etc. Isto permite ter um conjunto de dependências curadas e reduz bastante o risco.

Eu torço o nariz para a ideia de instalar uma lib para cada situação que aparecer. Alguns ambientes como Ruby e NPM levam isso às raias do absurdo, tem umas coisas com quase zero linhas de código - custava codar aquilo na raça, usando a biblioteca padrão ou libs bem consagradas?
Às vezes tem aquele conflito de estar reinventando a roda mas, se não existe uma roda reconhecida, prefiro a feita em casa mesmo.
Eu costumo brincar que o “problema da parada” tem solução sim, alguém já deve ter posto uma gema Ruby no Github.

Com certeza. Há formas de mitigar. Mas há sempre o risco né?

No meu cliente, como era uma startup, quem começou o código saiu usando tudo é quanto gem, mas fazia sentido pois o importante era ter o produto pronto rapidamente.

Hoje lutamos para tirar essas gems e tentamos adicionar o mínimo possível, sempre pesando o custo e benefício. Já removemos mais do que adicionamos.

Existem outras técnicas também para se livrar disso. Uma delas é pegar o código da gem e replicar, caso a licença permita. As vezes é bem pequeno e não vai mudar tanto com o tempo, o que não justifica uma dependência, mas sim ter esse código na nossa base.

Já o npm tem criado estratégias para lidar com isso. Porém, o problema deles é bem grande porque 1 pacote pode incluir centenas (e ter coisas extremamente bobas como isPositive, isNumber, notNumber, etc) e geralmente usamos dezenas dos primários… Aí vc tem uma árvore com dezenas de nodos e cada nodo tem mais um centena (e talvez cada um desses mais um bocado) e por aí vai.

itexto