Skip to main content

Boas práticas para gerenciar dependências Java

Escrito por
hero presentation

26 de agosto de 2022

0 minutos de leitura

Criar aplicações Java é ótimo, e há muitos recursos disponíveis. Para acelerar o desenvolvimento, muita gente usa frameworks e bibliotecas que dão conta de boa parte do trabalho. Nas aplicações Java modernas, quase todas incluem dependências de bibliotecas desenvolvidas por terceiros.

As dependências representam cerca de 80% a 90% do binário — por isso, precisamos cuidar bem delas ao criar um projeto Java. Neste artigo, vou compartilhar algumas dicas e boas práticas para lidar com as dependências Java do seu projeto.

Por que prestar mais atenção às suas dependências Java

Quando se trata de gerenciar contribuições de código, geralmente recorremos a um processo como revisões de código para fazer uma primeira verificação de qualidade antes de integrar o novo código à branch principal. Confira nosso guia sobre ferramentas de revisão de código Java para saber mais. A programação em pares é outra maneira de garantir esse controle de qualidade.

No entanto, a maneira como lidamos com as dependências é bem diferente da forma como tratamos nosso próprio código. Muitas vezes, usamos dependências sem nenhum tipo de validação. Além disso, as dependências de nível superior costumam trazer dependências transitivas, que podem se estender por vários níveis. Por exemplo, uma aplicação Spring de 200 linhas com cinco dependências diretas pode acabar usando 60 dependências no total, o que representa quase meio milhão de linhas de código em produção.

Atualizar dependências Java em projetos legados pode ser um desafio. Se estiverem desatualizadas, você pode enfrentar um efeito dominó de problemas de compatibilidade; atualizar uma única biblioteca talvez exija atualizar várias outras por causa de um bug ou problema de segurança. Se essas dependências Java mudarem suas APIs, pode ser necessário reescrever toda a aplicação.

Além disso, em muitas aplicações empresariais de grande porte, as dependências continuam no arquivo de manifesto mesmo quando já não são usadas no código. Essas dependências não utilizadas continuam disponíveis no programa.

Tudo isso pode resultar em:

  • Binários maiores, que consomem mais recursos ou demoram mais para iniciar

  • Possíveis conflitos entre bibliotecas ao adicionar novas dependências.

  • Bibliotecas desatualizadas com bugs ou problemas de segurança

  • Problemas de compatibilidade ao atualizar bibliotecas

  • E muito mais

Gerenciamento de dependências Java

Uma das melhores práticas ao usar repositórios como o Maven Central é configurar seu próprio gerenciador de repositórios. Trata-se de um servidor proxy dedicado entre o ambiente de desenvolvimento interno e os repositórios públicos. Além de proporcionar builds mais rápidos e estáveis, ele permite configurar políticas para pacotes Java. Por exemplo, você pode bloquear determinadas versões para que não sejam baixadas nem usadas nas suas aplicações.

Para saber mais sobre gerenciadores de repositórios e conhecer uma lista de opções, consulte a documentação do Maven.

Incluindo novas dependências no seu projeto Java

Quando você precisa resolver um problema e existe uma biblioteca para isso, provavelmente vai querer incluí-la nos arquivos de manifesto de dependências Java. Mas, antes de fazer isso, considere:

Ela resolve o problema?

O principal motivo para importar um pacote é resolver um problema. A questão é se a dependência escolhida consegue fazer isso. E ela resolve o problema por completo sem criar novos desafios? Se não, talvez existam soluções melhores.

Eu preciso do pacote inteiro?

Vale a pena importar uma dependência grande, com muitas funções e tipos de dados, se você precisa de apenas uma função? Às vezes, pode ser mais simples e prático escrever essa função por conta própria. Por exemplo, faz sentido incluir todo o Eclipse Collections se eu só quiser usar o tipo de dado Tuple? Provavelmente não.

Uma consulta rápida em mvnrepository.com mostra que esse pacote tem cerca de 10 MB

Página do MvnRepository da biblioteca principal Eclipse Collections versão 11.1.0, com licença, tags, arquivos, repositório, classificações e uso.

Confira também se as dependências que você já usa podem resolver o problema. Talvez algumas funções ou tipos de dados semelhantes já estejam disponíveis. Por outro lado, incluir uma biblioteca nova e abrangente pode ajudar a resolver vários problemas de uma só vez — tudo depende da situação.

Quantas pessoas contribuem para o projeto?

O fator de ônibus é baixo quando a dependência Java que você usa tem apenas uma ou poucas pessoas mantenedoras. O que acontece se a pessoa responsável decidir sair ou não tiver tempo para corrigir um bug? Outra possibilidade é você mesmo contribuir com o projeto — tornando-o mais seguro para todas as pessoas envolvidas.

