| name | golang-patterns |
| description | Idiomatic Go patterns and conventions for robust, efficient applications. Use when writing Go code, structuring Go projects, applying Go best practices, or reviewing Go architecture decisions. |
| origin | MCC |
Go Development Patterns
Idiomatic Go patterns and best practices for building robust, efficient, and maintainable applications.
When to Activate
- Writing new Go code
- Reviewing Go code
- Refactoring existing Go code
- Designing Go packages/modules
Core Principles
1. Simplicity and Clarity
func GetUser(id string) (*User, error) {
user, err := db.FindUser(id)
if err != nil {
return nil, fmt.Errorf("get user %s: %w", id, err)
}
return user, nil
}
2. Make the Zero Value Useful
type Counter struct {
mu sync.Mutex
count int
}
func (c *Counter) Inc() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
3. Accept Interfaces, Return Structs
func ProcessData(r io.Reader) (*Result, error) {
data, err := io.ReadAll(r)
if err != nil {
return nil, err
}
return &Result{Data: data}, nil
}
Error Handling Patterns
Error Wrapping with Context
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("load config %s: %w", path, err)
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parse config %s: %w", path, err)
}
return &cfg, nil
}
Custom Error Types
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}
var (
ErrNotFound = errors.New("resource not found")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidInput = errors.New("invalid input")
)
Error Checking with errors.Is and errors.As
func HandleError(err error) {
if errors.Is(err, sql.ErrNoRows) {
log.Println("No records found")
return
}
var validationErr *ValidationError
if errors.As(err, &validationErr) {
log.Printf("Validation error on field %s: %s",
validationErr.Field, validationErr.Message)
return
}
log.Printf("Unexpected error: %v", err)
}
Never Ignore Errors
result, err := doSomething()
if err != nil {
return err
}
Package Organization
Standard Project Layout
myproject/
+-- cmd/
| +-- myapp/
| +-- main.go # Entry point
+-- internal/
| +-- handler/ # HTTP handlers
| +-- service/ # Business logic
| +-- repository/ # Data access
| +-- config/ # Configuration
+-- pkg/
| +-- client/ # Public API client
+-- api/
| +-- v1/ # API definitions (proto, OpenAPI)
+-- testdata/ # Test fixtures
+-- go.mod
+-- go.sum
+-- Makefile
Package Naming
package http
package json
package user
package httpHandler
package json_parser
package userService
Avoid Package-Level State
var db *sql.DB
type Server struct {
db *sql.DB
}
func NewServer(db *sql.DB) *Server {
return &Server{db: db}
}
Go Tooling Integration
Essential Commands
go build ./...
go run ./cmd/myapp
go test ./...
go test -race ./...
go test -cover ./...
go vet ./...
staticcheck ./...
golangci-lint run
go mod tidy
go mod verify
gofmt -w .
goimports -w .
Recommended Linter Configuration (.golangci.yml)
linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gofmt
- goimports
- misspell
- unconvert
- unparam
linters-settings:
errcheck:
check-type-assertions: true
govet:
check-shadowing: true
issues:
exclude-use-default: false
Quick Reference: Go Idioms
| Idiom | Description |
|---|
| Accept interfaces, return structs | Functions accept interface params, return concrete types |
| Errors are values | Treat errors as first-class values, not exceptions |
| Don't communicate by sharing memory | Use channels for coordination between goroutines |
| Make the zero value useful | Types should work without explicit initialization |
| A little copying is better than a little dependency | Avoid unnecessary external dependencies |
| Clear is better than clever | Prioritize readability over cleverness |
| gofmt is no one's favorite but everyone's friend | Always format with gofmt/goimports |
| Return early | Handle errors first, keep happy path unindented |
Anti-Patterns to Avoid
func ProcessRequest(ctx context.Context, id string) error { ... }
Remember: Go code should be boring in the best way - predictable, consistent, and easy to understand. When in doubt, keep it simple.
Reference Files
- concurrency-patterns.md — Worker pools, context for cancellation/timeouts, graceful shutdown, errgroup, and avoiding goroutine leaks
- interfaces-structs-performance.md — Small focused interfaces, functional options pattern, embedding for composition, slice preallocation, sync.Pool, and string building