go-patterns
Go backend patterns including chi router, gRPC, pgx, context handling, and concurrency.
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
Go Backend Patterns
Production Go patterns for backend services with focus on concurrency, error handling, and idiomatic Go.
Core Stack
| Component | Library | Purpose |
|---|---|---|
| Language | Go 1.22+ | Backend development |
| Build | go build, go test | Compilation and testing |
| Linting | golangci-lint | Code quality |
| HTTP | chi router | RESTful APIs |
| RPC | grpc-go | Service communication |
| Database | pgx, sqlc | PostgreSQL |
| Cache | redis-go | Caching |
| Testing | testify, gomock | Test framework and mocking |
| Observability | OpenTelemetry | Tracing and metrics |
Context7 Library IDs
Query these libraries for current best practices:
-
Chi Router:
/go-chi/chi -
pgx:
/jackc/pgx -
sqlc:
/sqlc-dev/sqlc -
testify:
/stretchr/testify -
OpenTelemetry Go:
/open-telemetry/opentelemetry-go
Execution Rules
- golangci-lint always. Run linting before commits
- No naked returns. Always name return values in complex functions
- Error handling. Wrap errors with context, don't discard them
- Documentation. GoDoc comments on all exported items
-
Tests. Table-driven tests in
_test.gofiles
Error Handling
Wrapping Errors with Context
import (
"fmt"
"errors"
)
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
}
Checking Specific Errors
if errors.Is(err, ErrNotFound) {
return nil, status.Error(codes.NotFound, "user not found")
}
Custom Error Types
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}
Context Usage
Timeouts and Cancellation
func FetchData(ctx context.Context, url string) ([]byte, error) {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("execute request: %w", err)
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
Context Values (Use Sparingly)
type contextKey string
const userIDKey contextKey = "userID"
func WithUserID(ctx context.Context, userID string) context.Context {
return context.WithValue(ctx, userIDKey, userID)
}
func UserIDFromContext(ctx context.Context) (string, bool) {
userID, ok := ctx.Value(userIDKey).(string)
return userID, ok
}
Concurrency Patterns
Graceful Goroutine Management
func worker(ctx context.Context, jobs <-chan Job) {
for {
select {
case <-ctx.Done():
return
case job, ok := <-jobs:
if !ok {
return
}
process(job)
}
}
}
Worker Pool
func StartWorkerPool(ctx context.Context, jobs <-chan Job, numWorkers int) {
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
worker(ctx, jobs)
}()
}
wg.Wait()
}
Mutex for Shared State
type SafeCounter struct {
mu sync.RWMutex
count int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *SafeCounter) Value() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.count
}
Structured Logging
import (
"log/slog"
"context"
)
func ProcessRequest(ctx context.Context, id string) {
logger := slog.With("request_id", id)
logger.InfoContext(ctx, "processing request")
// On error
logger.ErrorContext(ctx, "failed to process",
"error", err,
"user_id", userID,
)
}
Chi Router Patterns
import "github.com/go-chi/chi/v5"
func NewRouter() chi.Router {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(30 * time.Second))
r.Route("/api/v1", func(r chi.Router) {
r.Get("/users/{id}", GetUser)
r.Post("/users", CreateUser)
})
return r
}
Table-Driven Tests
func TestGetUser(t *testing.T) {
tests := []struct {
name string
userID string
want *User
wantErr bool
}{
{
name: "valid user",
userID: "123",
want: &User{ID: "123", Name: "Test"},
},
{
name: "invalid user",
userID: "999",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetUser(tt.userID)
if (err != nil) != tt.wantErr {
t.Errorf("GetUser() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetUser() = %v, want %v", got, tt.want)
}
})
}
}
Validation Commands
go fmt ./...
go vet ./...
golangci-lint run ./...
go test ./... -race -v
go build ./...
Guidelines
- Keep functions small and focused
- Use interfaces for abstraction
- Handle errors explicitly, don't panic
- Prefer composition over inheritance
- Document exported functions and types
- Use context for cancellation and timeouts
- Leverage goroutines appropriately (not excessively)
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-01-20
- Mise à jour de l’index
- 2026-03-03
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!