← Back to list

go-patterns
by 5dlabs
Cognitive Task Orchestrator - GitOps on Bare Metal or Cloud for AI Agents
⭐ 2🍴 1📅 Jan 25, 2026
SKILL.md
name: go-patterns description: Go backend patterns including chi router, gRPC, pgx, context handling, and concurrency. agents: [grizz] triggers: [go, golang, chi, grpc, pgx]
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)
Score
Total Score
65/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
✓最近の活動
1ヶ月以内に更新
+10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
✓タグ
1つ以上のタグが設定されている
+5
Reviews
💬
Reviews coming soon


