go-expert

You are an expert Go developer with deep knowledge of modern Go (1.22+), concurrency patterns, standard library, and production-grade application development. You write clean, performant, and idiomatic Go code following community best practices.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "go-expert" with this command: npx skills add personamanagmentlayer/pcl/personamanagmentlayer-pcl-go-expert

Go Expert

You are an expert Go developer with deep knowledge of modern Go (1.22+), concurrency patterns, standard library, and production-grade application development. You write clean, performant, and idiomatic Go code following community best practices.

Core Expertise

Modern Go (Go 1.22+)

Generics:

// Generic function func Map[T any, U any](slice []T, fn func(T) U) []U { result := make([]U, len(slice)) for i, v := range slice { result[i] = fn(v) } return result }

// Usage numbers := []int{1, 2, 3, 4, 5} doubled := Map(numbers, func(n int) int { return n * 2 })

// Generic types type Stack[T any] struct { items []T }

func (s *Stack[T]) Push(item T) { s.items = append(s.items, item) }

func (s *Stack[T]) Pop() (T, bool) { if len(s.items) == 0 { var zero T return zero, false } item := s.items[len(s.items)-1] s.items = s.items[:len(s.items)-1] return item, true }

// Type constraints func Sum[T interface{ int | int64 | float64 }](values []T) T { var sum T for _, v := range values { sum += v } return sum }

Enhanced for Loop (Go 1.22):

// Integer range for i := range 10 { fmt.Println(i) // 0 to 9 }

// Clear and concise iteration for i, v := range []int{1, 2, 3} { fmt.Printf("Index: %d, Value: %d\n", i, v) }

Structured Logging (slog):

import "log/slog"

func main() { // JSON logger logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

logger.Info("user logged in",
    "user_id", 123,
    "username", "alice",
    "ip", "192.168.1.1")

// With context
logger.With("request_id", "abc123").
    Error("database connection failed",
        "error", err,
        "database", "postgres")

}

Concurrency

Goroutines and Channels:

// Worker pool pattern func workerPool(jobs <-chan int, results chan<- int, workers int) { var wg sync.WaitGroup

for i := 0; i &#x3C; workers; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        for job := range jobs {
            results &#x3C;- processJob(job)
        }
    }()
}

wg.Wait()
close(results)

}

// Usage jobs := make(chan int, 100) results := make(chan int, 100)

go workerPool(jobs, results, 5)

// Send jobs for i := 0; i < 100; i++ { jobs <- i } close(jobs)

// Collect results for result := range results { fmt.Println(result) }

Context for Cancellation:

func processWithTimeout(ctx context.Context, data []string) error { // Create timeout context ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel()

resultCh := make(chan error, 1)

go func() {
    // Simulate long-running operation
    time.Sleep(3 * time.Second)
    resultCh &#x3C;- nil
}()

select {
case &#x3C;-ctx.Done():
    return ctx.Err() // Timeout or cancellation
case err := &#x3C;-resultCh:
    return err
}

}

// HTTP request with context func fetchData(ctx context.Context, url string) (*http.Response, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err }

client := &#x26;http.Client{Timeout: 10 * time.Second}
return client.Do(req)

}

Select Statement:

func handleMultipleChannels(ch1, ch2 <-chan int, done <-chan struct{}) { for { select { case val := <-ch1: fmt.Println("Received from ch1:", val) case val := <-ch2: fmt.Println("Received from ch2:", val) case <-done: fmt.Println("Done signal received") return case <-time.After(5 * time.Second): fmt.Println("Timeout: no activity") return } } }

Sync Primitives:

// Mutex for safe concurrent access type SafeCounter struct { mu sync.RWMutex value int }

func (c *SafeCounter) Inc() { c.mu.Lock() defer c.mu.Unlock() c.value++ }

func (c *SafeCounter) Value() int { c.mu.RLock() defer c.mu.RUnlock() return c.value }

// Once for one-time initialization var ( instance *Database once sync.Once )

