- As respostas recorrentes às críticas ao Go — “grandes empresas também usam”, “também há pontos positivos”, “precisão custa caro” — podem obscurecer como falhas de design da linguagem se acumulam como custo no longo prazo
- O Go tem pontos fortes em runtime assíncrono, GC, ferramentas de gerenciamento de pacotes, refatoração e compilação cruzada, mas isso vem junto com toolchain e sistema de build próprios, convenção de chamadas, GC único e todo o ecossistema
- A ausência de sum types, o controle fraco de mutabilidade, o zero value e os comportamentos diferentes de slice·map·channel
nilimpedem que o compilador garanta muitas invariantes - Sem cgo, é difícil integrar com outras linguagens e ferramentas; com cgo, surgem custos com GC, regras de ponteiros, inclusão do runtime e bibliotecas estáticas grandes, então a fronteira de rede acaba sendo o ponto de integração mais realista
- A expectativa de “fazer só o protótipo em Go e reescrever depois” na prática pode aumentar o custo operacional por causa da evasão da reescrita, do acoplamento ao código Go existente e da carga de linters, documentação e verificação manual
Lógicas de defesa que se repetem nas críticas ao Go
- Desde o texto de 2020 I want off Mr Golang’s Wild Ride, as reações em torno das críticas ao Go continuam se repetindo
- A reação de que o caso do Windows não é uma área em que o Go seja bom
- A reação de que não se fala dos pontos bons do Go
- A reação de que não se entendem os compromissos que o Go escolheu
- A reação de que, se grandes empresas usam Go, ele não pode ser tão ruim
- A reação de que o custo de modelar o problema “corretamente” é alto, então discutir precisão não faz sentido
- A reação de que, como Rust também tem defeitos, a crítica ao Go seria inválida
- O ponto central não é se o Go tem vantagens, mas se as lógicas de defesa repetidas para continuar usando Go escondem os custos reais
A forma de atacar primeiro a qualificação de quem critica
- Quando surge um feedback que não se quer ouvir, assumir que quem falou é incompetente ou tem uma motivação oculta permite evitar examinar o conteúdo
- Detectar problemas não exige necessariamente alta especialização, e desenvolvedores sêniores podem acabar ignorando desconfortos aos quais já se acostumaram
- Desenvolvedores juniores, por ainda não terem se acostumado ao que parece estranho, podem sentir o incômodo mais rápido e fazer perguntas
- O valor do feedback deve ser avaliado não por quem falou, mas por haver de fato um problema ou não
Os limites do argumento “outras empresas também usam”
- Em escolhas tecnológicas, observar o que outras empresas usam é útil para descoberta, mas não basta como fundamento para adoção
- Blogs técnicos corporativos normalmente têm dificuldade para expor por completo o custo da adoção
- Muitas vezes são escritos depois que a decisão já foi tomada
- Têm o objetivo de melhorar a imagem da empresa e ajudar no recrutamento
- Críticas técnicas mais fortes costumam vir de indivíduos, não da empresa
- O uso de Go pela Tailscale está mais próximo de um caso em que uma equipe especialista em Go conhece e consegue absorver os custos
- netaddr.IP: a new IP address type for Go expõe restrições do Go no processo de representar endereços IPv4 ou IPv6
- Hey linker, can you spare a meg? trata de problemas que exigem conhecimento profundo do linker do Go
- A dor revelada em
netaddr.IPestá ligada a várias restrições do Go ao mesmo tempo- O Go não tem sum types, então é estranho representar um tipo “IPv4 ou IPv6”
- Slice é usado como estrutura de dados genérica, com custo de 24 bytes em máquinas 64-bit
- A ausência de sobrecarga de operadores cria distinções como
a == bea.equals(b) - Não há suporte a dados imutáveis, então é preciso passar cópias e tomar cuidado com mudanças internas
- É difícil criar
newtypeopaco, exigindo pacote separado e contornos com interfaces
- Equipes como a da Tailscale conseguem lidar profundamente com o funcionamento interno do Go e casos de borda, mas nem toda organização tem especialistas em Go e orçamento de engenharia como Google ou Tailscale
O que o Go realmente faz bem
- O Go oferece um runtime assíncrono bastante bom, padrões fortes por opinião, um GC moderno com dois knobs de ajuste e uma boa toolchain
- Como o runtime assíncrono está no centro da linguagem, há ferramentas que desenvolvedores Rust podem até invejar
- É fácil despejar o backtrace de todas as goroutines em execução
- Há detecção de deadlock
- Existe profiler próprio
- A linguagem faz você pensar menos em function coloring
- Ferramentas de gerenciamento de pacotes, refatoração e compilação cruzada parecem, num primeiro momento, uma melhora em relação a
pkg-config, autotools e CMake do ecossistema C - Mas não se adota apenas o runtime conveniente do Go
- Toolchain própria
- Sistema de build
- Convenção de chamadas
- GC único
- Componentes padrão incluídos e ecossistema
- Até decisões acidentais de design da linguagem vêm no pacote
Lacunas no sistema de tipos e no design da linguagem
- O Go coloca no centro um runtime voltado à implementação de TCP, HTTP, TLS, HTTP/2, DNS e serviços web, assumindo uma forma familiar para desenvolvedores do Google acostumados a Java/C/C++/Python
- Como em C, o tratamento de erro em si não recebe grande elaboração, e muito estado mutável precisa ser gerenciado manualmente com
ifeelse - Como em Java, a distinção entre valor e referência fica borrada, tornando difícil saber pelo ponto de chamada se um valor será alterado
- Dependendo de o receiver do método ser
Aou*A, a variável localaemmainpode ser alterada ou não
- Dependendo de o receiver do método ser
- Como é difícil restringir com clareza mutabilidade e imutabilidade por tipo, ao passar uma referência há risco de o valor interno mudar ou de ele ser retido por muito tempo e não ser liberado
- O Go não consegue prevenir bem os seguintes grupos de erro
- Copiar um mutex por engano e neutralizá-lo
- Deixar de inicializar campos de uma struct e acabar com zero value
- Propagar
nil, zero ou string vazia incorretos para partes profundas do sistema
Zero value e os limites de “basta tomar cuidado”
- Mesmo que adicionar um novo campo a uma struct mude na prática a forma de entrada de uma função, o compilador não bloqueia a omissão desse novo campo em composite literals
- No exemplo, foi adicionado
b int32aParams, mas se o chamador passar sóa: 47, a execução acontece comb=0 - Em contraste, se o argumento fosse adicionado diretamente à função, haveria erro de compilação por número insuficiente de argumentos
- No exemplo, foi adicionado
- Um dos princípios do Go é que o zero value seja útil, mas o significado real muda conforme o tipo
- Chamar
lenem um slicenilretorna 0, então ele se comporta como slice vazio - Inserir valor em um map
nilgerapanic: assignment to entry in nil map
- Chamar
- Channel também tem regras próprias
- Enviar para um channel
nilbloqueia para sempre - Receber de um channel
nilbloqueia para sempre - Enviar para um channel fechado causa panic
- Receber de um channel fechado retorna imediatamente o zero value
- Enviar para um channel
- Como o Go não tem sum types, é difícil expressar no tipo entradas do tipo “permitir apenas uma entre opções conhecidas”, ficando mais próximo de C e da lógica de “é só tomar cuidado”
- A atitude de que, como nem todos os problemas podem ser evitados, então não faz sentido evitar alguns, é uma falsa dicotomia que empurra até problemas que o sistema de tipos poderia ajudar a resolver para o campo da cautela manual
A comparação com Rust não é uma discussão sobre “substituto perfeito”
- A posição não é que Rust seja perfeito, e Go e Rust podem ser vistos como casos de sucesso por caminhos diferentes
- O sucesso do Go depende bastante de componentes incluídos e padrões opinativos
- O sucesso do Rust depende bastante de adoção gradual e facilidade de integração com outras linguagens
- Casos reais de adoção de Rust estão mais próximos de migração gradual do que de substituição total
- O Firefox inclui vários componentes centrais em Rust dentro de uma grande base C++
- O Android reimplementou a stack de Bluetooth em Rust
- Código criptográfico em Rust entrou no Python, e código HTTP em Rust entrou como um dos vários backends do curl
- O patch de Rust para o kernel Linux vem sendo melhorado repetidamente
- Usuários de Rust também falam ativamente sobre tempos de build, falta de recursos na linguagem e outros defeitos
- O ponto não é escolher obrigatoriamente entre Go e Rust, mas entender que tipo de problemas ferramentas para expressar mutabilidade, lifetime e abstração no tipo ajudam a reduzir
- Mesmo em Rust, nem todo caso precisa ser modelado de forma rigorosa
- Por exemplo, se não for uma ferramenta de baixo nível como
ls, pode-se usar camino e decidir lidar apenas com paths UTF-8 - No tratamento de erro, pode-se enumerar apenas alguns variants importantes e deixar o restante como
Other,Internal,Unknown - Chamar unwrap() em
Option<T>é uma forma de afirmar explicitamente que há um valor
- Por exemplo, se não for uma ferramenta de baixo nível como
O Go está mais perto de uma ilha separada dos outros ecossistemas
- Sem usar cgo, o Go fica distante de assembly, linker, debugger, verificadores de memória e convenções de chamada mais comuns
- O Go está mais próximo de uma linguagem de mundo fechado do que C/C++, e é mais hostil a FFI do que Node.js, Python e Ruby
- Essa diferença também traz vantagens
- É possível inspecionar com profiler o interior das stacks de TLS e HTTP do mesmo jeito que a lógica de negócio
- Isso contrasta com linguagens dinâmicas, em que o stack trace para no OpenSSL
- O runtime pode cuidar de non-blocking I/O e scheduling
- Mas isso dificulta reaproveitar muitas ferramentas existentes e conhecimento institucional acumulado, além de tornar complicada a integração entre Go e outras linguagens
- Cada forma de integração com Go tem seu custo
- Quando Go chama C, além do custo da fronteira FFI, é preciso rastrear manualmente descriptors para não quebrar o GC
- Quando outra linguagem chama Go, é preciso embutir todo o runtime do Go junto com o GC, o que traz biblioteca estática grande e peso operacional
- A melhor fronteira de contato com Go fica mais próxima de uma fronteira de rede
- RPC over TCP como REST-ish HTTP/1, JSON-RPC e gRPC tende a ser menos doloroso se o custo de latência for aceitável
- Ainda assim, é preciso manter invariantes dos dois lados, e do lado do Go o compilador não garante isso se pacotes de validação não forem usados de forma consistente
A armadilha da expectativa de usar Go para protótipos
- Como Go é fácil de aprender e contratar, existe a ideia de fazer primeiro o protótipo e, quando ficar difícil, reescrever depois ou trazer especialistas
- Mas “código descartável” quase não existe na prática
- Reescrever leva tempo
- É difícil coordenar uma transição suave
- Detalhes podem se perder
- O desenvolvimento de novas funcionalidades para
- É preciso retreinar a equipe para a nova tecnologia
- Quanto mais componentes em Go existem, mais razões aparecem para continuar escolhendo Go a fim de interagir com o código Go já existente
- Linters ajudam em parte, mas não chegam perto do que faz o compilador de uma linguagem que levou esses problemas a sério desde o início, e ainda desaceleram o desenvolvimento
- A complexidade ausente da linguagem se desloca para a base de código
- Invariantes não expressas no tipo precisam ser escritas manualmente no código
- Só pela assinatura da função é difícil saber se dados serão alterados, retidos, se zero value é aceito, se uma goroutine será iniciada, se um channel
nilé possível, ou quais tipos realmente podem ser passados parainterface{} - Passa-se a depender de documentação, e documentação tem tanto custo de atualização quanto custo de desatualização
Por fim, as “mentiras” que se consolidam
- O que se diz a si mesmo para continuar usando Go pode ser resumido assim
- Se outras pessoas usam, então também deve ser bom para nós
- Quem expressa preocupação é elitista ou grosseiro
- O runtime assíncrono atraente e o GC compensam todas as outras desvantagens
- Cada falha de design da linguagem, isoladamente ou em conjunto, é aceitável
- Dá para superar com “mais cuidado”, linters e mais revisores
- Como é fácil de escrever, também é fácil desenvolver software de produção
- Como a linguagem é simples, o restante também é simples
- Vamos usar só um pouco, ou só no começo, e será fácil sair depois
- Sempre poderemos reescrever mais tarde
- Se a ferramenta escolhida não elimina a complexidade, essa complexidade é transferida para outros desenvolvedores, operadores e clientes
- Situações em que um valor
nilentra onde não deveria e faz alguém acordar de madrugada em produção repetem o Billion Dollar Mistake
2 comentários
Fico na dúvida se um amador que passou um período tão breve quanto um instante focado em Go deveria mesmo deixar um texto... mas Go é uma linguagem com vantagens e desvantagens muito claras, então parece haver razões bem definidas tanto para quem a escolhe quanto para quem a evita. Pessoalmente, acho que não faz muito sentido compará-la com Rust; o mais correto me parece compará-la com Kotlin (Java).
As goroutines de Go são realmente excelentes, mas não são magia. Especialmente em projetos pequenos de backend que usam apenas um MySQL, essa tal concorrência é bem complicada de gerenciar. Coisas como esgotamento de recursos do MySQL ou gerenciamento de pool, que em runtimes JS/TS você nem precisa pensar tanto, acabam sendo mais difíceis do que parecem. No fim, nessa situação o banco vira o gargalo, então parte da vantagem da concorrência do Go se perde. (o I/O assíncrono ou o event loop de runtimes JS/TS pode até ser mais adequado) Dá para perceber isso na hora se você jogar algo como
-c 100com uma ferramenta comohey.E embora haja um GC excelente, isso não significa que você possa sair por aí passando objetos só por ponteiro e ignorando completamente a limpeza depois. Tudo tem trade-offs, mas em Go também é melhor, sempre que possível, passar objetos pequenos por cópia de valor e deixar que sejam processados logo quando a função terminar. Talvez eu esteja preso a uma forma de pensar antiga, mas não dava para abordar ponteiros com tanta facilidade sob a ótica de eficiência como em C/C++.
Ter que retornar
errorem praticamente toda função e verificar isso toda vez comif err != nil {}é realmente chato, mas isso é uma vantagem. O custo é menor do quetry catch. E a keyworddefer, que faz um papel parecido comfinally {}, também é excelente. É ótimo não precisar ficar pensando no momento de liberar recursos. Também é muito bom que dê para montar um servidor backend excelente só com a biblioteca padrão (1.23 ou superior). Acima de tudo, o melhor é que, ao compilar para o SO de destino, não é necessário outro runtime nem instalação prévia.Não usei Go por tanto tempo assim, mas acho que já estou me alongando demais com opiniões muito pessoais, então vou parar por aqui. haha Eu gosto de Go, e gosto de outras linguagens também!
Comentários do Hacker News
O texto aponta muito bem várias desvantagens do Go, mas não acho que tratamento explícito de erros seja uma delas
Nunca gostei de tratamento por exceções, porque ele adiciona uma camada de “mágica” que é fácil demais de quebrar; mesmo que isso não seja um argumento tecnicamente perfeito, por décadas de experiência prefiro a abordagem em que o erro é tratado exatamente no ponto em que ocorre
Dá para dizer que Rust faz isso de forma mais elegante, e em projetos pessoais eu prefiro Rust, mas em projetos grandes, por onde passam desenvolvedores de vários níveis de experiência, vejo a filosofia do Go como uma abordagem bastante razoável entre as formas modernas de tratamento de erros
Na minha região, Go vem sendo adotado muito mais do que outras linguagens “novas” graças à sua simplicidade. Não é a melhor linguagem, mas tem muitos juízos embutidos que impedem você de se machucar, então muitas vezes acaba sendo a melhor opção como linguagem de uso geral
Não há stack trace e é preciso encapsular erros; por isso, além de mensagens de erro únicas, é necessário criar mensagens de encapsulamento únicas em cada ponto de chamada, para então poder dar
grepnas mensagens e inferir algo parecido com um stack trace“Tuplas de retorno” são um mecanismo estranho que existe só por causa dos erros, e nem dá para usar tuplas em outros lugares da linguagem. Por causa das regras esquisitas de inicialização de variáveis, em algum momento você acaba usando a variável
errerradaGo deveria ter tido enums, ou então um açúcar sintático melhor, como o
?do Rust, em vez dessas “tuplas de retorno” bizarras dedicadas a erros. O jeito atual é tedioso e propenso a errosGo é opinativo, mas com frequência demais isso parece uma teimosia para proteger a linguagem de críticas. Infelizmente, para uma linguagem de programação se tornar popular, marketing — ou seja, mentiras — também é importante, e isso funciona
Exceções são um
gotohorrível que qualquer biblioteca pode lançar dentro do meu código, e quase nunca são tratadas direito. Mesmo depois que uma exceção é “tratada”, a aplicação normalmente fica em um estado desconhecido e impossível de raciocinarErros em Go geralmente são fáceis até para engenheiros juniores depurarem, enquanto a causa real de exceções em Java pode ser difícil até para principals experientes entenderem
Por outro lado, ainda não vi alguém familiarizado com tipos soma como Maybe, Option e Result achar que a abordagem do Go é minimamente boa
Se você está tentando ler a lógica de um método específico e 75% dela é ruído de erro, enquanto só 25% das linhas são necessárias para entender o comportamento real, isso dificulta a leitura
Ao olhar uma base de código pela primeira vez, quero primeiro saber o que ela está tentando realizar, antes dos casos de borda de erro; depois vejo como ela garante que isso esteja correto
Nesse ponto, o tratamento de erros do Go falhou bastante. O problema não é o tratamento explícito de erros ser ruim, mas a insistência em misturar o caminho de sucesso e o tratamento de erros linha a linha. Também é possível tratar erros explicitamente em uma seção dedicada para isso
A parte com que concordo é que ele força você a pensar em todas as possibilidades que o código pode produzir. Mas isso é uma questão mais próxima de C do que de outras linguagens
Ao abstrair código, às vezes não há necessidade de tratar o erro imediatamente, ou você pode querer tratá-lo mais abaixo na pilha de chamadas
A linguagem acaba abrindo mão de muita legibilidade para obrigar um determinado estilo
Rust e Go são muito diferentes, e parece que as pessoas querem um meio-termo que ainda não existe
Uma linguagem com garbage collection, relativamente simples e que compile para binários com linkagem estática, mas com um sistema de tipos parecido com o do Rust e coisas como tipos resto
Sintaticamente, Gleam e Kotlin chegam relativamente perto, mas não totalmente. Eu gosto de Rust, mas acho que ela é complexa demais para muita gente que até tem capacidade de construir algo, mas não é formada em ciência da computação nem trabalha como programadora
Se você usa a linguagem só de vez em quando, não vai lembrar o que é uma vtable, nem quando e como um valor é liberado. Não existe “linguagem perfeita”, mas tanto Go quanto Rust trouxeram coisas excelentes, e eu gostaria que alguém se inspirasse nas duas para criar uma linguagem simples que pudesse ser amplamente usada
Kotlin de verdade é bem complexa, e você acaba gastando tempo tentando ser esperto. Há 1000 maneiras de fazer as coisas, e existem sobrecarga de operadores, proxies, properties, companion objects, exceções e tipos de resultado
Supondo que você use Gradle, o sistema de build é quase uma tortura para quem está acostumado com
go buildA API de corrotinas parece ao mesmo tempo mais complexa e mais limitada que Goroutines. Ela é mais estruturada, mas menos flexível e exige mais esforço para usar
O controle de acesso também é estranho. Não há como tornar um tipo exclusivo do pacote; ele é exclusivo do arquivo ou público para o módulo inteiro. Então a superfície da API cresce sem querer, ou você fica impedido de dividir a complexidade em vários arquivos
Kotlin/Native e Kotlin/JVM são, na prática, coisas diferentes. Kotlin/JVM é maduro, mas roda sobre a JVM, então parte dos usos para os quais você recorreria a Rust desaparece
O ecossistema de Kotlin/Native é muito fraco e a documentação também é insuficiente. Há bugs assustadores no rastreador de issues
Kotlin/Native também não permite distribuir bibliotecas somente com o código-fonte, então é preciso um CI complexo que compile binários para todos os sistemas operacionais e arquiteturas. Caso contrário, você acaba nem distribuindo a biblioteca, e isso também parece piorar ainda mais o ecossistema fraco
Assim como adicionaram generics, bastaria acrescentar exatamente três coisas: Result, um contêiner genérico para erros; Optional, para um tratamento melhor de nil; e um pequeno açúcar sintático como o operador Elvis
A grande vantagem que quase explica sozinha o sucesso de Go é que ela foi criada por uma empresa gigante, usada internamente por ela e teve recursos para criar ótimas ferramentas e bibliotecas open source. O fato de uma linguagem já estar em uso e ter bibliotecas parece ser o maior fator determinante para seu sucesso
A sintaxe básica é muito direta, a adoção já é enorme, o ecossistema é grande, o runtime continua melhorando, e também é possível compilar para WASM
A partir daí, remover as partes estranhas, como flexibilidade excessiva de redefinição, abuso do escopo global e esquisitices com números, e adicionar tipos, tipos soma/produto, Result, Maybe, decimal etc.
E, se incluir um estilo em que tudo é expressão, pattern matching, recursos funcionais como currying e uma biblioteca padrão abrangente, poderia surgir uma linguagem muito próxima do que muita gente quer
Outro grande avanço que eu gostaria de ver nas ferramentas de desenvolvimento é reformular o HTML para incorporar controles avançados de verdade e permitir estilização fácil com CSS inline. Isso reduziria a quantidade de JS necessária no cliente
Acho que essas duas coisas aumentariam bastante a produtividade de programação no desenvolvimento relacionado à web
Com
dotnet publish -p:PublishAot=true, é possível compilar para um binário nativo estático. Em F#, é preciso usar os métodosConsole.*em vez deprint*, porqueprinttem reflexão irrestrita internamente por causa da saída estrutural do especificador de formato%A, o que prejudica o tamanho do binário e faz o compilador reclamarEm especial, F# é recomendável como uma “alternativa a Rust mais fácil e orientada a negócios”. Ela é orientada a expressões, tem uniões discriminadas, inferência de tipos HM completa e uma boa combinação com tipagem gradual. Análise de dados e modelagem de domínio também são muito agradáveis
Para programação de sistemas, C# se torna uma opção. Ela oferece excelentes primitivas de concorrência, interoperabilidade nativa rápida e às vezes quase sem custo, binários nativos menores que os de Go e muitas APIs de baixo nível, incluindo SIMD portátil
Go muitas vezes não se encaixa bem nesse tipo de trabalho ou simplesmente não consegue fazê-lo sem contornos. .NET vem ganhando popularidade nessa área entre comunidades não ligadas a jogos, e muitas bibliotecas de alto desempenho também estão surgindo. Além disso, ao contrário das linguagens de nicho sugeridas no comentário irmão, ela se beneficia de um enorme ecossistema existente, então você não precisa fazer todo o trabalho de base por conta própria
Go: “Sou uma linguagem simples!”
O usuário usa Go por um tempo
Usuário: “Eu te odeio, você é uma linguagem simples!”
Talvez por eu ter mais de 50 anos, eu goste de linguagens simples
Essa “crítica” não me parece muito equilibrada; tudo em tecnologia é uma questão de trade-offs, e julgamentos sem equilíbrio são fracos
Claro que minha conclusão é outra: https://www.inkmi.com/blog/why-we-chose-go-over-rust-for-our...
O texto traz muitos exemplos apontando inconsistências irritantes da linguagem, e isso é o oposto de simplicidade
Gosto da apresentação de Go do Rob Pike, e parte dela abriu meus olhos. Só queria que o Go que vejo na prática fosse muito mais parecido com o Go que os fãs de Go descrevem em abstrato
Ao contrário da aparência, quando você começa a usar Go, logo fica claro que ela não é simples. Há muita complexidade escondida e muitas armadilhas. Ex.: https://archive.ph/WcyF4
Ainda assim, é uma linguagem útil e eu a uso bastante, mas ela não é “simples”
Acho que uma linguagem simples deveria ter alta consistência. As regras deveriam ser poucas, mas universais e consistentes em todos os lugares. Mas Go tem esquisitices, inconsistências e gambiarras por toda parte
Um bom exemplo é a omissão de tipos. Ela é possível em declarações de arrays, slices e maps, mas não em structs. Permitir isso também em structs traria várias vantagens de legibilidade e também possibilitaria parâmetros nomeados por meio de argumentos de structs anônimas, o que poderia ser um desenho muito melhor do que a gambiarra feia usada hoje
Claro que a maioria das alternativas razoáveis a Go é ainda pior sob esse ponto de vista. Coisas como Python, Ruby e JS
Fico curioso sobre o que motiva alguém a detonar uma linguagem em um texto tão longo. Mesmo alguns pontos válidos do texto não foram escritos em um tom construtivo, então vou chamar isso de acusação
Existe alguma linguagem que não possa ser criticada?
O autor escreveu isso para se sentir melhor culpando a linguagem pelo fracasso naquele projeto? Ou será que não entende que nem todo mundo pensa e trabalha da mesma forma, e que algo insuportavelmente irritante para uma pessoa pode quase nem ser percebido por outra?
Linguagens que não atendem a necessidades reais geralmente desaparecem sozinhas e quase nunca precisam de ajuda para isso
Go pode ser diferente de linguagens “mais refinadas”, mas funcionou muito bem nos projetos que encontrei. Espero que o autor não seja obrigado a trabalhar com Go. O restante da comunidade vai continuar construindo serviços, dando feedback ao time de Go e acompanhando a linguagem evoluir lentamente sem quebrar produção
Complexidade essencial não desaparece só porque você fecha os olhos
Se você decide não se importar com a complexidade, está apenas empurrando essa complexidade para outros desenvolvedores da organização, para o pessoal de operações, para os clientes, para alguém. Agora são eles que precisam contornar suas suposições para que tudo funcione sem problemas
Hoje em dia, muitas vezes eu sou esse “alguém”, e já estou cansado
É claro que Go funcionou muito bem nos projetos que você encontrou. C, C++, PHP e JavaScript também funcionam. No nosso mundo, muita coisa “funciona”. Adicionar mais uma faixa à via também funciona, e um Estado policial também funciona
Mas será que outra coisa não poderia funcionar muito melhor?
O motivo de ter sido escrito provavelmente é que o autor original sentiu que sua opinião não estava suficientemente representada “no mundo”. Eu também não sou fã de Go, então a popularidade de Go em 2022 me deixava confuso. Parecia que desvantagens bem claras estavam sendo ignoradas
As pessoas já não escrevem tanto posts longos dizendo que PHP é uma linguagem ruim, porque isso já foi discutido demais
O fato de este post de blog não ter sido escrito do jeito que você prefere não significa que ele não tenha valor para outras pessoas
Tudo que Go fazia era apresentado como essencial, o melhor e a única forma de resolver aquele problema, independentemente de ser a implementação mais básica possível ou algo que outras linguagens já faziam melhor
Tudo que Go não fazia era tratado como desnecessário, como uma conspiração da Big Complexity para viciar nós, usuários não iluminados de não-Go, em açúcar sintático. Coisas como tratamento decente de null, tratamento de erros, menos boilerplate, generics ou soluções consistentes sem criar casos especiais para tudo
Mesmo nesta thread, o ataque a espantalhos continua. Quando surge tratamento de erros, em vez de reconhecer o fato óbvio de que o tratamento de erros de Go poderia ser muito melhor, puxam algo como JavaScript, como se quem critica Go achasse que JavaScript é o ápice do tratamento de erros
Sempre que trabalho com outra linguagem, mesmo que Go não seja uma linguagem perfeita, sempre dá vontade de voltar para ela
Gosto do fato de que ela literalmente simplesmente funciona. Você instala Go, baixa o código, escreve o código e pronto
Não é preciso descobrir qual versão, runtime, configuração, ferramenta de build ou gerenciador de pacotes usar. Basta instalar e usar Go
Talvez Rust seja a única outra linguagem que ofereça a mesma experiência
Pode ser uma mentira que conto a mim mesmo, mas toda vez que uso Python, TypeScript ou Java, só quero escrever código, e acabo ficando com medo de programar porque tenho que depurar ou entender problemas de configuração, gerenciador de pacotes, ferramentas de build e versões
Toda vez que leio críticas a Go, penso a mesma coisa. Ainda assim, vou continuar usando Go
Entendo que há muitos problemas que, em teoria, poderiam ser documentados facilmente e que, na prática, as pessoas encontram com frequência, mas, mesmo assim, no trabalho real, ainda sinto que é uma das melhores linguagens de programação
De algumas coisas frequentemente listadas como desvantagens eu discordo; por exemplo, gosto do modo como os erros são tratados explicitamente em todos os lugares. De outras eu concordo, mas elas não me incomodam muito mais do que as desvantagens de outras linguagens de programação
Dá até pena pensar que pessoas especialmente sensíveis às áreas em que Go não teve sucesso vão passar a vida inteira reclamando disso
Mas, ao escolher que linguagem usar em um projeto, não aplico uma lógica racional cheia de argumentos sobre “qual é a melhor linguagem”. Escolho uma linguagem que combina comigo, na qual sinto que consigo escrever bom software de forma consistente e com a qual é agradável trabalhar
Como alguém que valoriza correção, eu gostaria que essa linguagem fosse mais próxima de Rust, mas no momento não é. Também não odeio Rust, mas não é a linguagem à qual recorro quando estou com pressa
Já se escreveram textos demais sobre como Go é horrível. Pelo menos agora sei um pouco como deve ser ser fã de PHP alguns anos atrás. É um exagero, mas, na minha opinião, não é um exagero tão grande assim
Você vai usar Go
E vai se arrepender
Mas aí já será tarde demais, e você estará preso a ela
O autor apresenta um argumento bastante convincente, e é muito difícil refutá-lo. Em muitos dos casos explicitamente listados e em muitas situações profissionais em que empresas tecnicamente incompetentes usam tecnologias inadequadas, Go é uma má escolha, e você vai se arrepender de colocá-la em produção
Empresas devem se ater a ferramentas entediantes. Em projetos pessoais, claro, faça o que quiser
Fora isso, vejo a simplicidade como um ponto forte. É agradável de usar, fácil de aprender e, em geral, tem desempenho bem decente. Eu não a escolheria para todas as situações, mas há muitas em que ela é uma boa ferramenta
Da mesma forma, gosto até certo ponto de Java e Python, e lido suficientemente bem com ambas. Essas linguagens são boas ferramentas para aquilo a que se propõem
Não entendo por que as pessoas se apegam de forma tão apaixonada a linguagens de programação. Linguagens são ferramentas. Você pode gostar mais de algumas ferramentas, mas isso não invalida as ferramentas de que você não gosta
if err != nilme batem no rostoHouve muitas boas propostas para corrigir esse problema, mas uma parte extremamente conservadora e barulhenta da comunidade matou todas elas com feedback quase venenoso
Agora a equipe de Go está psicologicamente assustada com essas pessoas e desistiu da batalha por melhorias no tratamento de erros
Em todas as pesquisas com desenvolvedores Go, a comunidade vota de forma esmagadora por melhorias no tratamento de erros, mas uma minoria de extremistas descarrilou todo o progresso
Go adotou uma postura arrogante de decidir por você quais recursos seriam necessários e ofereceu muito pouco para usar no trabalho
Discussões da época:
(130 pontos, 148 comentários) https://news.ycombinator.com/item?id=34188528
(748 pontos, 544 comentários) https://news.ycombinator.com/item?id=31205072
Há uma parte que diz: “Comecei a me importar muito mais com semântica do que com sintaxe, então nem olhei para Zig, Nim, Odin etc. Não tenho mais interesse em um ‘C melhor’”
Acho o texto bastante prolixo. A parte em que diz que Go é “acidental” é bobagem, porque Rust também é igualmente “acidental” em termos de origem e design
Uma coisa que chamou a atenção é que Nim definitivamente não é um “C melhor”. Ela tem garbage collection e também pode usar contagem de referências. Se quiser, dá para usá-la como um C melhor
O sistema de tipos de Nim evita muitos dos problemas que Go tem, mas não é tão rigoroso quanto o de Rust
No fim, muito software foi escrito e implantado em Go, roda rápido, tem pouco downtime, é em geral eficaz e parece ter poucos problemas de segurança. Vejo muito menos projetos de software implantados em Rust, e o Firefox ainda é mais de 95% C++
Não tenho interesse nessa briga e, no trabalho, uso Python e C++ com satisfação. Mesmo que haja muito menos software escrito em Rust, ainda assim dá para dizer que muito software foi escrito e implantado em Rust. Também não dá para ignorar os investimentos das grandes empresas
0: https://x.com/m_ou_se/status/1599173341117435905
Não entendo por que Go é sempre comparado a Rust. A comparação mais adequada é com Java.
A comparação entre Go e Rust é mais comum, mas menos útil, provavelmente porque ambas fazem parte de uma nova onda de linguagens e ambas, por padrão, geram binários com linkagem estática.
Em situações em que Java é uma boa escolha, Go é uma escolha realmente ruim. Além de a linguagem ser limitada, o ecossistema também é muito limitado em comparação com o de Java.
Eu mantenho Go, C# e TypeScript como minhas linguagens principais, e essa combinação oferece uma ótima cobertura. Se eu tiver seis meses em que possa arcar com uma queda de produtividade, ou um projeto com requisitos de qualidade baixos, vou adicionar Rust.
Para aprender a linguagem, uma ou duas semanas bastam, mas leva tempo para aprender a fazer engenharia dentro do ecossistema.
A maioria das empresas que não tem grandes preocupações de desempenho não usa C++ para serviços de rede. Se Rust já fosse maduro, teria sido uma boa escolha, mas o tempo de compilação, que era um dos problemas centrais no início, pode ser ainda pior em bases de código Rust.
Na prática, também vi casos de uso de Go em vez de Rust, e o principal motivo era que Go compila muito mais rápido.
Se a pergunta é se Rust e Go são suficientemente diferentes para que cada uma deva ser escolhida em casos diferentes, sim. É justamente por isso que as duas são consideradas juntas e uma delas é escolhida.
Houve uma época em que eu estava procurando um Python “melhor”. Queria uma linguagem mais simples e segura que C/C++, mas mais rápida que Python e, acima de tudo, capaz de gerar um binário único.
Por isso olhei de tudo, de Rust a linguagens menos conhecidas como Hare.
Go deveria ter sido a escolha óbvia, mas, por algum motivo que não sei explicar, eu não gostava da sintaxe. Rust eu entendo. Usa muitos caracteres especiais difíceis de lembrar e de digitar em teclados não QWERTY, além de outros pontos dolorosos. Em vários Lisps, o problema eram os parênteses e a notação polonesa reversa. Mas não consigo racionalizar por que não gosto de Go.
No fim, decidi compilar o código Python com Nuitka. Não sei quanto ao ganho de velocidade, mas agora consigo fornecer um binário.
Também tenho olhado cada vez mais para C#, porque a compilação AOT está evoluindo. Ainda assim, não gosto da verbosidade do modo padrão nem do fato de parecer preso demais ao Windows.
Gostei bastante de Nim e Crystal, mas as comunidades pequenas eram uma barreira. Mesmo assim, fiquei realmente impressionado com o que Nim consegue fazer com uma comunidade tão pequena, e passei a considerá-la uma ótima linguagem.
Pretendo tentar me motivar a aprender uma das linguagens mencionadas.
Hoje em dia, ele é realmente bastante bom de usar.
Há também outra abordagem: abandonar a C-API e criar uma ponte para linguagens como Go ou Rust.
Trabalhei por alguns anos no py2many. Gostaria de receber feedback sobre essa abordagem.