Article image
Thiago Poiani
Thiago Poiani08/07/2025 17:20
Compartilhe

Java vs. Python? Esqueça a Guerra e Use os Dois com GraalVM e Programação Poliglota

  • #Java
  • #Python

Se você já participou de discussões entre desenvolvedores, sabe como elas podem rapidamente se transformar em batalhas de linguagens. Java é robusto, tipado, maduro, escalável. Python é expressivo, produtivo, com uma sintaxe limpa e uma comunidade vibrante. Mas será que ainda faz sentido tratar isso como uma escolha?

image

Panorama Atual das Linguagens de Programação

De acordo com a pesquisa da Statista de 2024, tanto Python quanto Java continuam entre as linguagens de programação mais usadas no mundo. Python se destaca por sua simplicidade e domínio em áreas como ciência de dados, automação e scripting. Java, por outro lado, é onipresente em sistemas corporativos, aplicações Android e soluções onde performance e estabilidade são críticas.

Essas estatísticas mostram que as duas linguagens não competem apenas por preferência — elas refletem necessidades diferentes do mercado.

Mas e se não precisássemos escolher um lado?

Interoperabilidade entre Linguagens

É cada vez mais comum que diferentes componentes ou módulos de uma aplicação apresentem necessidades técnicas bastante distintas, como alta performance, flexibilidade na evolução do código, integração com bibliotecas específicas, prototipagem rápida para validação de ideias, entre outras demandas específicas. Essa diversidade de requisitos muitas vezes leva as organizações a adotarem múltiplas linguagens de programação dentro do mesmo ecossistema de software, buscando aproveitar as vantagens e pontos fortes de cada uma delas.

image

Entra em cena: GraalVM

GraalVM é uma máquina virtual criada pela Oracle, que surgiu com o objetivo de repensar a forma como linguagens de programação são executadas — especialmente no ecossistema Java.

image

Ela substitui o compilador padrão da JVM (o HotSpot JIT Compiler) por um compilador moderno baseado em Java, chamado Graal, e uma de suas funcionalidades é o Polyglot Programming, permitindo que os desenvolvedores escrevam aplicações poliglotas que transferem valores de forma transparente entre diferentes linguagens no mesmo runtime, por meio do framework de implementação de linguagens Truffle. Além de Java e outras linguagens JVM (como Kotlin e Scala), o GraalVM pode executar JavaScript, Python, Ruby, R, WebAssembly e até linguagens nativas via LLVM (como C e C++).

Mas o grande diferencial vai além da execução: é a interoperabilidade, pois com GraalVM, você pode instanciar objetos, chamar funções, e compartilhar dados entre linguagens de forma segura e eficiente.

Por que isso importa?

Antes do surgimento do GraalVM, a utilização conjunta de múltiplas linguagens de programação em um mesmo sistema costumava exigir arquiteturas complexas, como a divisão em microserviços independentes, execução de processos separados, troca de mensagens via eventos ou comunicação por chamadas de APIs externas. Essas abordagens, apesar de funcionais, frequentemente traziam consigo diversos desafios operacionais e técnicos, como:

  • a sobrecarga de comunicação entre processos, que podia impactar negativamente a performance geral do sistema, além do aumento da latência causada pela passagem de dados por redes ou barramentos intermediários
  • a complexidade do processo de deploy se tornava maior, já que cada componente precisava ser gerenciado e orquestrado separadamente, muitas vezes com diferentes stacks tecnológicos
  • o diagnóstico de problemas e a realização de debug eram complicados devido à distribuição das responsabilidades em múltiplos processos e linguagens isoladas.

Com a chegada do GraalVM, esse panorama muda. A plataforma possibilita a execução de múltiplas linguagens dentro de um único processo, compartilhando o mesmo espaço de memória e oferecendo acesso direto e transparente entre elas. Isso elimina a necessidade de interfaces complexas e a sobrecarga associada a chamadas externas, sem comprometer a performance da aplicação.

Essa capacidade traz inúmeras vantagens, como redução da latência, simplificação do deployment e maior facilidade para realizar debugging e manutenção, além de permitir que desenvolvedores escolham a linguagem mais adequada para cada tarefa, sem as tradicionais barreiras de integração.

Da Teoria à Prática: Minha Experiência com Programação Poliglota

image

Na empresa onde trabalho atualmente, mantemos uma biblioteca Java que encapsula uma lógica de negócio crítica, amplamente validada e reutilizada em diferentes projetos também baseados em Java. Essa biblioteca concentra regras complexas e sensíveis, que demandam alta confiabilidade e consistência.

