Go Guide
Applies to: Go 1.21+, Microservices, APIs, CLIs
Core Principles
- •Simplicity: Prefer simple, readable code over clever solutions
- •Concurrency: Use goroutines and channels for concurrent operations
- •Errors Are Values: Explicit error handling, no exceptions
- •Composition Over Inheritance: Interfaces and struct embedding
- •Standard Library First: Rich stdlib, minimize dependencies
Guardrails
Version & Dependencies
- •Use Go 1.21+ with Go modules (
go.mod) - •Run
go mod tidybefore committing - •Pin major versions in
go.mod
Code Style
- •Run
gofmt/goimportsbefore every commit - •Run
go vetandgolangci-lintbefore committing - •Package names: lowercase, no underscores (
userservicenotuser_service) - •Exported:
PascalCase| Unexported:camelCase - •Follow Effective Go guidelines
Error Handling
- •Always check errors:
if err != nil { return err } - •Return errors, don't panic (panic only for unrecoverable)
- •Wrap errors with context:
fmt.Errorf("fetch user: %w", err) - •Use custom error types for domain errors
- •Don't ignore errors with
_unless justified with comment
Concurrency
- •Use
context.Contextfor cancellation and timeouts - •Always set timeout for HTTP requests
- •Use
sync.WaitGroupfor goroutine coordination - •Close channels from sender side only
- •Use
selectwithdefaultto avoid blocking
Interfaces
- •Accept interfaces, return structs
- •Define interfaces where they're used (not where implemented)
- •Keep interfaces small (1-3 methods ideal)
- •Use
io.Reader,io.Writerfrom stdlib when applicable
Project Structure
code
myproject/ ├── cmd/ # Main applications │ └── api/ │ └── main.go # Entry point ├── internal/ # Private application code │ ├── domain/ # Business logic │ ├── service/ # Application services │ ├── repository/ # Data access │ └── http/ # HTTP handlers ├── pkg/ # Public libraries (reusable) ├── api/ # OpenAPI/Protobuf specs ├── go.mod ├── go.sum └── README.md
- •
internal/for private code (not importable by other projects) - •
pkg/only for truly reusable libraries - •
cmd/for executables (one per subdirectory) - •No global variables (use dependency injection)
Error Handling Patterns
Basic Pattern
go
func GetUser(id string) (*User, error) {
user, err := db.FindUserByID(id)
if err != nil {
return nil, fmt.Errorf("get user %s: %w", id, err)
}
return user, nil
}
Custom Errors
go
type NotFoundError struct {
Resource string
ID string
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("%s with ID %s not found", e.Resource, e.ID)
}
// Check with errors.As
var notFound *NotFoundError
if errors.As(err, ¬Found) {
// Handle not found
}
Testing
Standards
- •Test files:
*_test.go(same package) - •Test functions:
func TestFunctionName(t *testing.T) - •Table-driven tests for multiple cases
- •Use
t.Helper()in test helpers - •Use subtests:
t.Run("name", func(t *testing.T) {...}) - •Coverage target: >80% for business logic
- •Benchmark critical paths:
func BenchmarkFunction(b *testing.B)
Table-Driven Tests
go
func TestCalculate(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
wantErr bool
}{
{"positive numbers", 2, 3, 5, false},
{"negative numbers", -2, -3, -5, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := Calculate(tt.a, tt.b)
if tt.wantErr {
if err == nil {
t.Error("expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != tt.expected {
t.Errorf("got %d, want %d", result, tt.expected)
}
})
}
}
Tooling
Essential Commands
bash
go fmt ./... # Format code go vet ./... # Detect suspicious constructs go test ./... # Run all tests go test -cover ./... # With coverage go test -race ./... # Race detector go build ./cmd/api # Build go mod tidy # Clean dependencies golangci-lint run # Comprehensive lint
Linter Configuration
yaml
# .golangci.yml
linters:
enable:
- gofmt
- govet
- staticcheck
- ineffassign
- misspell
- gosec # Security
- errcheck # Unchecked errors
- gocyclo # Cyclomatic complexity
- dupl # Code duplication
linters-settings:
gocyclo:
min-complexity: 10
dupl:
threshold: 100
Advanced Topics
For detailed patterns and examples, see:
- •references/patterns.md — HTTP server, database, middleware, concurrency patterns
- •references/pitfalls.md — Common do/don't examples
- •references/security.md — Security best practices and examples