GoLang — Simplificando a complexidade

Evandro F. Souza
Training Center
Published in
9 min readApr 8, 2018
Esse bichinho ai não é uma Capivara! É um Gopher na vida real, o mascote de Go. Créditos: http://fav.me/d3dkb3l

Olá, este post faz parte de um série de três partes:

Parte 1: GoLang — Simplificando a complexidade (você está aqui)

Parte 2: Goroutines e go channels

Parte 3: GoLang e Docker

Desde 2000 até o ano atual, surgiram inúmeras linguagens novas — como é mostrado na imagem abaixo — e a cada novidade vêm a onda de hype. A maioria dos desenvolvedores gosta de aprender — e brincar com — uma linguagem nova. Eu faço parte deste grupo — apesar de não ser um early adopter -, eu gosto muito de conhecer, comparar diferentes linguagens e formar a minha opinião sobre cada tecnologia.

Figura 1 — As mais de notáveis, desde 2000

Este post será dividido em duas partes. O objetivo da primeira parte é conhecer a linguagem de programação criada pelo Google, o famigerado GoLang — ou só Go. Na segunda parte será um tutorial mostrando como utilizar GoLang com Docker.

Não existe bala de prata

Uma das coisas que eu acho errado é pensar que determinada linguagem ou tecnologia será a solução de todos problemas. Cada tecnologia possui seus pontos fracos e fortes e é de responsabilidade do desenvolvedor conhecê-los antes de utilizá-la. Para isto, neste post eu pretendo apresentar o Go respondendo três perguntas — que eu sempre procuro responder quando estou conhecendo uma nova linguagem. São elas:

O que faz o Go ser único?

Se fossemos resumir Go em duas palavras seriam: simplicidade e performance. A razão desta linguagem ser criada é que os autores queriam uma linguagem que fosse fácil de programar e com alta capacidade de performance. Diferente de C e C++ que possuem performance porém complexidade de desenvolvimento.

Para manter a simplicidade, a linguagem adotou algumas particularidades, são elas:

Inferência de tipos

O Go tenta combinar a facilidade de uma linguagem de programação interpretada e dinamicamente tipada — por exemplo Javascript, Python — com a eficiência e segurança de uma linguagem de programação estaticamente tipada — por exemplo C e C++. É possível observar isso em alguns aspectos, como por exemplo a inferência de tipos, conforme é visualizado na Figura 2.

Figura 2 — Inferência de tipos

Neste exemplo, ambos formatos de declaração de variáveis resultará nos mesmos tipos. Observando a imagem é possível notar que no primeiro formato os tipos estão sendo definidos explicitamente. Já no segundo formato, o valor é atribuído diretamente, sem a necessidade da especificação do tipo (muito semelhante às linguagens dinamicamente tipadas).

Garbage collector com baixa latência

O Garbage collector é um recurso já presente em muitas linguagens e que vem para facilitar a vida do desenvolvedor, gerenciando a memória da aplicação. Mas normalmente as linguagens com Garbage collection possuem certas desvantagens, entre elas está o consumo de recursos adicionais, impactos de desempenho e possíveis paralisações na execução do programa.

Isso ocorre devido ao processo do garbage collector ser apartado das linguagens. Por outro lado, com o objetivo de priorizar a performance, o Garbage collector do Go está embutido no core da linguagem, isso significa que está otimizado para os recursos da linguagem. Tornando o gerenciamento de memória de Go muito mais rápido que o das demais linguagens.

Go é rápido

Ele é realmente bem rápido. Como o Go é compilado para o código de máquina, ele naturalmente superará as linguagens que são interpretadas ou rodam em máquinas virtuais. Isso é bem perceptível quando analisamos alguns benchmarks. O site BenchMarksGame demonstra isto rodando vários algoritmos e comparando a performance entre as linguagens, abaixo dois exemplos:

Programação concorrente é fácil