Mas, antes de incluir qualquer dependência no seu projeto, confira o repositório principal e veja quantas pessoas contribuem ativamente para a manutenção.

Cabeçalho do repositório do GitHub mostrando 1.173 commits, 10 branches, 0 pacotes, 40 lançamentos e 66 colaboradores

Ela ainda recebe manutenção?

Se um pacote não recebe mais manutenção, definitivamente não é uma boa ideia depender dele. Antes de integrar um pacote, confira se há novos commits no repositório do GitHub e observe o ciclo de lançamentos. Isso ajuda a entender o quanto ele é bem mantido.

Interface do repositório mostrando um commit de 2 horas atrás, destacado por uma seta rosa, com duas pastas listadas abaixo

Qual é a versão mais recente do pacote?

Exemplos de código podem oferecer ótimas informações sobre uma dependência Java específica. No entanto, esses exemplos podem estar desatualizados, e o pacote em questão talvez já tenha sido atualizado. Considere usar a versão estável mais recente. No caso do Eclipse Collections, o mvnpackage.com indica que a versão estável mais recente é a 11.1.0, lançada em 5 de julho de 2022. Considere usar essa versão.

Observe que a versão 11.1.0.M2 também aparece nesta imagem. Ela é claramente uma versão de pré-lançamento. Em aplicações de produção, inclua apenas versões estáveis, a menos que tenha certeza absoluta. Como regra geral, evite versões com qualificadores como:

  • alpha ou a

  • beta ou b

  • milestone ou m

  • rc ou cr

  • snapshot

Se uma dependência Java tiver o qualificador GA ou final, geralmente você pode considerá-la uma versão estável.

Tabela com versões do Java, número de vulnerabilidades, links para o repositório Central, dados de uso e datas de lançamento.

Há vulnerabilidades de segurança?

Antes de passar a depender de um pacote Java, verifique se ele tem vulnerabilidades conhecidas. A Snyk CLI é uma ótima ferramenta para analisar seu arquivo Maven ou Gradle. Se sua biblioteca tiver uma vulnerabilidade de segurança, talvez seja melhor escolher outro pacote.

Atualize suas dependências Java

Há versões mais recentes disponíveis?

Você não vai querer verificar manualmente cada dependência Java para saber se há versões mais recentes. Felizmente, há maneiras mais fáceis de fazer isso. Com plugins no gerenciador de pacotes, você pode verificar as dependências automaticamente com a frequência que quiser — por exemplo, em cada build.

Lembre-se de que suas ferramentas podem indicar versões beta ou de pré-lançamento. É altamente recomendável usar apenas versões estáveis de uma biblioteca.

Exemplo com Maven

No Maven, você pode usar o plugin versions, como no exemplo abaixo. Não é necessário incluir nenhuma configuração específica no seu pom.xml.

mvn versions:display-dependency-updates
Saída do terminal do Maven Versions Plugin lista atualizações disponíveis de dependências e mostra que a compilação foi concluída com sucesso.

Exemplo com Gradle

No Gradle, precisamos incluir um plugin, como o versions, de ben-manes.

plugins {
  id "com.github.ben-manes.versions" version "0.42.0"
}

Agora, podemos executar um comando semelhante para exibir versões mais recentes das suas bibliotecas.

gradle dependencyUpdates -Drevision=release
Saída do terminal com o título “Atualizações de dependências do projeto”, listando dependências Java com as versões atuais e as versões posteriores.

IntelliJ IDEA

Se você usa o IntelliJ IDEA, as dependências que podem ser atualizadas serão sublinhadas quando houver versões mais recentes. Isso funciona tanto em projetos Maven quanto Gradle.

XML de dependência Maven do Java exibindo org.eclipse.collections e um aviso do IDE para atualizar eclipse-collections-api da versão 10.3.0 para 11.1.0.

Snyk

Ao conectar seu repositório do GitHub à sua conta Snyk, podemos recomendar correções ou atualizações a cada pull request. Junto com nossas orientações abrangentes de segurança, isso ajuda você a manter suas dependências Java atualizadas.

Pull request do GitHub do Snyk propondo a atualização de org.eclipse.collections:eclipse-collections da versão 10.3.0 para a 11.1.0.

Os pacotes que você usa ainda recebem manutenção?

Vale a pena voltar ao repositório do GitHub ou ao mvnpackage.com para verificar se houve atualizações e commits recentes. Se parecer que um pacote já não recebe manutenção adequada, você pode assumir a manutenção ou migrar para outra biblioteca mais atualizada.

No entanto, se você encontrar um problema em uma dependência essencial para sua aplicação, considere corrigi-lo por conta própria e contribuir com essa correção para o projeto de código aberto. A contribuição será muito apreciada — e, muitas vezes, é mais rápida do que abrir chamados e pressionar a pessoa mantenedora para resolver o problema.

