Toolchain
go version
go mod init example.com/shop
go mod tidy
go build ./...
go run ./cmd/api
go test ./... -race -cover
go vet ./...
gofmt -l .
go install ./cmd/apiTypes, slices and maps
type Order struct {
ID string
Items []Item
Tags map[string]string
}
names := make([]string, 0, 8)
names = append(names, "a")
m := map[string]int{"a": 1}
if v, ok := m["b"]; !ok { _ = v }Errors and defer
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("open config %q: %w", path, err)
}
defer f.Close()
if errors.Is(err, os.ErrNotExist) { /* handle */ }
var pe *fs.PathError
if errors.As(err, &pe) { /* inspect */ }Interfaces
type Store interface {
Get(ctx context.Context, id string) (Order, error)
}
func NewService(s Store) *Service { return &Service{store: s} }Accept interfaces, return concrete types. Keep interfaces small and define them where they are used.
Goroutines and channels
var wg sync.WaitGroup
results := make(chan int, len(jobs))
for _, j := range jobs {
wg.Add(1)
go func() {
defer wg.Done()
results <- work(j)
}()
}
wg.Wait()
close(results)Context
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
select {
case <-ctx.Done():
return ctx.Err()
case r := <-ch:
_ = r
}Pass ctx as the first parameter. Never store it in a struct.
HTTP service
mux := http.NewServeMux()
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
srv := &http.Server{Addr: ":8080", Handler: mux, ReadHeaderTimeout: 5 * time.Second}
go srv.ListenAndServe()
// on shutdown signal:
srv.Shutdown(ctx)Testing
func TestSum(t *testing.T) {
cases := []struct{ name string; a, b, want int }{
{"zero", 0, 0, 0}, {"pos", 1, 2, 3},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := Sum(c.a, c.b); got != c.want { t.Fatalf("got %d want %d", got, c.want) }
})
}
}go test -run TestSum -v ./pkg/...
go test -bench . -benchmem
go test -race ./...Build for containers
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o app ./cmd/apiFROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/api
FROM gcr.io/distroless/static-debian12
COPY --from=build /app /app
USER 65532:65532
ENTRYPOINT ["/app"]Debugging
go test -race ./...
go tool pprof -http=:0 http://localhost:6060/debug/pprof/profile
curl -s localhost:6060/debug/pprof/goroutine?debug=1 | head -30
GODEBUG=gctrace=1 ./app