Embora normalmente desenvolver software com programação concorrente nunca é de fato simples, o Go torna isso mais fácil do que em outras linguagens. A criação de uma thread — também chamada de goroutine — é praticamente trivial no dia a dia do desenvolvedor. Na Figura 3 é possível verificar como é feita a chamada uma goroutine para a função f. No exemplo, declaramos a função f, na função main existe duas chamadas para f(). A primeira chamada é sem thread e a segunda é com thread. Note que a unica diferença entre as duas chamadas é o uso da keyword go.

Figura 3 — Utilizando goroutines

Biblioteca padrão

A biblioteca padrão do Go é simples e rica em funcionalidades. Ela fornece funções úteis para trabalhar com tipos primitivos. Existem pacotes que facilitam a criação de um web server, lidar com I/O , trabalhar com criptografia e manipular bytes. Possui também serialização e desserialização de JSON, é possível utilizar tags.

Tem orientação a objetos? Sim e não

Embora seja possível aplicar a maioria dos conceitos de orientação a objetos, muitos dos conceitos conhecidos ou estão diferentes ou inexistem em Go. Vamos dar uma olhada em alguns deles:

Structs

Em Go, não existe classes, porém tem Structs(tipos definidos pelos usuários). Um Struct(com métodos) têm finalidades semelhantes às classes em outras linguagens.

Figura 4 — Struct as “classes” do Go

Methods

No Go, o métodos são funções que atuam em tipos específicos. Eles têm uma cláusula receptora que ordena em que tipo eles atuam. A Figura 5 mostra o método Reveal() que atua na struct de Hero.

Figura 5 — Methods em Go são functions.

Interfaces

São a marca do suporte a orientação a objetos do Go. As Interfaces declaram um conjunto de métodos. Elas também não possuem implementação, semelhante as interfaces de outras linguagens. Os objetos que implementam todos os métodos da interface, automaticamente implementam a interface. Sendo assim, não existe herança ou subclasse em Go, para ter um comportamento semelhante, é utilizado Composition. Na Figura 6 é possível verificar como fica o código. No trecho de código, o tipo Foo implementa a interface Fooer (por convenção, os nomes de interfaces terminam com “er”)

Figura 6 — Interfaces

Funções são cidadãs de primeira classe

Como vimos até agora, o Go possui o conceito de funções. E ocorre que elas são cidadãs de primeira classe. Em palavras simples, isso significa que podemos passar funções por parâmetros, retornar uma função de outra função, armazenar funções em variáveis e etc. Apesar de não ser explícito no FAQ da linguagem, isso significa que é possível utilizar conceitos do paradigma funcional para deixar o seu código mais simples.

Quais são os obstáculos em Go?

Como foi possível perceber até agora, o Go é uma boa linguagem. Tem uma sintaxe limpa. Tem tempos de execução rápidos. Há muitas coisas boas sobre ela. No entanto, uma linguagem de programação é mais do que sua sintaxe. Agora vamos analisar algumas coisas que a comunidade está percebendo como obstáculos.

Não há Generics

Vamos iniciar com um dos problemas mais citados no stackoverflow e posts em geral. O Go não possui suporte a Generics. Este é um grande obstáculo para superar para aqueles que vêm de linguagens como C Sharp e Java. Pois isso resulta em uma menor capacidade de reutilizar o código. Embora o Go tenha funções de primeira classe, se escrevermos funções comuns como map, reduce e filter que funcionam para determinado tipo, não será possivel reutilizar as mesmas funções para outro tipos. Há maneiras de resolver isso, mas todas elas envolvem escrever mais código. Isso prejudica a produtividade e a facilidade de manutenção do código.

Interfaces são implicitas

Embora ter interfaces é ótimo, as Struct implementam as interfaces implicitamente, não é explícito. Isso é referenciado como uma vantagem na linguagem, porém alguns desenvolvedores argumentar que somente olhando para um Struct é difícil identificar se o mesmo implementa ou não uma interface. Pessoalmente eu também acho: melhor explícito que implícito.