Por questões de confidencialidade, não posso entrar em muitos detalhes sobre os aspectos específicos dessa lógica, mas vou explicar, de forma geral, o cenário que enfrentamos.

Em determinado momento, surgiu a necessidade de aproveitar essa mesma lógica em um novo projeto que estava sendo desenvolvido em Python, com características e ecossistemas distintos dos outros projetos Java.

A princípio, a solução que parecia mais natural e direta foi reimplementar toda a biblioteca em Python, para manter a independência do projeto e explorar a flexibilidade que a linguagem oferece. E foi exatamente isso que fizemos inicialmente, dedicando esforços para traduzir as regras de negócio e funcionalidades para o novo ambiente.

Um caminho mais difícil do que parecia

Apesar do sucesso inicial com a reimplementação da biblioteca em Python, rapidamente começaram a surgir desafios significativos que comprometeram a viabilidade da solução no médio e longo prazo.

Um dos principais problemas foi a duplicação da lógica de negócio, que introduziu um custo de manutenção elevado e contínuo. Sempre que uma nova funcionalidade era adicionada ou uma regra de negócio era atualizada na biblioteca Java, era necessário replicar manualmente a alteração na versão Python — o que, inevitavelmente, aumentava o risco de erros e inconsistências.

Manter as duas implementações em perfeita sincronia se mostrou uma tarefa mais complexa do que o previsto. Com o passar do tempo, pequenas divergências de comportamento foram se acumulando, muitas vezes despercebidas até se manifestarem em bugs em produção ou em resultados inesperados durante testes mais elaborados.

Em cenários específicos, especialmente naqueles que envolviam regras complexas ou comportamentos condicionais mais sutis, a versão reescrita em Python apresentava resultados distintos da versão original, o que gerava confusão e aumentava o esforço de debugging. Esses casos se tornaram difíceis de diagnosticar e corrigir, já que exigiam conhecimento profundo tanto da lógica original em Java quanto da forma como ela havia sido interpretada e adaptada para Python.

Ou seja, ao tentar evitar uma dependência entre linguagens — em nome da autonomia ou da simplificação percebida — acabamos, na prática, criando uma nova camada de complexidade. Essa escolha nos afastou de uma fonte única de verdade e introduziu um vetor de inconsistência técnica que afetava diretamente a confiabilidade do sistema.

Ponte entre Python e Java

Buscando uma alternativa mais robusta e sustentável, passamos a adotar o PyJNIus, uma ferramenta que permite acessar e interagir com classes Java diretamente a partir de um código Python, por meio da Java Native Interface (JNI). Essa abordagem se mostrou eficiente e confiável durante um bom período, especialmente em cenários onde a reutilização de lógica previamente validada era essencial.

Vantagens:

  • Permitiu a reutilização direta da biblioteca Java original, eliminando a necessidade de reescrever ou adaptar regras de negócio já existentes.
  • Garantiu um comportamento 100% idêntico ao observado em outros projetos baseados em Java, o que reduziu significativamente o risco de inconsistências lógicas.

Desvantagens:

  • Era necessário realizar a instalação e configuração da JVM dentro do ambiente de execução (um container) do projeto Python, o que adicionava complexidade à etapa de provisionamento de infraestrutura.
  • O uso da Java Native Interface demandava atenção redobrada com aspectos como versionamento de bibliotecas, compatibilidade entre tipos de dados e gerenciamento de memória, exigindo um bom domínio tanto do ecossistema Java quanto do Python.
  • A JNI pode introduzir uma camada adicional de complexidade e overhead de performance, já que as chamadas entre as duas linguagens envolvem marshaling de dados e context switches que impactam a eficiência da aplicação.
  • Apesar de funcional e confiável, essa solução impunha uma dependência rígida da JVM, o que limitava nossa flexibilidade e dificultava o processo de deploy em determinados ambientes mais restritivos ou com requisitos específicos de portabilidade.

Prova de Conceito com Programação Poliglota

Foi nesse ponto da jornada que decidimos explorar uma abordagem mais moderna e promissora utilizando o GraalVM, buscando superar as limitações impostas pelas soluções anteriores.

Desenvolvemos uma prova de conceito (PoC) onde uma aplicação Python interagia diretamente com a biblioteca Java, previamente compilada como native executable utilizando os recursos do GraalVM Native Image. Esse binário encapsulava toda a lógica de negócio escrita em Java, dispensando a necessidade de instalar ou configurar uma JVM no ambiente Python — o que simplificou consideravelmente o processo de deploy e reduziu o consumo de recursos.