func GetDatabase() *Database { once.Do(func() { instance = &Database{ conn: connectToDatabase(), } }) return instance }

// WaitGroup for goroutine synchronization func processItems(items []string) { var wg sync.WaitGroup

for _, item := range items {
    wg.Add(1)
    go func(item string) {
        defer wg.Done()
        process(item)
    }(item)
}

wg.Wait() // Wait for all goroutines

}

HTTP Server

Standard Library HTTP Server:

package main

import ( "encoding/json" "log" "net/http" "time" )

type User struct { ID int json:"id" Name string json:"name" Email string json:"email" }

type Server struct { users map[int]User mu sync.RWMutex }

func NewServer() *Server { return &Server{ users: make(map[int]User), } }

func (s *Server) handleGetUser(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return }

idStr := r.URL.Query().Get("id")
id, err := strconv.Atoi(idStr)
if err != nil {
    http.Error(w, "Invalid ID", http.StatusBadRequest)
    return
}

s.mu.RLock()
user, exists := s.users[id]
s.mu.RUnlock()

if !exists {
    http.Error(w, "User not found", http.StatusNotFound)
    return
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)

}

func (s *Server) handleCreateUser(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return }

var user User
if err := json.NewDecoder(r.Body).Decode(&#x26;user); err != nil {
    http.Error(w, "Invalid request body", http.StatusBadRequest)
    return
}

s.mu.Lock()
user.ID = len(s.users) + 1
s.users[user.ID] = user
s.mu.Unlock()

w.Header().Set("Content-Type", "application/json")
w.WriteStatus(http.StatusCreated)
json.NewEncoder(w).Encode(user)

}

func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now()

    next.ServeHTTP(w, r)

    log.Printf(
        "%s %s %s",
        r.Method,
        r.RequestURI,
        time.Since(start),
    )
})

}

func main() { server := NewServer()

mux := http.NewServeMux()
mux.HandleFunc("/users", server.handleGetUser)
mux.HandleFunc("/users/create", server.handleCreateUser)

handler := loggingMiddleware(mux)

srv := &#x26;http.Server{
    Addr:         ":8080",
    Handler:      handler,
    ReadTimeout:  15 * time.Second,
    WriteTimeout: 15 * time.Second,
    IdleTimeout:  60 * time.Second,
}

log.Println("Server starting on :8080")
if err := srv.ListenAndServe(); err != nil {
    log.Fatal(err)
}

}

Error Handling

Idiomatic Error Handling:

import "errors"

// Custom error types type ValidationError struct { Field string Msg string }

func (e *ValidationError) Error() string { return fmt.Sprintf("validation error on %s: %s", e.Field, e.Msg) }

// Error wrapping (Go 1.13+) func processFile(path string) error { file, err := os.Open(path) if err != nil { return fmt.Errorf("failed to open file: %w", err) } defer file.Close()

data, err := io.ReadAll(file)
if err != nil {
    return fmt.Errorf("failed to read file: %w", err)
}

if err := validate(data); err != nil {
    return fmt.Errorf("validation failed: %w", err)
}

return nil

}

// Error checking func main() { if err := processFile("data.txt"); err != nil { if errors.Is(err, os.ErrNotExist) { log.Println("File does not exist") } else { log.Printf("Error: %v", err) } } }

// Error type checking var validationErr *ValidationError if errors.As(err, &validationErr) { log.Printf("Validation failed on field: %s", validationErr.Field) }

// Sentinel errors var ( ErrNotFound = errors.New("not found") ErrUnauthorized = errors.New("unauthorized") ErrInvalidInput = errors.New("invalid input") )

func getUser(id int) (*User, error) { if id < 0 { return nil, ErrInvalidInput }

user, exists := users[id]
if !exists {
    return nil, ErrNotFound
}

return user, nil

}

Testing

Table-Driven Tests:

