Visão geral da toolchain do Go no dia a dia
A toolchain do Go é um conjunto de comandos oficiais (executados via go) que cobre o ciclo completo de desenvolvimento: executar código (go run), compilar binários (go build), testar (go test), formatar (gofmt / go fmt) e analisar problemas comuns (go vet). A ideia é padronizar o fluxo: você escreve código, formata, valida com vet, roda testes e gera o binário final.
Como o Go encontra pacotes (módulos, import e diretórios)
No Go moderno, o ponto de partida é o arquivo go.mod, que define o módulo (ex.: module example.com/cli). A partir dele, o Go resolve imports usando:
- Pacotes do módulo atual: imports que começam com o caminho do módulo (ex.:
example.com/cli/internal/app) são encontrados dentro do diretório do projeto. - Dependências: imports externos são baixados e versionados automaticamente (quando necessário) e ficam disponíveis para build/test.
- Biblioteca padrão: imports como
fmt,os,net/httpsão resolvidos localmente.
O comando go env ajuda a inspecionar variáveis relevantes. Duas úteis no cotidiano:
go env GOMOD: mostra qualgo.modestá sendo usado (ou vazio se você estiver fora de um módulo).go env GOPATH: ainda é usado como local padrão de cache e downloads, mesmo em projetos com módulos.
Pacote vs. arquivo: por que isso importa
Quase sempre você trabalha com pacotes (diretórios). Um pacote é um conjunto de arquivos .go no mesmo diretório com o mesmo package. Muitos comandos aceitam:
- Caminhos de pacote (ex.:
./...,./cmd/mytool) - Arquivos (mais comum em exemplos rápidos, especialmente com
go run)
Cache de build: o que é e quando é invalidado
O Go usa cache agressivo para acelerar builds e testes. Em geral, se nada relevante mudou, recompilar é quase instantâneo. O cache é invalidado quando:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
- Você altera arquivos
.godo pacote ou de dependências. - Você altera arquivos que afetam o build (ex.:
go.mod,go.sum). - Você muda flags que impactam o resultado (ex.:
-race, tags de build,GOOS/GOARCH). - Você muda variáveis de ambiente que afetam compilação/links.
Comandos úteis para lidar com cache:
go clean -cache: limpa o cache de build.go clean -testcache: limpa cache de testes (útil quando você quer forçar reexecução).go env GOCACHE: mostra onde o cache fica.
go run na prática (executar rapidamente)
go run compila e executa um programa, normalmente sem deixar o binário final no diretório do projeto. É ideal para desenvolvimento rápido e para rodar ferramentas internas.
Executando um pacote
go run ./cmd/mytoolEsse formato é o mais comum em projetos com estrutura cmd/. O Go compila o pacote main daquele diretório e executa.
Executando com argumentos
Para passar argumentos ao seu programa, coloque -- antes dos argumentos (boa prática para evitar conflito com flags do próprio go):
go run ./cmd/mytool -- -name Ana -n 3Executando arquivos diretamente
go run main.goÚtil em exemplos pequenos. Em projetos reais, prefira executar o pacote (diretório) para evitar confusão com múltiplos arquivos e dependências internas.
Flags úteis com go run
-v: mostra quais pacotes estão sendo compilados.-race: habilita o detector de race conditions (apenas em plataformas suportadas; aumenta tempo e consumo).
go run -v -race ./cmd/mytool -- -name Ana -n 3go build na prática (compilar binários)
go build compila pacotes. Para programas (package main), ele gera um executável. Para bibliotecas, ele apenas verifica e compila para o cache.
Build do pacote main
go build ./cmd/mytoolPor padrão, o binário é gerado no diretório atual com o nome do diretório do pacote (ex.: mytool).
Definindo nome e pasta de saída
go build -o bin/mytool ./cmd/mytoolEsse padrão (bin/) é comum para separar artefatos de build do código-fonte.
Build com informações detalhadas
-v: lista pacotes compilados.-x: mostra os comandos internos executados (útil para depurar).
go build -v -x -o bin/mytool ./cmd/mytoolBuild com race detector
go build -race -o bin/mytool ./cmd/mytoolUse para builds de desenvolvimento quando você suspeita de problemas de concorrência. Em geral, não é usado para release por custo e restrições.
Entendendo erros comuns de build
| Mensagem | O que significa | Como agir |
|---|---|---|
package ... is not in std | Import foi interpretado como biblioteca padrão ou não foi resolvido no módulo | Verifique o caminho do import, se está dentro do módulo, e se go.mod está correto |
cannot find module providing package ... | Dependência não encontrada | Rode go get (quando apropriado) ou go mod tidy para ajustar dependências |
found packages X and Y in ... | Arquivos no mesmo diretório com package diferente | Separe em diretórios distintos ou unifique o nome do pacote |
Mini-projeto CLI para praticar go run e go build
Você vai criar um pequeno utilitário de linha de comando que gera saudações e, opcionalmente, faz trabalho concorrente para demonstrar -race em testes.
Estrutura sugerida
mytool/ go.mod cmd/ mytool/ main.go internal/ greet/ greet.go counter.go counter_test.goPasso 1: criar o módulo
mkdir mytool && cd mytool go mod init example.com/mytoolPasso 2: implementar o pacote internal/greet
internal/greet/greet.go
package greet import "fmt" func Message(name string, times int) string { if times <= 0 { times = 1 } return fmt.Sprintf("Olá, %s! (x%d)", name, times) }internal/greet/counter.go (intencionalmente simples para testes)
package greet type Counter struct { n int } func (c *Counter) Inc() { c.n++ } func (c *Counter) Value() int { return c.n }Passo 3: criar o comando em cmd/mytool
cmd/mytool/main.go
package main import ( "flag" "fmt" "os" "example.com/mytool/internal/greet" ) func main() { name := flag.String("name", "mundo", "nome para saudar") times := flag.Int("n", 1, "quantidade") flag.Parse() msg := greet.Message(*name, *times) fmt.Fprintln(os.Stdout, msg) }Passo 4: executar com go run
go run ./cmd/mytool -- -name Ana -n 3Passo 5: compilar com go build
go build -o bin/mytool ./cmd/mytool ./bin/mytool -name Ana -n 3Se você alterar apenas um arquivo pequeno, o próximo go build tende a ser bem mais rápido por causa do cache.
go test: testes, flags úteis e cache de testes
go test compila e executa testes. Ele procura arquivos *_test.go e funções TestXxx. Também pode executar benchmarks e exemplos.
Criando um teste simples
internal/greet/counter_test.go
package greet import ( "sync" "testing" ) func TestCounterSequential(t *testing.T) { var c Counter c.Inc() c.Inc() if c.Value() != 2 { t.Fatalf("expected 2, got %d", c.Value()) } } func TestCounterConcurrent(t *testing.T) { var c Counter var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() c.Inc() }() } wg.Wait() if c.Value() != 100 { t.Fatalf("expected 100, got %d", c.Value()) } }Esse teste concorrente pode falhar de forma intermitente e, mesmo quando passa, pode esconder uma race condition. É exatamente o tipo de problema que o -race ajuda a detectar.
Rodando testes por pacote e por árvore
go test ./... go test ./internal/greetFlags úteis do go test
-v: mostra o nome dos testes executados.-run: filtra quais testes rodar (regex).-count=1: desativa o cache de testes para essa execução.-race: habilita detector de race conditions.
go test -v ./internal/greet go test -run TestCounterConcurrent -count=1 ./internal/greet go test -race ./internal/greetEntendendo o cache de testes
O go test pode reutilizar resultados anteriores quando detecta que nada relevante mudou. Você verá algo como (cached) no output. Para forçar reexecução:
go test -count=1 ./... go clean -testcachePadrões de qualidade: gofmt, go vet e um fluxo recomendado
gofmt (formatação padrão)
gofmt é o formatador oficial. Ele aplica um estilo consistente (indentação, alinhamento, espaçamento) e é o padrão aceito pela comunidade e ferramentas. Você pode usar:
gofmtdiretamente (mais controle)go fmt(atalho que formata pacotes)
Formatando um arquivo:
gofmt -w cmd/mytool/main.goFormatando o projeto todo (recursivo):
gofmt -w .Verificando se há arquivos não formatados (sem modificar):
gofmt -l .Alternativa via toolchain:
go fmt ./...go vet (análise estática para bugs comuns)
go vet procura padrões suspeitos que compilam, mas costumam indicar bug: formatação incorreta de Printf, uso indevido de append, cópias de locks, erros em tags, entre outros. Ele não substitui testes, mas pega problemas cedo.
Rodando no projeto:
go vet ./...Se o vet apontar algo, trate como alerta sério. Exemplo clássico: usar fmt.Printf("%d", "texto") (tipo incompatível) pode passar despercebido em revisão, mas o vet acusa.
Fluxo prático de qualidade (rotina diária)
Um fluxo simples e efetivo para antes de commit/PR:
- 1) Formatar:
gofmt -w .(ougo fmt ./...) - 2) Analisar:
go vet ./... - 3) Testar:
go test ./... - 4) Se houver concorrência:
go test -race ./...(quando aplicável)
Para depurar builds e entender o que está acontecendo, use -v (visibilidade) e -x (comandos internos). Para problemas intermitentes em testes, combine -count=1 com -race.