El comandament go explicat: run, build, test, mod, fmt i vet
Guia pràctica dels comandaments essencials de Go: go run, build, test, fmt, vet i mod. Tot el que necessites per treballar en local i a CI.

Quan comences amb Go venint de Java, Python o Node, una de les primeres coses que notes és que no necessites Maven, Gradle, pytest, black ni ESLint. El binari go que s’instal·la amb el llenguatge ja inclou compilador, gestor de dependències, runner de tests, formatador, linter i generador de codi. Tot en un sol comandament.
Això no és un accident. És una decisió de disseny deliberada. L’equip de Go va apostar des del principi per eines estàndard integrades al propi toolchain. Menys configuració, menys debats sobre quina eina fer servir, menys fricció en incorporar algú nou al projecte. Si saps Go, ja saps com compilar, testejar i formatar qualsevol projecte Go.
Aquest article cobreix els subcomandaments que faràs servir cada dia: go run, go build, go test, go fmt, go vet, go mod, go generate, go install i go env. Amb exemples pràctics, flags útils i com combinar-los en un pipeline de CI.
go run: compilar i executar en un pas
go run compila i executa un programa Go sense generar un binari persistent. És l’equivalent a python script.py o node app.js: útil per al desenvolupament ràpid i scripts.
go run main.goSi el teu main importa altres fitxers del mateix paquet, pots passar diversos fitxers o fer servir el patró de directori:
go run .
go run ./cmd/serverPassar arguments al programa
Els arguments després del fitxer o paquet es passen directament al programa:
go run main.go --port 8080 --env productionFlags útils de go run
# Mostrar els comandaments que executa internament
go run -x main.go
# Compilar sense optimitzacions (útil per a debugging amb Delve)
go run -gcflags=\"-N -l\" main.goQuan NO fer servir go run
go run compila cada vegada que l’executes. No hi ha caché implícita del binari resultant. Per a un servidor que reinicies 50 vegades al dia, és acceptable. Per a un binari que distribuiràs o desplegues, necessites go build.
go runés per al desenvolupament local. Mai el facis servir en producció ni en un Dockerfile final.
go build: crear binaris
go build compila el teu codi i genera un binari executable. Si no especifiques un nom de sortida, fa servir el nom del mòdul o del directori.
go build -o server ./cmd/serverEl binari resultant és estàtic per defecte (en la majoria de casos): no necessita runtime, no requereix que Go estigui instal·lat a la màquina de destí. Copies el binari i funciona.
Cross-compilation
Una de les millors característiques de Go és la compilació creuada. Pots generar binaris per a qualsevol plataforma des de la teva màquina:
# Linux AMD64 (l'opció habitual per a servidors i contenidors Docker)
GOOS=linux GOARCH=amd64 go build -o server-linux ./cmd/server
# macOS ARM (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o server-mac ./cmd/server
# Windows
GOOS=windows GOARCH=amd64 go build -o server.exe ./cmd/serverNo necessites una màquina virtual, no necessites Docker, no necessites un CI especial. Només dues variables d’entorn. Això és quelcom que a Java o Python directament no existeix de forma nativa.
Flags de build comuns
# Reduir la mida del binari eliminant informació de debug
go build -ldflags=\"-s -w\" -o server ./cmd/server
# Injectar variables en temps de compilació (versió, commit, data)
go build -ldflags=\"-X main.version=1.2.3 -X main.commit=$(git rev-parse HEAD)\" -o server ./cmd/server
# Build completament estàtic (sense dependències CGO)
CGO_ENABLED=0 go build -o server ./cmd/server
# Veure quins comandaments executa internament
go build -x -o server ./cmd/serverInjectar versió en temps de compilació
Un patró molt comú és definir variables al teu main.go i omplir-les amb -ldflags:
package main
import \"fmt\"
var (
version = \"dev\"
commit = \"none\"
date = \"unknown\"
)
func main() {
fmt.Printf(\"server %s (commit: %s, built: %s)\n\", version, commit, date)
}go build -ldflags=\"-X main.version=1.2.3 -X main.commit=$(git rev-parse --short HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)\" -o server .Això et dóna un binari que sap exactament quina versió és i quan es va compilar. Molt útil per a logs i per a diagnòstic en producció.
go test: tests, cobertura i benchmarks
go test és el runner de tests integrat. Llegeix fitxers *_test.go, executa funcions que comencen per Test, Benchmark o Example, i reporta resultats. Sense JUnit, sense pytest, sense configuració.
# Executar tests del paquet actual
go test
# Executar tests de tot el projecte
go test ./...
# Amb output detallat
go test -v ./...
# Executar només tests que coincideixen amb un patró
go test -run TestCreateUser ./internal/user/Si vols aprofundir en testing, tinc un article dedicat a testing en Go amb més detall sobre table-driven tests, mocks i organització.
Cobertura
# Veure percentatge de cobertura
go test -cover ./...
# Generar fitxer de cobertura per a inspecció detallada
go test -coverprofile=coverage.out ./...
# Veure cobertura línia per línia al navegador
go tool cover -html=coverage.out
# Veure cobertura per funció
go tool cover -func=coverage.outL’output de -cover et dóna un percentatge per paquet:
ok github.com/user/project/internal/user 0.012s coverage: 87.3% of statements
ok github.com/user/project/internal/auth 0.008s coverage: 92.1% of statementsBenchmarks
Go té suport natiu per a benchmarks. Defineixes funcions Benchmark* als teus fitxers de test:
func BenchmarkParseConfig(b *testing.B) {
data := loadTestConfig()
b.ResetTimer()
for i := 0; i < b.N; i++ {
ParseConfig(data)
}
}I els executes amb:
# Executar benchmarks
go test -bench=. ./...
# Benchmarks amb informació de memòria
go test -bench=. -benchmem ./...
# Només benchmarks, sense tests normals
go test -bench=. -run=^$ ./...
# Executar un benchmark concret
go test -bench=BenchmarkParseConfig ./internal/config/Output típic:
BenchmarkParseConfig-8 1000000 1052 ns/op 256 B/op 4 allocs/opAixò et diu: s’ha executat un milió de vegades, cada execució ha trigat 1052 nanosegons, ha assignat 256 bytes i ha realitzat 4 allocations. Informació concreta per optimitzar.
Race detector
Go té un detector de race conditions integrat. Activa’l amb -race:
go test -race ./...Això instrumenta el codi per detectar accessos concurrents a memòria compartida. És més lent, així que no el facis servir en benchmarks, però hauria de ser obligatori a CI.
Flags útils de go test
# Timeout global (per defecte 10 minuts)
go test -timeout 30s ./...
# Executar tests en paral·lel (per defecte GOMAXPROCS)
go test -parallel 4 ./...
# Desactivar caché de tests (força re-execució)
go test -count=1 ./...
# Tests curts (skip de tests pesats marcats amb testing.Short())
go test -short ./...go fmt: el final dels debats de format
go fmt formata el teu codi segons l’estil oficial de Go. No hi ha configuració, no hi ha opcions, no hi ha .editorconfig ni .prettierrc. Un format. Per a tothom.
# Formatar un fitxer
go fmt main.go
# Formatar tot el projecte
go fmt ./...A la pràctica, la majoria de projectes fan servir gofmt (el formatador subjacent) o goimports (que a més organitza els imports):
# goimports: formata + organitza imports + afegeix imports que falten
goimports -w .Per què això importa
A Java tens Checkstyle, SpotBugs, Google Java Format, IntelliJ formatter, cadascun amb la seva configuració. A Python tens Black, YAPF, autopep8, isort. A JavaScript tens Prettier, ESLint, Standard. Cada projecte en tria un, el configura, i sempre hi ha algú que té l’IDE configurat diferent i introdueix canvis de format als diffs.
A Go aquest problema no existeix. go fmt és l’estàndard. Punt. No hi ha debat sobre tabs vs espais (tabs), no hi ha debat sobre on va la clau d’obertura (mateixa línia), no hi ha debat sobre l’amplada màxima de línia (no hi ha límit forçat). El format és part del llenguatge.
Si el teu codi no està formatat amb
go fmt, la comunitat ho considera incorrecte. Així de simple.
go vet: anàlisi estàtica integrada
go vet examina el teu codi buscant errors comuns que el compilador no detecta: arguments mal passats a fmt.Printf, variables de loop capturades en goroutines, condicions impossibles, codi inassolible i més.
# Analitzar el paquet actual
go vet
# Analitzar tot el projecte
go vet ./...Què detecta go vet
Alguns exemples del que troba:
// Printf amb arguments incorrectes
fmt.Printf(\"user: %d\", username) // vet: wrong type for %d
// Copiar un sync.Mutex (error greu de concurrència)
var mu sync.Mutex
mu2 := mu // vet: assignment copies lock value
// Comparació impossible
if x != x { // vet: suspicious comparison
}
// Codi inassolible
func foo() int {
return 1
fmt.Println(\"never\") // vet: unreachable code
}go vet no és un linter complet com golangci-lint, però cobreix els errors més perillosos i és ràpid. A CI hauria d’executar-se sempre.
go vet vs golangci-lint
go vet és un subconjunt. golangci-lint agrupa dotzenes de linters (incloent-hi vet) i permet configurar regles. Per a un projecte seriós, fes servir ambdós:
# A CI: primer el ràpid i estàndard
go vet ./...
# Després l'anàlisi completa
golangci-lint rungo mod: gestió de dependències
go mod és el sistema de mòduls de Go. Gestiona les dependències del teu projecte a través del fitxer go.mod. Si vens d’altres llenguatges: go.mod és el teu pom.xml, package.json o requirements.txt.
Per a una guia completa sobre mòduls, consulta l’article de mòduls en Go. Aquí cobreixo els subcomandaments que faràs servir cada dia.
go mod init
Inicialitza un nou mòdul:
go mod init github.com/usuari/projecteAixò crea un go.mod amb el nom del mòdul i la versió de Go:
module github.com/usuari/projecte
go 1.22go mod tidy
El comandament que més faràs servir. Analitza els teus imports, afegeix les dependències que falten al go.mod, i elimina les que ja no s’usen:
go mod tidyExecuta’l després d’afegir o treure imports. A CI, una tècnica comuna és verificar que el go.mod i go.sum estiguin actualitzats:
go mod tidy
git diff --exit-code go.mod go.sumSi hi ha diferències, algú s’ha oblidat d’executar go mod tidy abans de fer push.
go mod download
Descarrega totes les dependències a la caché local sense compilar res:
go mod downloadÚtil en Dockerfiles per aprofitar la caché de capes:
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Primer copiem només go.mod i go.sum
COPY go.mod go.sum ./
RUN go mod download
# Després el codi (aquesta capa s'invalida més sovint)
COPY . .
RUN go build -o server ./cmd/servergo mod vendor
Copia totes les dependències a un directori vendor/ dins del projecte:
go mod vendorAixò permet builds sense accés a internet i garanteix reproducibilitat. Per compilar usant el vendor:
go build -mod=vendor -o server ./cmd/servergo mod graph i go mod why
Per depurar dependències:
# Veure el graf complet de dependències
go mod graph
# Saber per què una dependència és al teu go.mod
go mod why github.com/lib/pqgo mod why és especialment útil quan veus una dependència a go.sum i no saps qui l’ha introduïda.
go generate: generació de codi
go generate executa comandaments definits en comentaris especials dins del teu codi Go. No és un sistema de build ni un preprocessador: és un mecanisme per executar eines que generen codi Go.
//go:generate stringer -type=Status
//go:generate mockgen -source=repository.go -destination=mock_repository.go
//go:generate protoc --go_out=. --go-grpc_out=. api.protoPer executar tots els generadors del projecte:
go generate ./...Casos d’ús comuns
- Enums amb stringer: Genera mètodes
String()per a tipus basats eniota. - Mocks amb mockgen: Genera implementacions mock d’interfícies per a tests.
- Protocol Buffers: Genera codi Go des de fitxers
.proto. - Embeds SQL: Eines com
sqlcgeneren codi Go type-safe des de queries SQL.
Bones pràctiques amb go generate
- Fes commit del codi generat. Qui cloni el teu repo no hauria de necessitar tenir
protoc,mockgenostringerinstal·lats per compilar. - Verifica a CI que el codi generat està actualitzat:
go generate ./...
git diff --exit-codeSi hi ha diferències, algú ha modificat el codi font sense regenerar.
go install: instal·lar eines
go install compila i instal·la un binari a $GOPATH/bin (o $GOBIN si el tens definit). És la forma estàndard d’instal·lar eines escrites en Go.
# Instal·lar una eina específica amb versió
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.0
# Instal·lar des del projecte actual
go install ./cmd/serverDiferència entre go install i go build
go buildgenera el binari al directori actual (o on indiquis amb-o).go installgenera el binari a$GOPATH/bin.
Per a eines CLI que vols tenir disponibles globalment, fes servir go install. Per al teu projecte, fes servir go build.
Gestionar versions d’eines al projecte
Un patró habitual és tenir un fitxer tools.go amb un build tag que mai es compila, només perquè go mod tidy registri les dependències d’eines:
//go:build tools
package tools
import (
_ \"github.com/golangci/golangci-lint/cmd/golangci-lint\"
_ \"go.uber.org/mock/mockgen\"
_ \"golang.org/x/tools/cmd/goimports\"
)Així les versions queden fixades a go.mod i tot l’equip fa servir les mateixes.
go env: entendre el teu entorn
go env mostra totes les variables d’entorn que afecten el toolchain de Go. És el primer comandament a executar quan alguna cosa no funciona com esperes.
# Veure totes les variables
go env
# Veure una variable concreta
go env GOPATH
go env GOROOT
go env GOOS
go env GOARCH
# Veure en format JSON
go env -jsonVariables importants
| Variable | Què fa |
|---|---|
GOPATH | Directori base per a dependències i binaris instal·lats |
GOROOT | Directori d’instal·lació de Go |
GOBIN | On s’instal·len els binaris amb go install |
GOOS | Sistema operatiu objectiu per a compilació |
GOARCH | Arquitectura objectiu per a compilació |
GOPROXY | Proxy per descarregar mòduls (per defecte https://proxy.golang.org) |
GONOSUMCHECK | Mòduls que no es verifiquen al sumdb |
CGO_ENABLED | Si es permet compilar codi C (0 o 1) |
GOFLAGS | Flags que s’apliquen a tots els comandaments go |
Modificar variables de forma persistent
# Canviar el proxy (útil en entorns corporatius)
go env -w GOPROXY=https://goproxy.io,direct
# Desactivar CGO per defecte
go env -w CGO_ENABLED=0Aquestes configuracions es guarden a $GOPATH/env i persisteixen entre sessions.
Combinant comandaments a CI/CD
Un pipeline de CI per a un projecte Go típic té aquest aspecte:
#!/bin/bash
set -euo pipefail
echo \"=== Verificant format ===\"
gofmt -l . | tee /tmp/fmt-check
if [ -s /tmp/fmt-check ]; then
echo \"ERROR: fitxers sense formatar\"
exit 1
fi
echo \"=== Anàlisi estàtica ===\"
go vet ./...
echo \"=== Verificant go.mod ===\"
go mod tidy
git diff --exit-code go.mod go.sum
echo \"=== Verificant codi generat ===\"
go generate ./...
git diff --exit-code
echo \"=== Tests amb race detector ===\"
go test -race -cover -coverprofile=coverage.out ./...
echo \"=== Cobertura ===\"
go tool cover -func=coverage.out
echo \"=== Build ===\"
CGO_ENABLED=0 go build -ldflags=\"-s -w\" -o /tmp/app ./cmd/serverExemple amb GitHub Actions
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Verify formatting
run: |
if [ -n \"$(gofmt -l .)\" ]; then
echo \"Code not formatted:\"
gofmt -l .
exit 1
fi
- name: Vet
run: go vet ./...
- name: Test
run: go test -race -coverprofile=coverage.out ./...
- name: Build
run: CGO_ENABLED=0 go build -ldflags=\"-s -w\" -o app ./cmd/serverFixa’t en l’ordre: format primer (és instantani i falla ràpid), després vet, després tests, després build. Sense Maven que tarda 30 segons a arrencar, sense npm install de 200 MB. Un projecte Go mitjà passa CI en menys d’un minut.
Taula comparativa: Go tooling vs altres ecosistemes
| Tasca | Go | Java | Python | Node.js |
|---|---|---|---|---|
| Compilar | go build | mvn package / gradle build | N/A (interpretat) | N/A (interpretat) |
| Executar | go run | java -jar / mvn exec:java | python script.py | node app.js |
| Tests | go test | JUnit + Maven/Gradle | pytest / unittest | Jest / Vitest |
| Formatar | go fmt | google-java-format / Spotless | Black / YAPF | Prettier |
| Linter | go vet + golangci-lint | Checkstyle / SpotBugs | Ruff / Flake8 / Pylint | ESLint |
| Dependències | go mod | Maven / Gradle | pip / Poetry / uv | npm / pnpm / yarn |
| Cobertura | go test -cover | JaCoCo | coverage.py / pytest-cov | c8 / istanbul |
| Benchmarks | go test -bench | JMH | pytest-benchmark | Benchmark.js |
| Cross-compile | GOOS=x GOARCH=y go build | GraalVM native-image (limitat) | No natiu | No natiu (pkg) |
| Generar codi | go generate | Annotation processors | No estàndard | No estàndard |
La diferència principal: a Go tot és un únic binari amb una interfície consistent. En altres ecosistemes has d’instal·lar, configurar i mantenir eines separades per a cada tasca.
El que fa diferent el tooling de Go
El go command no és només un compilador. És una declaració sobre com hauria de funcionar el tooling d’un llenguatge. Format sense configuració. Tests sense framework extern. Compilació creuada amb dues variables d’entorn. Gestió de dependències sense fitxer de lock separat (el go.sum es genera automàticament).
Hi ha coses que no cobreix: linting avançat (necessites golangci-lint), hot reload (necessites air o similar), i gestió de releases (necessites goreleaser o scripts). Però la base que ofereix el toolchain estàndard és més completa que la de qualsevol altre llenguatge que hagi fet servir.
Si estàs començant amb Go, dedica una hora a explorar go help i els subcomandaments que hem vist. Aquesta hora t’estalviarà dies de configuració d’eines que en altres ecosistemes dones per descomptades. Si vols una guia per donar els primers passos, comença per com començar amb Go.