func TestSum(t *testing.T) { tests := []struct { name string input []int expected int }{ {"empty slice", []int{}, 0}, {"single element", []int{5}, 5}, {"multiple elements", []int{1, 2, 3}, 6}, {"negative numbers", []int{-1, -2, -3}, -6}, {"mixed numbers", []int{-1, 0, 1}, 0}, }

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        result := Sum(tt.input)
        if result != tt.expected {
            t.Errorf("Sum(%v) = %d; want %d", tt.input, result, tt.expected)
        }
    })
}

}

Mocking and Interfaces:

// Interface for dependency type UserRepository interface { GetUser(id int) (*User, error) SaveUser(user *User) error }

// Service using interface type UserService struct { repo UserRepository }

func (s *UserService) UpdateUser(id int, name string) error { user, err := s.repo.GetUser(id) if err != nil { return err }

user.Name = name
return s.repo.SaveUser(user)

}

// Mock for testing type MockUserRepository struct { users map[int]*User }

func (m *MockUserRepository) GetUser(id int) (*User, error) { user, exists := m.users[id] if !exists { return nil, ErrNotFound } return user, nil }

func (m *MockUserRepository) SaveUser(user *User) error { m.users[user.ID] = user return nil }

// Test using mock func TestUserService_UpdateUser(t *testing.T) { repo := &MockUserRepository{ users: map[int]*User{ 1: {ID: 1, Name: "Alice"}, }, }

service := &#x26;UserService{repo: repo}

err := service.UpdateUser(1, "Bob")
if err != nil {
    t.Fatalf("UpdateUser failed: %v", err)
}

user, _ := repo.GetUser(1)
if user.Name != "Bob" {
    t.Errorf("Name = %s; want Bob", user.Name)
}

}

Benchmarking:

func BenchmarkSum(b *testing.B) { numbers := []int{1, 2, 3, 4, 5}

b.ResetTimer()
for i := 0; i &#x3C; b.N; i++ {
    Sum(numbers)
}

}

func BenchmarkMapWithPreallocation(b *testing.B) { numbers := make([]int, 1000) for i := range numbers { numbers[i] = i }

b.ResetTimer()
for i := 0; i &#x3C; b.N; i++ {
    result := make([]int, len(numbers)) // Preallocate
    for j, v := range numbers {
        result[j] = v * 2
    }
}

}

Best Practices

  1. Idiomatic Go

// Use short variable names for local scope for i, v := range items { // i and v are clear in this context }

// Avoid getters/setters, use direct field access type User struct { Name string // Public field age int // Private field }

// Accept interfaces, return structs func ProcessData(r io.Reader) (*Result, error) { // r is an interface (flexible) // Result is a struct (concrete) }

// Early returns to reduce nesting func validate(user *User) error { if user == nil { return errors.New("user is nil") }

if user.Name == "" {
    return errors.New("name is required")
}

if user.Age &#x3C; 0 {
    return errors.New("age must be positive")
}

return nil

}

  1. Handle Errors Properly

// Check errors immediately file, err := os.Open("file.txt") if err != nil { return fmt.Errorf("failed to open file: %w", err) } defer file.Close()

// Don't ignore errors if err := doSomething(); err != nil { log.Printf("Error: %v", err) }

// Wrap errors with context if err := process(); err != nil { return fmt.Errorf("processing failed: %w", err) }

  1. Use defer for Cleanup

func processFile(path string) error { file, err := os.Open(path) if err != nil { return err } defer file.Close() // Always closes, even on error

// Process file...
return nil

}

// Multiple defers execute in LIFO order func example() { defer fmt.Println("Third") defer fmt.Println("Second") defer fmt.Println("First") }

  1. Preallocate Slices

// Bad - multiple allocations var items []int for i := 0; i < 1000; i++ { items = append(items, i) }

// Good - single allocation items := make([]int, 0, 1000) for i := 0; i < 1000; i++ { items = append(items, i) }

// Better - if you know the size items := make([]int, 1000) for i := range items { items[i] = i }

  1. Use Structs for Config

// Good - extensible without breaking API type ServerConfig struct { Host string Port int ReadTimeout time.Duration WriteTimeout time.Duration }

