Tecnologias/01 - Linguagens/01 - Go5 min
Go
Conceito
Go nasceu no Google em 2007 para resolver um problema que não era de linguagem, era de organização: uma base de código C++ enorme, com compilação medida em minutos, grafo de dependências ingovernável e concorrência resolvida por biblioteca, mão a mão com threads.
A pergunta de design não foi "como escrever menos código". Foi:
Como manter milhares de engenheiros produtivos numa base de código que ninguém conhece por inteiro?
Quase toda escolha do Go sai daí:
| Decisão | O que ela protege |
|---|---|
| Compilação em segundos | ciclo de feedback; ninguém "espera o build" |
| Imports não usados são erro | grafo de dependências não apodrece |
gofmt canônico, sem opções |
zero discussão de estilo em code review |
| Sem herança, só composição | não existe hierarquia de 6 níveis para entender |
Erros como valores, sem try/catch |
o caminho de falha está visível na leitura linear |
| Concorrência na linguagem | go f() em vez de biblioteca de threads por projeto |
| Binário estático único | deploy é copiar um arquivo |
| Biblioteca padrão grande | menos dependências de terceiros para auditar |
| Promessa de compatibilidade Go 1 | código de 2012 compila hoje |
O que Go recusou de propósito
Isto é o mais informativo da linguagem:
- Herança de classes → só
struct+ embedding + interfaces. Ver 4. Composição vs Herança. - Exceções →
erroré um valor de retorno.panicexiste, mas é para bug, não para erro esperado. - Genéricos → só chegaram no Go 1.18 (2022), 13 anos depois, e ainda são deliberadamente limitados. O Go 1.27 finalmente permitiu métodos genéricos.
- Sobrecarga de operadores, construtores, anotações, macros → não existem.
- Enum / tipos-soma /
Option→ não existem; o zero value faz esse papel, mal.
O modelo mental
Go é uma linguagem compilada, com coletor de lixo, tipagem estática nominal e concorrência CSP. Compare o posicionamento:
| Go | Java/C# | Python/Node | Rust | |
|---|---|---|---|---|
| Tempo de build | segundos | dezenas de s | — | minutos |
| Runtime no deploy | nenhum (binário estático) | JVM/CLR | interpretador | nenhum |
| Gerência de memória | GC (pausas sub-ms) | GC (tunável, maior) | GC + GIL | sem GC, ownership |
| Concorrência | goroutine + channel | thread / virtual thread | event loop | async + Send/Sync |
| Curva | dias | semanas | dias | meses |
| Expressividade | baixa por escolha | média | alta | alta |
O nicho onde Go domina é bem definido: serviços de rede, CLIs e infraestrutura — Docker, Kubernetes, Terraform, Prometheus, etcd e o próprio Kafka Connect em Go (sarama/franz-go) vivem aí. Não é a linguagem para cálculo numérico, UI ou ciência de dados.
Trade-offs
O custo que você sente no primeiro dia: verbosidade. Um if err != nil a cada
chamada, três linhas para o que outras linguagens fazem em uma. A defesa honesta não é
"você se acostuma" — é que o caminho de erro fica junto do caminho de sucesso, e por
isso ninguém esquece dele. O custo é real e a compensação é real.
O custo que você sente no primeiro mês: o zero value. Go não tem null safety.
Toda variável nasce com valor zero — 0, "", nil, struct com campos zerados. Isso
elimina uma classe de bugs (variável não inicializada) e cria outra: não há como
distinguir "não informado" de "informado como zero". Daí a epidemia de *int e
sql.NullString em qualquer código que fale com banco ou JSON.
O custo que você sente no primeiro ano: pouca abstração. Sem tipos-soma, um error
pode ser qualquer coisa; sem enums, uma constante de status é só um string ou int
disfarçado; sem imutabilidade na linguagem, "não modifique este slice" é comentário, não
garantia. Ver 4. Mutabilidade.
O trade-off central: Go aposta que o tempo total gasto lendo código supera em muito o tempo gasto escrevendo. Se essa aposta vale para o seu contexto (time grande, serviço longevo, rotatividade), o preço é barato. Se você é um dev sozinho num protótipo, você só está pagando.
Exemplo prático
Um servidor HTTP completo, sem uma única dependência externa:
package main
import (
"encoding/json"
"log/slog"
"net/http"
"time"
)
type health struct {
Status string `json:"status"`
Time time.Time `json:"time"`
}
func main() {
mux := http.NewServeMux()
// Roteamento por método e wildcard: stdlib, desde o Go 1.22.
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(health{"ok", time.Now()}); err != nil {
slog.Error("encode falhou", "err", err)
}
})
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second, // sem isso, Slowloris derruba o processo
}
slog.Info("subindo", "addr", srv.Addr)
if err := srv.ListenAndServe(); err != nil {
slog.Error("servidor caiu", "err", err)
}
}
Três coisas dessa amostra explicam a adoção do Go em infraestrutura:
- Zero dependências. Roteador, JSON, log estruturado (
log/slog, Go 1.21+) e servidor HTTP são biblioteca padrão. Nada para atualizar por CVE de terceiro. - O deploy é um arquivo.
CGO_ENABLED=0 go buildgera um binário estático que roda num contêinerFROM scratchde ~10 MB. Ver 5. Go em Produção. - A tag
json:"status"mostra o estilo: metadados por struct tag, resolvidos por reflexão — barato de escrever, e a razão de oencoding/jsonter sido reescrito no Go 1.27. Ver 4. Versões, Módulos e Compatibilidade.
Aprofundamento aplicado
Fronteiras de pacote e dependências
Em um serviço de pedidos, separar pedido, httpapi e postgres pode manter a regra
de reserva independente do transporte e do banco. A dependência deve acompanhar a
necessidade: a regra recebe uma operação de persistência, enquanto o adaptador conhece SQL.
Uma interface é útil quando existe comportamento a substituir ou uma fronteira que merece um contrato. Criar uma interface para cada struct aumenta navegação sem necessariamente reduzir acoplamento. Também é possível começar com funções e tipos concretos.
Cenário: a regra de desconto precisa ser exercitada sem abrir conexão. Se ela exige inicializar servidor HTTP e banco, a montagem está misturada ao cálculo. Extraia uma função que recebe os dados necessários e devolve o resultado ou erro.
Exercício: implemente o mesmo cálculo em um comando de terminal e em um handler. Resultado esperado: os dois usam a mesma regra; apenas leitura da entrada, resposta e tratamento do erro pertencem aos adaptadores.
Referência: organização de módulos e pacotes.
Relacionado
- 2. Goroutines e Channels
- 3. Erros, Interfaces e o Estilo Go
- 4. Versões, Módulos e Compatibilidade
- 5. Go em Produção
- 1. Tipos e Sistema de Tipos
- 4. Composição vs Herança
Parte de MOC Go · go.dev/doc