Pouco suporte de bibliotecas de terceiros

Muito possivelmente isso ocorre por ser uma linguagem relativamente nova — ou não, já são 10 anos de linguagem. A questão é: comparando com outras linguagens, o Go não possui um amplo suporte de bibliotecas oficiais de terceiros. Por exemplo a biblioteca oficial do ElasticSearch está “in progress” no momento que escrevo este post, sendo assim quem quiser se arriscar terá de utilizar de terceiros. Uma das bibliotecas mais famosas de MongoDB do GiHub foi abandonada por seus criadores. Até onde é possível perceber, as bibliotecas oficiais de terceiros para Go não recebem o tanto de atenção que as demais linguagens.

Gerenciamento de dependências falho

Durante muito tempo, o Go não teve um gerenciador de dependências oficial e estável. Após alguns anos de solicitação da comunidade, o projeto Go lançou o godep e recentemente arquivou e lançou outra opção, o dep no qual comentam ser experimental e ainda não ser a ferramenta oficial. É possível notar que este assunto ainda é um problema e pode ser algo bem confuso, principalmente para os novos desenvolvedores. Além disso, praticamente todo o gerenciamento de dependências é apoiado por repositórios Git, cujo o conteúdo pode mudar a qualquer momento. Compare isto com o npm(NodeJs) ou pip(Python) que possuem uma central de pacotes versionados que nunca são excluídos, permitindo você vincular seu projeto a versões estáveis. Na minha opinião, ter um gerenciador de dependências que garanta o versionamento dos pacotes é crucial para garantir estabilidade dos sistemas que são desenvolvidos.

E agora, quando devo usar Go?

Algumas vezes, é necessário preocupar-se com o hardware. Nos casos que é necessário manipular bytes. Ou também quando há a necessidade de gerenciar milhares de threads concomitantes. Imagine que você precisa escrever um sistema operacional, um sistema de containers ou um nó de Blockchain. Nessas situações, há uma boa chance de você não se importar com o Generics. Pois você estará muito ocupado espremendo cada nanossegundo de desempenho do seu hardware.

Por outro lado, as vezes é necessário pensar no mundo real. É necessário trabalhar com dados e domínios de negócio: clientes, funcionários, produtos, pedidos, imóveis. Nestes casos, a responsabilidade do desenvolvedor será escrever um código claro — que reflita da melhor maneira a regra de negócio — e que possivelmente será mantido — muitas vezes por outros desenvolvedores — por anos. A regra de negócio muda rápido e a todo momento. Por isso, seu código precisará mudar na mesma velocidade. Neste casos, quanto mais recursos sua linguagem oferecer, melhor será.

O Go é uma linguagem de programação que valoriza a performance de hardware, mais que a flexibilidade para mudanças. Os criadores perceberam que para ter uma linguagem realmente rápida, algumas funcionalidades deveriam ser deixadas de lado.

Agora, finalmente respondendo a pergunta:

  • Quando devo utilizar? Quando o seu domínio for a máquina ou o desempenho do seu programa for crítico. Nessas situações, o Go pode ser uma ótima escolha.
  • Quando — talvez — não devo utilizar? Quando estamos escrevendo aquele aplicativo típico, com n camadas. Nestes casos os gargalo de desempenho geralmente aparece no banco de dados. Então os benefícios que o Go traz muito provavelmente serão ofuscados pelos pontos negativos já citados no artigo.

Talvez um dia tudo isso mude. A linguagem e a comunidade Go ainda são relativamente jovens. Eles poderiam nos surpreender e adicionar Generics, ou um framework web popular poderia emergir e fazer muito sucesso. Até lá, é bom não cair no impulso do hype e pensar em Go como a solução de performance. Precisa de performance? Go pode te ajudar!

No próximo post será um tutorial de GoLang com Docker!

Se quiser trocar uma ideia ou entrar em contato comigo, pode me achar no Twitter (@e_ferreirasouza) ou Linkedin.

Grande abraço e até a próxima!

--

--