- Um texto que explica as limitações e problemas da linguagem Go percebidos pelo autor após anos de uso e sua migração para Java
- Apresenta a perspectiva de que a característica de Go ser uma linguagem simples e "entediante" (
boring) pode ser uma desvantagem, não uma vantagem
- Filosofia do Go: a equipe de design do Go no Google enfatizou simplicidade e restrições, mas isso acaba gerando trabalho repetitivo que o usuário precisa resolver por conta própria
1. O fato de Go ser "não divertido" pode ser uma desvantagem
- Argumento de Russ Cox:
- Destaca que o fato de Go ser "entediante" (
boring) é uma vantagem
- Só existe
for para loops, e recursos como filter, map e reduce não são oferecidos por padrão
- A ausência de vários recursos avançados presentes na maioria das outras linguagens é vista como parte da simplicidade
- Opinião de usuários no Reddit:
- A fronteira entre "não ser divertido" e "ser poderoso" é vaga
- Argumentam que a falta de recursos básicos do Go provavelmente acabará sendo corrigida com adições à linguagem
- Dependência de pacotes de terceiros:
- Pacote
samber/lo, muito usado para suprir recursos ausentes:
- Inclui funções essenciais como filter, map e busca
- Tem 18.1k estrelas no GitHub e é usado em mais de 12.6k projetos
- Algumas funções foram adicionadas ao pacote
slices, mas ainda são insuficientes em termos funcionais
- Reclamações do autor:
- Obrigação de escrever loops repetitivos
- Dificuldade de lidar de forma concisa com tarefas como filter e map
- É possível extrair isso para métodos com receiver separados, mas isso prejudica a limpeza do código
- A simplicidade do Go é uma vantagem em muitos casos, mas a falta de recursos básicos de conveniência também pode reduzir a produtividade e a legibilidade do código
2. Atrapalha os princípios de Clean Code
- Problema no tratamento de erros:
- Na maioria dos casos, funções incluem
error como valor de retorno:
- É preciso repetir o padrão
if err != nil o tempo todo
- Ao tentar organizar o código, ele acaba ficando ainda mais complexo
- Mesmo em projetos simples, o código de handlers HTTP cresce para mais de 20 linhas
- Quando a meta original era manter algo em torno de 4 linhas
- A frustração com a abordagem de tratamento de erros chega ao ponto de considerar
panic() com middleware de recuperação
- Recomendação de nomes curtos:
- Recomenda-se usar nomes curtos para variáveis, métodos e funções:
- Nomes como
c e a não deixam claro o que significam
- Ex.:
c pode ser Command, Controller, Argument ou Amendment?
- Nomes mais longos poderiam deixar isso mais claro, mas a filosofia do Go prefere nomes curtos
- Isso acaba gerando discussões intermináveis em code reviews da equipe, inclusive sobre nomes de métodos de teste
- A filosofia do Go enfatiza concisão e simplicidade, mas o resultado pode ser complexidade e ineficiência contrárias aos princípios de código limpo
3. A filosofia de linguagem intencionalmente pequena e a cultura DIY
- Falta de funcionalidades básicas:
- Implementar um handler HTTP simples é fácil, mas se você precisar de middleware básico (ex.: exponential backoff, configurações cross-site etc.), terá de procurar vários pacotes
- É difícil ter certeza de que esses pacotes (1) ainda são mantidos e (2) funcionam como esperado
- Aumento de trabalho repetitivo:
- A filosofia de design do Go de manter a simplicidade acaba exigindo do desenvolvedor "reinventar a roda"
- Ex.: até um recurso simples de filter pode precisar ser implementado manualmente
- Ecossistema de pacotes imaturo:
- Muitos projetos no GitHub estão abandonados ou tiveram poucas versões lançadas
- Por ser uma linguagem relativamente jovem, pode ser injusto compará-la a .NET/Java, mas na prática faltam estabilidade e maturidade ao ecossistema de pacotes do Go
- Limitações dos ORMs:
- O principal pacote ORM do Go (
Gorm) fica atrás de Hibernate e Entity Framework em recursos
- Há comportamentos estranhos e falta de documentação
- Reação da comunidade Go: "Em Go você não precisa de ORM, faça você mesmo!"
- A simplicidade do Go pode ser uma vantagem dependendo do projeto e da equipe, mas a falta de recursos nativos pode afetar negativamente a produtividade e a experiência de desenvolvimento
4. No Go, não existe só uma forma de fazer as coisas
- O equívoco sobre consistência e uniformidade:
- Table tests
- Uso de suítes de teste como
stretchr/testify (utilizado em 557k projetos)
- Escrita de subtests customizados dentro de table tests
- Isso mostra uma distância entre a filosofia do Go de haver uma "forma unificada" e a realidade
- Gera conflitos dentro da equipe:
- Aumentam as discussões entre equipes sobre estilo de testes e formas de implementação
- A própria filosofia do Go e sua equipe de design carecem de consistência:
- Ex.: divergências sobre nomenclatura de métodos getter
- Recusa de recursos e dependência de pacotes:
- A equipe do Go se recusa a adicionar recursos de assertion e é criticada por tratar a limitação como falha do programador
- Como resultado, é preciso instalar outro pacote (
go get) para usar funcionalidades necessárias
- Embora Go busque simplicidade e uniformidade, na prática existem várias formas de implementar as coisas e debates decorrentes disso, e a ambiguidade da filosofia de design da linguagem agrava o problema
5. Debugar em Go não é divertido
- Não é possível avaliar expressões durante o debug:
- Em uma sessão de depuração, não dá para avaliar expressões nem verificar representações de string customizadas de objetos
- Isso dificulta entender claramente o estado dos objetos em tempo de execução
- Stack traces e logs pouco intuitivos:
- Quando testes em grande escala falham (ex.: milhares de testes rodando em CI), os stack traces e logs gerados são confusos
- Como resultado, o debug fica mais difícil e a produtividade cai
- Experiência de debug no estilo C:
- A cadeia de ferramentas de debug do Go funciona com base em C:
- Oferece uma experiência de depuração primitiva, parecida com a de C
- Pouco amigável para desenvolvedores
- Comparação com Rust:
- Rust melhora limitações do Go:
- Fornece informações de erro claras e úteis
- Inclui sugestões exatas de correção nas mensagens de erro
- A experiência de debug em Go se baseia numa filosofia de design voltada a entregar binários otimizados, mas isso sacrifica a experiência do desenvolvedor. Em ambientes onde eficiência de depuração é importante, pode ser melhor considerar outras linguagens
Resumo: adequação e limitações do Go
- Vantagens das ferramentas nativas do Go:
- Fornece uma toolchain básica para gerenciamento de pacotes, testes e monitoramento de desempenho
- Pode ser usada sem configuração adicional, simplificando a preparação inicial do ambiente de desenvolvimento
- Limitações:
- "Código entediante" e trabalho repetitivo:
- A toolchain do Go é funcional, mas força trabalho repetitivo (
plumbing code) ao escrever código
- Ex.: sintaxe monótona e recursos limitados reduzem o interesse no trabalho
- "import cycle not allowed":
- Não permite dependências circulares (
import cycle) em testes
- Em trabalhos com Domain-Driven Design (DDD), isso aumenta a complexidade por causa de restrições estruturais
- Dependência da técnica de embedding de
struct:
- O mecanismo de embedding de
struct é estranho e limitado, causando dificuldade prática de uso
- Áreas de uso adequadas:
- Adequado para desenvolvimento de infraestrutura:
- Ferramentas em nível de sistema como Docker, Drone e Hugo são escritas em Go
- Útil para desenvolver servidores leves e aplicações CLI
- Áreas de uso inadequadas:
- Desenvolvimento de aplicações corporativas complexas (ex.: sistemas ERP):
- A filosofia limitada da linguagem e suas ferramentas tornam ineficiente gerenciar lógica de negócio em larga escala
- Go oferece excelente eficiência em certos tipos de trabalho, especialmente os ligados à infraestrutura, mas não é uma ferramenta adequada para aplicações com domínios de negócio complexos. Mesmo quando o CTO é inclinado à stack do Google, é preciso cautela na escolha tecnológica
5 comentários
Se ao menos tivesse o
?do Rust, já não seria muito melhor do que é agora...A sensação que tive ao usar Go foi perceber o quanto, até então, eu fazia tratamento de erros de forma implícita.
Claro, tratar erros em um único ponto pode parecer estruturalmente mais limpo, mas tenho a impressão de que, ao deixar explícito que se trata de uma operação que pode retornar erro, acabamos escrevendo código de forma mais segura.
if err != nil {}isso sinceramente é meio incômodo. Também concordo com as desvantagens apontadas. Mesmo assim, se entendermos claramente o que essa linguagem se propõe a ser e pensarmos em que pontos aproveitá-la melhor, acho que dá para usá-la ainda melhor apesar dessas críticas. É parecida com C, mas tem GC, suporta genéricos, ainda que de forma limitada, e além disso faz cross-compilation! Vendo por esse lado, não dá para dizer que é uma linguagem bem generosa?Quando migrei de Java para Go, acho que no começo tive uma sensação parecida.
Hoje gosto tanto de Go que chego a pensar que foi um desperdício o tempo que usei Java. Dizer que não é adequado para aplicações de negócio complexas me faz pensar que não houve reflexão suficiente sobre como simplificar o sistema nessa aplicação.
Opinião do Hacker News
Problemas surgem quando desenvolvedores Java tentam impor um estilo Java ao Go
Muitos desenvolvedores tentam abstrair cedo demais
A biblioteca padrão do Go é grande, mas não tão grande a ponto de fazer tudo
Existem desafios maiores do que a escolha da linguagem de programação
É difícil entender por que gostam de Go
É frustrante ver a equipe principal do Go voltar atrás em decisões erradas
Go tem problemas parecidos com os do UNIX