A interação entre os dois mundos foi feita com estabilidade, mantendo um excelente desempenho e um comportamento totalmente alinhado com o que já havia sido validado em ambientes puramente Java. A prova de conceito nos permitiu testar a interoperabilidade em cenários reais, simulando integrações com dados dinâmicos, chamadas concorrentes e até casos de erro e exceção.

O resultado foi positivo: conseguimos comprovar na prática que era possível integrar linguagens distintas de forma eficiente, reutilizar código já existente, evitar reimplementações complexas e, ao mesmo tempo, manter a confiabilidade da lógica original. A abordagem não apenas funcionou tecnicamente, como também foi bem recebida pela equipe, que passou a enxergar o uso de GraalVM como uma alternativa viável e estratégica para projetos futuros.

Lições aprendidas

A experiência prática com programação poliglota nos ensinou que interoperabilidade não é apenas sobre rodar múltiplas linguagens em um mesmo sistema — é, acima de tudo, sobre garantir consistência entre os comportamentos, confiabilidade na execução, clareza na comunicação entre os componentes e facilidade de manutenção a longo prazo.

Ao adotar ferramentas como o GraalVM, fomos capazes de construir pontes sólidas entre diferentes tecnologias, permitindo que linguagens com paradigmas distintos coexistam de forma harmônica dentro de um mesmo ambiente. Essa abordagem nos deu a possibilidade de evoluir soluções sem reescrever tudo do zero, além de manter o que já funcionava bem em Java, ao mesmo tempo em que exploramos novas possibilidades e ganhos de produtividade com Python.

Essa união não apenas evitou retrabalho e erros causados por duplicação de código, mas também nos ofereceu uma nova perspectiva sobre como pensar a arquitetura de sistemas modernos: mais integrados, menos fragmentados, onde a escolha da linguagem deixa de ser uma barreira e passa a ser uma decisão estratégica alinhada aos objetivos do negócio e à maturidade técnica da equipe.

Quando unir é melhor do que escolher

image

No início, parecia que precisávamos fazer uma escolha: reimplementar a lógica de negócio em Python ou manter a versão confiável em Java. Cada caminho apresentava vantagens e desafios, mas todos envolviam algum tipo de trade-off — seja em manutenção, desempenho ou consistência.

Foi ao explorar a programação poliglota com GraalVM que encontramos uma alternativa mais equilibrada: não escolher entre as linguagens, mas permitir que elas colaborem.

Com essa abordagem, conseguimos:

  • Eliminar a duplicação de código, mantendo uma única fonte de verdade para regras de negócio complexas;
  • Reduzir riscos causados por divergência de comportamento entre implementações;
  • Aproveitar o melhor de cada linguagem — a robustez do Java e a agilidade do Python;
  • Simplificar o deploy, ao gerar executáveis nativos que dispensam a JVM.

Mais do que uma solução técnica, a programação poliglota se mostrou uma estratégia para manter qualidade, consistência e flexibilidade em projetos com requisitos diversos.

Se sua equipe enfrenta desafios semelhantes — seja integrando sistemas legados, evitando reimplementações ou tentando ganhar eficiência — vale a pena considerar o que o GraalVM tem a oferecer. Às vezes, a melhor escolha é não escolher uma linguagem só.

Curioso? Exemplo prático para quem quer testar

image

Para quem quer colocar a mão na massa e entender como funciona a interoperabilidade entre Java e Python com GraalVM, preparei um exemplo simples que pode servir como ponto de partida.

Nesse experimento, compilei uma classe Java básica e utilizei o GraalPy — a implementação Python da GraalVM — para instanciar essa classe e chamar seus métodos diretamente a partir de um script Python.

Isso significa que, em um único ambiente, você pode escrever lógica robusta em Java e usá-la sem esforço no Python, aproveitando o melhor dos dois mundos.

O código está disponível no meu repositório no GitHub: thpoiani/dio-polyglot-python-java-graalvm

Lá você encontra instruções básicas para compilar e executar o exemplo (ou olhe diretamente a execução do GitHub Actions), e pode usar como base para seus próprios experimentos com programação poliglota usando GraalVM.

Se estiver curioso para entender melhor essa integração, recomendo começar por esse projeto — é uma forma prática e direta de ver o potencial do GraalVM em ação.

Se quiser trocar ideias sobre esse exemplo, sinta-se à vontade para me adicionar no LinkedIn.
Compartilhe
Comentários (1)
Daniel Passos
Daniel Passos - 08/07/2025 17:56

Experiência prática muito interessante, Thiago!

obrigado por compartilhar!