Há problemas de segurança nas minhas dependências Java?

Mesmo que sua aplicação não tenha vulnerabilidades agora, isso não significa que continuará assim para sempre. Novas vulnerabilidades e explorações são descobertas e divulgadas todos os dias. Por isso, você precisa analisar suas bibliotecas regularmente para garantir que continuem livres de vulnerabilidades.

A Snyk oferece várias maneiras de integrar a análise de dependências ao seu ciclo de desenvolvimento. Na sua máquina local, você pode usar a Snyk CLI ou as integrações com IntelliJ, Eclipse ou VS Code para buscar vulnerabilidades. Também é possível fazer análises durante o ciclo de build com o plugin para Maven e Gradle (não oficial) ou escolher uma das integrações de pipeline de CI. Outra opção é adicionar seu repositório Git à Snyk para que possamos analisar e atualizar seus projetos diariamente.

Relatório de vulnerabilidades da Snyk mostrando problemas críticos no Apache Struts, incluindo vulnerabilidades de execução arbitrária e remota de código, com pontuações de gravidade.

Removendo dependências Java do seu projeto

O pacote ainda está sendo usado?

Se uma dependência Java não estiver mais em uso, devemos removê-la do arquivo de manifesto. Cada pacote listado nesse arquivo faz parte do seu binário e fica disponível no classpath. Remover dependências não utilizadas deixa o binário menor e acelera a inicialização e os downloads, além de melhorar a segurança. Reduzir ao mínimo as dependências no classpath é essencial para se proteger contra ataques como uma cadeia de gadgets de desserialização.

Manter a mesa organizada — ou, neste caso, a aplicação limpa — é sempre altamente recomendável. Felizmente, os gerenciadores de pacotes podem ajudar você a identificar dependências Java não utilizadas.

Exemplo com Maven

No Maven, posso usar o plugin dependency para analisar minhas dependências. Esse plugin verifica se as dependências Java declaradas também são usadas no código.

Neste caso, não quero incluir dependências com escopo provided ou test na análise, então uso a flag ignoreNonCompile.

mvn dependency:analyze -DignoreNonCompile
Aviso no terminal mostrando dependências declaradas não utilizadas: Apache Commons Lang 3.9 e Apache Struts 2.5.10.

Exemplo com Gradle

No Gradle, precisamos adicionar outro plugin para analisar as dependências. Neste caso, vamos usar o plugin nebula.lint. Esse linter do Gradle analisa as dependências Java incluídas e identifica as que não estão sendo usadas.

plugins {
   id "nebula.lint" version "17.7.0"
}

Preciso configurar o plugin adequadamente para definir gradleLint.rules. Você pode fazer isso no arquivo Gradle ou usar um parâmetro de linha de comando. No exemplo abaixo, escolho a segunda opção. Consulte a documentação do plugin para saber mais sobre como configurá-lo na sua aplicação.

gradle lintGradle -PgradleLint.rules=unused-dependency
Saída do terminal mostrando avisos do linter sobre dependências não utilizadas do Apache Commons Lang e do Struts no build.gradle.

Crie uma estratégia sólida de gerenciamento de dependências para suas aplicações Java

Ao desenvolver aplicações Java e usar dependências como bibliotecas ou frameworks, é recomendável criar uma estratégia para gerenciá-las. Saber como selecionar, atualizar e remover dependências Java da aplicação é essencial para a segurança. Com uma estratégia clara, evitamos surpresas quando um problema de segurança de alta prioridade exige a atualização de um pacote.

Confira este artigo para saber mais sobre como gerenciar dependências de código aberto.

Comece a resolver desafios de capture the flag

Aprenda a resolver desafios de capture the flag assistindo sob demanda ao nosso workshop virtual introdutório.

Leia mais

blog feature snyk container custom base recommendations
Blog

Conheça o Evo Agentic AppSec: segurança que acompanha o ritmo do desenvolvimento com IA

O desenvolvimento impulsionado por IA está avançando mais rápido do que os fluxos de trabalho de segurança tradicionais. O AppSec agentivo da Snyk usa agentes de segurança para encontrar, corrigir e verificar vulnerabilidades continuamente, enquanto as pessoas definem os critérios.

blog feature ai violet
Blog

VulnBump: agentes de IA conseguem corrigir dependências vulneráveis sem quebrar o build?

O VulnBump testa agentes de IA em 307 atualizações reais de dependências. Uma correção só é considerada válida se eliminar a vulnerabilidade e mantiver o build e os testes funcionando.

Blog

Modelos de ponta encontraram as vulnerabilidades. Só o atacante encontrou as cadeias.

A análise estática encontrou as falhas, mas só os testes de ataque em aplicações ativas provaram como elas poderiam ser encadeadas para causar invasões. Uma comparação entre Evo COS, Claude Security e Claude Code Security.