Home Artigos Portabilidade de aplicações em Go

Portabilidade de aplicações em Go

2
0
592

Quando pensamos em portabilidade, não pensamos em linguagens compiladas de baixo nível. Linguagens que compilam para linguagem de máquina geram binários que são específicos para cada arquitetura de CPU e sistema operacional. Linguagens interpretadas (Ruby, Python, JavaScript) bem como linguagens que rodam em máquinas virtuais (Java, Scala), por outro lado, são exaltadas por sua portabilidade. Mas, de fato, será que está correto afirmar que apps escritos nessas linguagens são mais portáveis que apps escritos em Go? Antes, vamos definir portabilidade.

“Write once, run anywhere”

Esse é o slogan, criado pela extinta Sun Microsystems, para o Java. Você escreve seu código uma vez, e, a partir daí, você pode executá-lo em qualquer máquina que esteja rodando a Máquina Virtual Java (JVM). Isso só é possível devido ao fato que o código Java não é compilado para código de máquina, que é executado diretamente pela CPU, mas sim em bytecode, que é executado pela JVM. Esse bytecode é o mesmo em qualquer JVM de qualquer sistema operacional, o que o torna portável.

Em termos de portabilidade, Python, Ruby e JavaScript se comportam de maneira similar ao Java. Você escreve código que é executado por um programa (máquina virtual ou interpretador) ao invés de ser executado diretamente pela CPU. E isso torna o código portável pois o programa que executa o código tem suporte a várias plataformas.

Então, por que não utilizar linguagens interpretadas ou baseadas em VM’s? Quais são os contras? Podemos citar duas desvantagens: desempenho e dependências.

Desempenho: quanto mais camadas de abstração existem entre o código e o hardware, mais lentamente o programa irá trabalhar. As linguagens interpretadas e baseadas em VM`s tendem a consumir mais ciclos de CPU do que linguagens que compilam diretamente para código da máquina e, além disso, há menos afinidade entre o software e o hardware que está executando, tornando assim mais difícil escrever código que usa  de forma otimizada o hardware. Mas, por mais importante que o desempenho seja importante, isso não afeta a portabilidade. O que afeta a portabilidade é a segunda desvantagem que mencionamos: dependências.

Ilusão da portabilidade

Podemos definir “dependências” como qualquer coisa que seu programa precise que esteja em seu ambiente de execução. Isso pode incluir:

  • O sistema operacional no qual o aplicativo é executado
  • A VM ou intérprete que executa o código
  • Pacotes que são importados no código

No primeiro aspecto, linguagens como Python, Ruby ou Java parecem ser os mais portáveis entre as linguagens de programação proeminentes hoje em dia: tudo o que você precisa fazer é certificar-se de que o código funciona em uma plataforma, e você deveria tê-lo funcionando em todos as outras, certo? Bem, essa é a teoria pelo menos. Seu código funcionará, desde que não use recursos específicos do SO e – assumindo que todas as suas dependências sejam satisfeitas. É aqui que está o problema.

Dependências também tem dependências

Se você já fez um pip install ou gem install para instalar um pacote e então descobrir que esse pacote depende de outro pacote que atualmente não existe e que não pode ser instalado – você sabe o quanto o gerenciamento de dependências pode ser frustrante. Permissões de arquivos, caminhos de instalação e pacotes conflitantes são causas muito comuns de problemas ao instalar dependências. Fica ainda pior quando um pacote que você está tentando instalar não funciona bem com outro pacote fornecido com seu sistema operacional e que não pode ser mexido – um caso muito comum com instalações Python e Ruby que fazem parte de um Linux ou instalação do MacOS. Além disso, seu código geralmente depende da versão do interpretador ou máquina virtual que o executa.

Todo o descrito acima se aplica não apenas ao seu código, mas aos pacotes nos quais seu código depende. Esses pacotes são simplesmente código que outra pessoa já escreveu – possivelmente você. Esse código também poderia depender de outros pacotes e poderia exigir uma versão de tempo de execução específica para funcionar corretamente, assim como o código da sua aplicação. Percebendo isso, podemos ver que as dependências podem ter suas próprias dependências. Isso significa que um problema com um pacote em algum lugar na hierarquia de dependências é suficiente para tornar seu código completamente inutilizável em um determinado ambiente.

Quem é responsável pelo gerenciamento de dependências?

Claro, o código escrito em linguagens compiladas, como C ou Go, também tem dependências. No entanto, estes geralmente são tratados em tempo de compilação e não em tempo de execução. Uma vez compilado o programa, o binário resultante simplesmente funcionará na plataforma alvo. As linguagens interpretadas, por outro lado, tendem a colocar o gerenciamento de dependências nas mãos do usuário, o que aumenta consideravelmente a chance de algo dar errado e complica o processo de implantação na plataforma destino.

Assim, podemos afirmar que, em termos de portabilidade, é melhor resolver todos os problemas de dependência em tempo de construção e dar aos usuários binários prontos para usar, que eles simplesmente podem executar. No entanto, isso exige escrever nosso aplicativo em uma linguagem compilada e, além disso, precisamos compilar nosso aplicativo para cada arquitetura de CPU e cada sistema operacional que queremos dar suporte. Então, tornamos a vida dos nossos usuários mais fácil, mas tornamos nossa vida mais difícil. Felizmente, Go faz cross-compiling de forma simples e fácil.

Golang e o cross-compiling

Go é uma linguagem compilada, o que significa que devemos compilar nosso código Go para cada plataforma que queremos dar suporte. No entanto, a ferramenta Go torna o cross-compiling do nosso código tão fácil como definir uma variável antes da compilação. Por exemplo, aqui é como criamos um binário para Windows x86, a partir de uma máquina Linux:

GOOS=windows GOARCH=386 go build -o hello.exe hello.go

Apenas isso. O arquivo resultante hello.exe irá rodar tranquilamente em qualquer máquina Windows com uma arquitetura x86.

E um Mac com uma arquitetura 64-bit? Sem problemas:

GOOS=darwin GOARCH=amd64 go build hello.go

E seu programa funcionará em um Mac.

Como dito, as dependências que se programa tem serão automaticamente incluídas no binário gerado, então tudo o que você precisa fazer é enviá-lo aos usuários.

Conclusão:

Se você quiser escrever um código altamente portável e facilitar a vida dos seus usuários, recomendo escrever seu código em Go. Você terá com um código que pode ser facilmente compilado para várias plataformas, e seus deploys podem tornar-se tão simples como um arquivo único zipado, que após extraído estará pronto pra ser usado.

Escrever aplicativos no Go irá trazer paz e a tranquilidade para seus usuários, uma vez que você não terá que escrever longos guias de instalação, e você pode confiar em que seu aplicativo funcionará na máquina do usuário sem uma lista de ressalvas.

Referência: http://tekjoe.com/2017/06/12/why-gloang-is-great-for-portable-apps/

2 Comentários

  1. Giorgio

    9 de julho de 2017 at 08:07

    Muito show. Parabens

    Responder

    • Arthur Mastropietro

      Arthur Mastropietro

      9 de julho de 2017 at 19:59

      Obrigado!

      Responder

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Veja Também

Validação de CPF e CNPJ em Go

Simples e direto. Funções para validar CPF e CNPJ em Go. …