3 pontos por GN⁺ 2024-11-27 | 2 comentários | Compartilhar no WhatsApp
  • 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 nil impedem 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
  • A dor revelada em netaddr.IP está 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 == b e a.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 newtype opaco, 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 if e else
  • 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 A ou *A, a variável local a em main pode ser alterada ou não
  • 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 int32 a Params, mas se o chamador passar só a: 47, a execução acontece com b=0
    • Em contraste, se o argumento fosse adicionado diretamente à função, haveria erro de compilação por número insuficiente de argumentos
  • Um dos princípios do Go é que o zero value seja útil, mas o significado real muda conforme o tipo
    • Chamar len em um slice nil retorna 0, então ele se comporta como slice vazio
    • Inserir valor em um map nil gera panic: assignment to entry in nil map
  • Channel também tem regras próprias
    • Enviar para um channel nil bloqueia para sempre
    • Receber de um channel nil bloqueia para sempre
    • Enviar para um channel fechado causa panic
    • Receber de um channel fechado retorna imediatamente o zero value
  • 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

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 para interface{}
    • 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 nil entra onde não deveria e faz alguém acordar de madrugada em produção repetem o Billion Dollar Mistake

2 comentários

 
tsboard 2024-11-28

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 100 com uma ferramenta como hey.

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 error em praticamente toda função e verificar isso toda vez com if err != nil {} é realmente chato, mas isso é uma vantagem. O custo é menor do que try catch. E a keyword defer, que faz um papel parecido com finally {}, 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!

 
GN⁺ 2024-11-27
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

    • O tratamento de erros do Go tem várias desvantagens. Como o autor pende bastante para o lado do Rust, a alternativa não são exceções, mas sim tipos soma Result
      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 grep nas 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 err errada
      Go 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 erros
      Go é 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
    • Programo há mais de 30 anos e usei C# por quase 10, mas considero exceções uma das piores ideias da história da programação
      Exceções são um goto horrí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 raciocinar
      Erros 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
    • Sem exceção, sempre que vi alguém elogiar o tratamento de erros do Go, a comparação era apenas com exceções, como se exceções fossem a única alternativa
      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
    • Essa parte do tratamento de erros é quase religião pura, e também contraria um dos princípios mais fundamentais do Go: “código deve ser mais fácil de ler do que de escrever”
      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
    • Discordo da afirmação de que o tratamento explícito de erros é exagerado
      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 é interessante como meio-termo, mas acho que, na maioria dos trabalhos, é muito menos produtiva que Go e não serve para os trabalhos em que você recorreria a Rust
      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 build
      A 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
    • Pessoalmente, acho que a maioria dos recursos de Rust que as pessoas gostariam de ver em Go ainda poderia caber dentro do “conceito” de Go
      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
    • Meu pensamento sobre esse assunto, em outras palavras, é este: começar por JavaScript
      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
    • Sou o autor; em que sentido Gleam fica aquém desse ponto? Também estou procurando esse meio-termo, então tenho observado Gleam com bastante curiosidade
    • C# e F# são, de longe, as opções mais próximas. As outras alternativas não têm um sistema de tipos ou ferramentas tão bons quanto os deles
      Com dotnet publish -p:PublishAot=true, é possível compilar para um binário nativo estático. Em F#, é preciso usar os métodos Console.* em vez de print*, porque print tem 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 reclamar
      Em 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...

    • É uma distorção comum em críticas a Go. Algo como: “você não entende simplicidade, foi infectado pelo verme cerebral do Java”
      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
    • Eu gostaria que desaparecesse o discurso de que Go é uma linguagem “simples”
      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”
    • O problema de Go é que ela não foi projetada como uma linguagem simples para os usuários, e sim como uma linguagem fácil de implementar para os mantenedores
      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
    • Scheme é uma linguagem simples, mas Go esconde complexidade até ela explodir no pior momento
      Claro que a maioria das alternativas razoáveis a Go é ainda pior sob esse ponto de vista. Coisas como Python, Ruby e JS
    • Go só é “simples” quando você faz coisas simples. Tente escrever por conta própria um armazenamento KV ou um banco de dados, e você logo vai precisar de recursos de linguagem um pouco mais modernos
  • 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

    • A resposta para “o que leva alguém a fazer uma crítica tão longa” está no próprio texto
      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?
    • É só o blog de alguém, com um bom desabafo. Tudo bem, não precisa analisar tão profundamente
      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
    • Nem todo mundo gosta apenas de análises tediosas e clínicas; há quem goste de textos com um pouco de paixão e humor
      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
    • Um fator muito subjetivo e irracional que me fez quase odiar Go foi que, por anos, os fanáticos por Go fizeram gaslighting com todo mundo sobre cada aspecto de Go
      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
    • Bjarne disse da melhor forma. Só existem dois tipos de linguagem: aquelas das quais as pessoas reclamam e aquelas que ninguém usa
  • 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

    • A força de Go é a toolchain integrada. Como o texto diz, isso torna a interoperabilidade mais difícil, mas, pela minha experiência pessoal e limitada, esse não é um caso de uso frequente
    • Esse é o aspecto de Go que mais deveria ser elogiado, e Rust também adotou isso com os channels
  • 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

    • A frase “pessoas sensíveis às áreas em que Go não teve sucesso vão reclamar pelo resto da vida” soa como um ótimo endosso a este texto. Porque isso é literalmente o ponto central dele
      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
    • Gosto de Go até certo ponto. O tratamento explícito de erros às vezes é um pouco chato, mas é apenas a forma de fazer o trabalho
      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
    • “Escolher uma linguagem que combina comigo” é a escolha mais sensata para qualquer pessoa. Golang só vira problema quando um gerente obriga você a usá-la
    • Gosto de Go, mas, depois de escrever e ler código Go demais, sonho pesadelos em que demônios if err != nil me batem no rosto
      Houve 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
    • O motivo pelo qual muita gente realmente odeia Go é que já experimentou recursos de outras linguagens
      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++

    • A afirmação de que “há muito menos projetos de software implantados em Rust, e o Firefox ainda é mais de 95% C++” me lembra este tweet[0] de 2022, o que é engraçado
      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.

    • Acho que essa é a forma correta de entender Go. Go é uma linguagem para criar servidores em ambientes onde uma forte consistência do código e um curto tempo de adaptação para engenheiros juniores são importantes. Ou seja, é perfeita para os cenários de grandes empresas em que Java costumava ser usado.
      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.
    • Go não é uma iteração de Java, é uma iteração de C.
      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.
    • Go, na verdade, foi criado enquanto se esperava a compilação de C++. O objetivo era fazer algo melhor que C++ para serviços de rede, e isso deu certo. Só que o sarrafo era bem baixo.
      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.
    • Quem decide em que linguagem escrever algo novo tem grande probabilidade de considerar Go e Rust juntos. A chance de considerar Java é muito baixa.
      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.

    • .NET Core não é preso ao Windows, exceto por certos frameworks como WPF e pelo armazenamento de credenciais. Até para WPF há alternativas que funcionam em qualquer lugar.
      Hoje em dia, ele é realmente bastante bom de usar.
    • Nuitka tem boa compatibilidade, mas mantém as desvantagens da Python C-API.
      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.