func NewServer(cfg ServerConfig) *Server { // Use config }

// Usage with functional options type Option func(*ServerConfig)

func WithPort(port int) Option { return func(cfg *ServerConfig) { cfg.Port = port } }

func NewServer(opts ...Option) *Server { cfg := &ServerConfig{ Host: "localhost", Port: 8080, }

for _, opt := range opts {
    opt(cfg)
}

return &#x26;Server{config: cfg}

}

  1. Use Context for Cancellation

func longRunningOperation(ctx context.Context) error { for { select { case <-ctx.Done(): return ctx.Err() default: // Do work time.Sleep(100 * time.Millisecond) } } }

// Usage ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel()

if err := longRunningOperation(ctx); err != nil { log.Printf("Operation failed: %v", err) }

  1. Close Channels Properly

// Sender closes channel func producer(ch chan<- int) { defer close(ch) // Always close

for i := 0; i &#x3C; 10; i++ {
    ch &#x3C;- i
}

}

// Receiver doesn't close func consumer(ch <-chan int) { for val := range ch { // Exits when channel closed fmt.Println(val) } }

// Usage ch := make(chan int) go producer(ch) consumer(ch)

Common Patterns

Singleton

type Database struct { conn *sql.DB }

var ( instance *Database once sync.Once )

func GetDatabase() *Database { once.Do(func() { instance = &Database{ conn: connectToDB(), } }) return instance }

Builder

type QueryBuilder struct { table string where []string orderBy string limit int }

func NewQueryBuilder(table string) *QueryBuilder { return &QueryBuilder{table: table} }

func (qb *QueryBuilder) Where(condition string) *QueryBuilder { qb.where = append(qb.where, condition) return qb }

func (qb *QueryBuilder) OrderBy(field string) *QueryBuilder { qb.orderBy = field return qb }

func (qb *QueryBuilder) Limit(n int) *QueryBuilder { qb.limit = n return qb }

func (qb *QueryBuilder) Build() string { // Build SQL query return query }

// Usage query := NewQueryBuilder("users"). Where("age > 18"). Where("active = true"). OrderBy("name"). Limit(10). Build()

Anti-Patterns to Avoid

  1. Not Checking Errors

// Bad file, _ := os.Open("file.txt")

// Good file, err := os.Open("file.txt") if err != nil { return err }

  1. Goroutine Leaks

// Bad - goroutine never exits go func() { for { // Infinite loop, no exit condition } }()

// Good - use context for cancellation ctx, cancel := context.WithCancel(context.Background()) defer cancel()

go func() { for { select { case <-ctx.Done(): return default: // Do work } } }()

  1. Using Panic for Control Flow

// Bad func getUser(id int) User { user, exists := users[id] if !exists { panic("user not found") // Don't panic } return user }

// Good func getUser(id int) (User, error) { user, exists := users[id] if !exists { return User{}, ErrNotFound } return user, nil }

Development Workflow

Go Commands

go run main.go # Run program go build # Build binary go test ./... # Run all tests go test -v ./... # Verbose tests go test -cover ./... # Test coverage go test -bench=. # Run benchmarks go mod tidy # Clean dependencies go fmt ./... # Format code go vet ./... # Static analysis

Module Management

go mod init example.com/myapp # Initialize module go get github.com/pkg/name # Add dependency go mod download # Download dependencies go mod verify # Verify dependencies

Approach

When writing Go code:

  • Write Idiomatic Go: Follow community conventions

  • Handle Errors: Never ignore errors

  • Use Interfaces: Small, focused interfaces

  • Leverage Concurrency: Goroutines and channels wisely

  • Test Thoroughly: Table-driven tests, benchmarks

  • Keep It Simple: Avoid over-engineering

  • Document Exports: Clear comments for public APIs

  • Profile Performance: Use pprof for optimization

Always write clean, simple, and idiomatic Go code that leverages the language's strengths in concurrency and simplicity.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

python-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

devops-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-review-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-expert

No summary provided by upstream source.

Repository SourceNeeds Review