Go Golang Backend Development

Go Programming: Best Practices and Advanced Patterns

R

Robert Chen

Go Programming: Best Practices and Advanced Patterns

Master Go programming with advanced patterns, performance optimization techniques, and best practices for building scalable applications.

Go has become a cornerstone of modern backend development, powering everything from microservices to distributed systems. This comprehensive guide explores advanced Go patterns, performance optimization techniques, and best practices that will help you build robust, scalable applications.

Advanced Concurrency Patterns

Worker Pool Pattern

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

// Job represents a unit of work
type Job struct {
    ID   int
    Data string
}

// Result represents the result of processing a job
type Result struct {
    Job   Job
    Value string
    Error error
}

// WorkerPool manages a pool of workers
type WorkerPool struct {
    workerCount int
    jobQueue    chan Job
    resultQueue chan Result
    quit        chan bool
    wg          sync.WaitGroup
}

// NewWorkerPool creates a new worker pool
func NewWorkerPool(workerCount, queueSize int) *WorkerPool {
    return &WorkerPool{
        workerCount: workerCount,
        jobQueue:    make(chan Job, queueSize),
        resultQueue: make(chan Result, queueSize),
        quit:        make(chan bool),
    }
}

// Start initializes and starts the worker pool
func (wp *WorkerPool) Start(ctx context.Context) {
    for i := 0; i < wp.workerCount; i++ {
        wp.wg.Add(1)
        go wp.worker(ctx, i)
    }
}

// worker processes jobs from the job queue
func (wp *WorkerPool) worker(ctx context.Context, id int) {
    defer wp.wg.Done()
    
    for {
        select {
        case job := <-wp.jobQueue:
            result := wp.processJob(job)
            select {
            case wp.resultQueue <- result:
            case <-ctx.Done():
                return
            }
        case <-ctx.Done():
            fmt.Printf("Worker %d stopping\n", id)
            return
        }
    }
}

// processJob simulates job processing
func (wp *WorkerPool) processJob(job Job) Result {
    // Simulate work
    time.Sleep(time.Millisecond * 100)
    
    return Result{
        Job:   job,
        Value: fmt.Sprintf("Processed: %s", job.Data),
        Error: nil,
    }
}

// Submit adds a job to the queue
func (wp *WorkerPool) Submit(job Job) error {
    select {
    case wp.jobQueue <- job:
        return nil
    default:
        return fmt.Errorf("job queue is full")
    }
}

// Results returns the result channel
func (wp *WorkerPool) Results() <-chan Result {
    return wp.resultQueue
}

// Stop gracefully shuts down the worker pool
func (wp *WorkerPool) Stop() {
    close(wp.jobQueue)
    wp.wg.Wait()
    close(wp.resultQueue)
}

// Usage example
func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    
    wp := NewWorkerPool(3, 10)
    wp.Start(ctx)
    
    // Submit jobs
    go func() {
        for i := 0; i < 10; i++ {
            job := Job{
                ID:   i,
                Data: fmt.Sprintf("task-%d", i),
            }
            if err := wp.Submit(job); err != nil {
                fmt.Printf("Failed to submit job: %v\n", err)
            }
        }
    }()
    
    // Collect results
    go func() {
        for result := range wp.Results() {
            if result.Error != nil {
                fmt.Printf("Job %d failed: %v\n", result.Job.ID, result.Error)
            } else {
                fmt.Printf("Job %d completed: %s\n", result.Job.ID, result.Value)
            }
        }
    }()
    
    time.Sleep(5 * time.Second)
    wp.Stop()
}

Pipeline Pattern

package main

import (
    "context"
    "fmt"
    "sync"
)

// Pipeline represents a processing pipeline
type Pipeline struct {
    stages []Stage
}

// Stage represents a single stage in the pipeline
type Stage func(ctx context.Context, input <-chan interface{}) <-chan interface{}

// NewPipeline creates a new pipeline
func NewPipeline(stages ...Stage) *Pipeline {
    return &Pipeline{stages: stages}
}

// Execute runs the pipeline
func (p *Pipeline) Execute(ctx context.Context, input <-chan interface{}) <-chan interface{} {
    current := input
    
    for _, stage := range p.stages {
        current = stage(ctx, current)
    }
    
    return current
}

// Common pipeline stages
func FilterStage(predicate func(interface{}) bool) Stage {
    return func(ctx context.Context, input <-chan interface{}) <-chan interface{} {
        output := make(chan interface{})
        
        go func() {
            defer close(output)
            for {
                select {
                case item, ok := <-input:
                    if !ok {
                        return
                    }
                    if predicate(item) {
                        select {
                        case output <- item:
                        case <-ctx.Done():
                            return
                        }
                    }
                case <-ctx.Done():
                    return
                }
            }
        }()
        
        return output
    }
}

func MapStage(transform func(interface{}) interface{}) Stage {
    return func(ctx context.Context, input <-chan interface{}) <-chan interface{} {
        output := make(chan interface{})
        
        go func() {
            defer close(output)
            for {
                select {
                case item, ok := <-input:
                    if !ok {
                        return
                    }
                    transformed := transform(item)
                    select {
                    case output <- transformed:
                    case <-ctx.Done():
                        return
                    }
                case <-ctx.Done():
                    return
                }
            }
        }()
        
        return output
    }
}

func BatchStage(batchSize int) Stage {
    return func(ctx context.Context, input <-chan interface{}) <-chan interface{} {
        output := make(chan interface{})
        
        go func() {
            defer close(output)
            batch := make([]interface{}, 0, batchSize)
            
            for {
                select {
                case item, ok := <-input:
                    if !ok {
                        if len(batch) > 0 {
                            select {
                            case output <- batch:
                            case <-ctx.Done():
                            }
                        }
                        return
                    }
                    
                    batch = append(batch, item)
                    if len(batch) == batchSize {
                        select {
                        case output <- batch:
                            batch = make([]interface{}, 0, batchSize)
                        case <-ctx.Done():
                            return
                        }
                    }
                case <-ctx.Done():
                    return
                }
            }
        }()
        
        return output
    }
}

// Fan-out/Fan-in pattern
func FanOut(ctx context.Context, input <-chan interface{}, workers int, processor func(interface{}) interface{}) <-chan interface{} {
    output := make(chan interface{})
    var wg sync.WaitGroup
    
    // Start workers
    for i := 0; i < workers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for {
                select {
                case item, ok := <-input:
                    if !ok {
                        return
                    }
                    result := processor(item)
                    select {
                    case output <- result:
                    case <-ctx.Done():
                        return
                    }
                case <-ctx.Done():
                    return
                }
            }
        }()
    }
    
    // Close output when all workers are done
    go func() {
        wg.Wait()
        close(output)
    }()
    
    return output
}

// Usage example
func main() {
    ctx := context.Background()
    
    // Create input data
    input := make(chan interface{}, 10)
    go func() {
        defer close(input)
        for i := 1; i <= 20; i++ {
            input <- i
        }
    }()
    
    // Create pipeline
    pipeline := NewPipeline(
        FilterStage(func(item interface{}) bool {
            return item.(int)%2 == 0 // Only even numbers
        }),
        MapStage(func(item interface{}) interface{} {
            return item.(int) * 2 // Double the value
        }),
        BatchStage(3), // Group into batches of 3
    )
    
    // Execute pipeline
    output := pipeline.Execute(ctx, input)
    
    // Process results
    for result := range output {
        fmt.Printf("Batch: %v\n", result)
    }
}

Error Handling Patterns

Custom Error Types

package main

import (
    "errors"
    "fmt"
    "net/http"
)

// ErrorCode represents different types of errors
type ErrorCode string

const (
    ErrorCodeValidation   ErrorCode = "VALIDATION_ERROR"
    ErrorCodeNotFound     ErrorCode = "NOT_FOUND"
    ErrorCodeUnauthorized ErrorCode = "UNAUTHORIZED"
    ErrorCodeInternal     ErrorCode = "INTERNAL_ERROR"
    ErrorCodeTimeout      ErrorCode = "TIMEOUT_ERROR"
)

// AppError represents an application-specific error
type AppError struct {
    Code       ErrorCode `json:"code"`
    Message    string    `json:"message"`
    Details    string    `json:"details,omitempty"`
    Cause      error     `json:"-"`
    HTTPStatus int       `json:"-"`
}

func (e *AppError) Error() string {
    if e.Cause != nil {
        return fmt.Sprintf("%s: %s (caused by: %v)", e.Code, e.Message, e.Cause)
    }
    return fmt.Sprintf("%s: %s", e.Code, e.Message)
}

func (e *AppError) Unwrap() error {
    return e.Cause
}

// Error constructors
func NewValidationError(message string, details string) *AppError {
    return &AppError{
        Code:       ErrorCodeValidation,
        Message:    message,
        Details:    details,
        HTTPStatus: http.StatusBadRequest,
    }
}

func NewNotFoundError(resource string) *AppError {
    return &AppError{
        Code:       ErrorCodeNotFound,
        Message:    fmt.Sprintf("%s not found", resource),
        HTTPStatus: http.StatusNotFound,
    }
}

func NewInternalError(message string, cause error) *AppError {
    return &AppError{
        Code:       ErrorCodeInternal,
        Message:    message,
        Cause:      cause,
        HTTPStatus: http.StatusInternalServerError,
    }
}

// Error wrapping utilities
func WrapError(err error, code ErrorCode, message string) *AppError {
    return &AppError{
        Code:    code,
        Message: message,
        Cause:   err,
    }
}

// Result type for better error handling
type Result[T any] struct {
    Value T
    Error error
}

func Ok[T any](value T) Result[T] {
    return Result[T]{Value: value}
}

func Err[T any](err error) Result[T] {
    var zero T
    return Result[T]{Value: zero, Error: err}
}

func (r Result[T]) IsOk() bool {
    return r.Error == nil
}

func (r Result[T]) IsErr() bool {
    return r.Error != nil
}

func (r Result[T]) Unwrap() (T, error) {
    return r.Value, r.Error
}

// Service layer with proper error handling
type UserService struct {
    repo UserRepository
}

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

type UserRepository interface {
    GetByID(id int) (*User, error)
    Create(user *User) error
    Update(user *User) error
}

func (s *UserService) GetUser(id int) Result[*User] {
    if id <= 0 {
        return Err[*User](NewValidationError("Invalid user ID", "ID must be positive"))
    }
    
    user, err := s.repo.GetByID(id)
    if err != nil {
        if errors.Is(err, ErrUserNotFound) {
            return Err[*User](NewNotFoundError("User"))
        }
        return Err[*User](NewInternalError("Failed to get user", err))
    }
    
    return Ok(user)
}

func (s *UserService) CreateUser(user *User) Result[*User] {
    if err := s.validateUser(user); err != nil {
        return Err[*User](err)
    }
    
    if err := s.repo.Create(user); err != nil {
        return Err[*User](NewInternalError("Failed to create user", err))
    }
    
    return Ok(user)
}

func (s *UserService) validateUser(user *User) error {
    if user.Name == "" {
        return NewValidationError("Name is required", "User name cannot be empty")
    }
    
    if user.Email == "" {
        return NewValidationError("Email is required", "User email cannot be empty")
    }
    
    // Email format validation
    if !isValidEmail(user.Email) {
        return NewValidationError("Invalid email format", "Please provide a valid email address")
    }
    
    return nil
}

func isValidEmail(email string) bool {
    // Simplified email validation
    return len(email) > 0 && fmt.Sprintf("%s", email) != ""
}

var ErrUserNotFound = errors.New("user not found")

// HTTP handler with proper error handling
func (s *UserService) HandleGetUser(w http.ResponseWriter, r *http.Request) {
    // Extract user ID from URL (simplified)
    userID := 1 // This would come from URL parameters
    
    result := s.GetUser(userID)
    if result.IsErr() {
        s.handleError(w, result.Error)
        return
    }
    
    user := result.Value
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    // JSON encoding would go here
    fmt.Fprintf(w, `{"id": %d, "name": "%s", "email": "%s"}`, user.ID, user.Name, user.Email)
}

func (s *UserService) handleError(w http.ResponseWriter, err error) {
    var appErr *AppError
    if errors.As(err, &appErr) {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(appErr.HTTPStatus)
        fmt.Fprintf(w, `{"error": {"code": "%s", "message": "%s"}}`, appErr.Code, appErr.Message)
    } else {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Fprintf(w, `{"error": {"code": "INTERNAL_ERROR", "message": "Internal server error"}}`)
    }
}

Performance Optimization

Memory Management and Object Pooling

package main

import (
    "bytes"
    "fmt"
    "runtime"
    "sync"
    "time"
)

// Object pool for expensive-to-create objects
type BufferPool struct {
    pool sync.Pool
}

func NewBufferPool() *BufferPool {
    return &BufferPool{
        pool: sync.Pool{
            New: func() interface{} {
                return &bytes.Buffer{}
            },
        },
    }
}

func (bp *BufferPool) Get() *bytes.Buffer {
    return bp.pool.Get().(*bytes.Buffer)
}

func (bp *BufferPool) Put(buf *bytes.Buffer) {
    buf.Reset() // Clear the buffer before returning to pool
    bp.pool.Put(buf)
}

// Worker pool with object reuse
type Task struct {
    ID   int
    Data []byte
}

type ProcessingResult struct {
    TaskID int
    Output string
    Error  error
}

type OptimizedProcessor struct {
    bufferPool *BufferPool
    workers    int
    taskChan   chan Task
    resultChan chan ProcessingResult
    wg         sync.WaitGroup
}

func NewOptimizedProcessor(workers int) *OptimizedProcessor {
    return &OptimizedProcessor{
        bufferPool: NewBufferPool(),
        workers:    workers,
        taskChan:   make(chan Task, workers*2),
        resultChan: make(chan ProcessingResult, workers*2),
    }
}

func (op *OptimizedProcessor) Start() {
    for i := 0; i < op.workers; i++ {
        op.wg.Add(1)
        go op.worker()
    }
}

func (op *OptimizedProcessor) worker() {
    defer op.wg.Done()
    
    for task := range op.taskChan {
        result := op.processTask(task)
        op.resultChan <- result
    }
}

func (op *OptimizedProcessor) processTask(task Task) ProcessingResult {
    // Get buffer from pool
    buf := op.bufferPool.Get()
    defer op.bufferPool.Put(buf) // Return to pool when done
    
    // Process data using the pooled buffer
    buf.Write(task.Data)
    buf.WriteString(fmt.Sprintf(" - processed by task %d", task.ID))
    
    return ProcessingResult{
        TaskID: task.ID,
        Output: buf.String(),
        Error:  nil,
    }
}

func (op *OptimizedProcessor) Submit(task Task) {
    op.taskChan <- task
}

func (op *OptimizedProcessor) Results() <-chan ProcessingResult {
    return op.resultChan
}

func (op *OptimizedProcessor) Stop() {
    close(op.taskChan)
    op.wg.Wait()
    close(op.resultChan)
}

// Memory-efficient string builder
type StringBuilder struct {
    buf []byte
}

func NewStringBuilder(capacity int) *StringBuilder {
    return &StringBuilder{
        buf: make([]byte, 0, capacity),
    }
}

func (sb *StringBuilder) WriteString(s string) {
    sb.buf = append(sb.buf, s...)
}

func (sb *StringBuilder) WriteByte(b byte) {
    sb.buf = append(sb.buf, b)
}

func (sb *StringBuilder) String() string {
    return string(sb.buf)
}

func (sb *StringBuilder) Reset() {
    sb.buf = sb.buf[:0]
}

func (sb *StringBuilder) Len() int {
    return len(sb.buf)
}

// Benchmark comparison
func BenchmarkStringConcatenation() {
    const iterations = 10000
    
    // Inefficient string concatenation
    start := time.Now()
    result := ""
    for i := 0; i < iterations; i++ {
        result += fmt.Sprintf("item-%d ", i)
    }
    inefficientTime := time.Since(start)
    
    // Efficient string building
    start = time.Now()
    sb := NewStringBuilder(iterations * 10) // Pre-allocate capacity
    for i := 0; i < iterations; i++ {
        sb.WriteString(fmt.Sprintf("item-%d ", i))
    }
    efficientResult := sb.String()
    efficientTime := time.Since(start)
    
    fmt.Printf("Inefficient concatenation: %v\n", inefficientTime)
    fmt.Printf("Efficient string builder: %v\n", efficientTime)
    fmt.Printf("Speedup: %.2fx\n", float64(inefficientTime)/float64(efficientTime))
    
    // Verify results are the same
    if result == efficientResult {
        fmt.Println("Results match!")
    }
}

// Memory monitoring utilities
func PrintMemStats() {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    
    fmt.Printf("Alloc = %d KB", bToKb(m.Alloc))
    fmt.Printf("TotalAlloc = %d KB", bToKb(m.TotalAlloc))
    fmt.Printf("Sys = %d KB", bToKb(m.Sys))
    fmt.Printf("NumGC = %d\n", m.NumGC)
}

func bToKb(b uint64) uint64 {
    return b / 1024
}

func main() {
    fmt.Println("=== Object Pool Demo ===")
    
    processor := NewOptimizedProcessor(3)
    processor.Start()
    
    // Submit tasks
    go func() {
        for i := 0; i < 10; i++ {
            task := Task{
                ID:   i,
                Data: []byte(fmt.Sprintf("data-%d", i)),
            }
            processor.Submit(task)
        }
        processor.Stop()
    }()
    
    // Collect results
    for result := range processor.Results() {
        fmt.Printf("Task %d: %s\n", result.TaskID, result.Output)
    }
    
    fmt.Println("\n=== String Building Benchmark ===")
    BenchmarkStringConcatenation()
    
    fmt.Println("\n=== Memory Stats ===")
    PrintMemStats()
    
    // Force garbage collection
    runtime.GC()
    fmt.Println("After GC:")
    PrintMemStats()
}

Testing Patterns

Table-Driven Tests and Test Utilities

package main

import (
    "context"
    "errors"
    "testing"
    "time"
)

// Calculator service for testing
type Calculator struct{}

func (c *Calculator) Add(a, b int) int {
    return a + b
}

func (c *Calculator) Divide(a, b int) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return float64(a) / float64(b), nil
}

func (c *Calculator) IsEven(n int) bool {
    return n%2 == 0
}

// Table-driven tests
func TestCalculator_Add(t *testing.T) {
    calc := &Calculator{}
    
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive numbers", 2, 3, 5},
        {"negative numbers", -2, -3, -5},
        {"mixed signs", -2, 3, 1},
        {"zero values", 0, 0, 0},
        {"large numbers", 1000000, 2000000, 3000000},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := calc.Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

func TestCalculator_Divide(t *testing.T) {
    calc := &Calculator{}
    
    tests := []struct {
        name        string
        a, b        int
        expected    float64
        expectError bool
    }{
        {"normal division", 10, 2, 5.0, false},
        {"division by zero", 10, 0, 0, true},
        {"negative result", -10, 2, -5.0, false},
        {"fractional result", 1, 3, 0.3333333333333333, false},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result, err := calc.Divide(tt.a, tt.b)
            
            if tt.expectError {
                if err == nil {
                    t.Errorf("Divide(%d, %d) expected error but got none", tt.a, tt.b)
                }
                return
            }
            
            if err != nil {
                t.Errorf("Divide(%d, %d) unexpected error: %v", tt.a, tt.b, err)
                return
            }
            
            if result != tt.expected {
                t.Errorf("Divide(%d, %d) = %f; want %f", tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

// Benchmark tests
func BenchmarkCalculator_Add(b *testing.B) {
    calc := &Calculator{}
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        calc.Add(i, i+1)
    }
}

func BenchmarkCalculator_IsEven(b *testing.B) {
    calc := &Calculator{}
    
    tests := []struct {
        name string
        n    int
    }{
        {"small even", 2},
        {"small odd", 3},
        {"large even", 1000000},
        {"large odd", 1000001},
    }
    
    for _, tt := range tests {
        b.Run(tt.name, func(b *testing.B) {
            for i := 0; i < b.N; i++ {
                calc.IsEven(tt.n)
            }
        })
    }
}

// Test utilities and helpers
type TestHelper struct {
    t *testing.T
}

func NewTestHelper(t *testing.T) *TestHelper {
    return &TestHelper{t: t}
}

func (h *TestHelper) AssertEqual(got, want interface{}) {
    h.t.Helper()
    if got != want {
        h.t.Errorf("got %v; want %v", got, want)
    }
}

func (h *TestHelper) AssertError(err error, expectError bool) {
    h.t.Helper()
    if expectError && err == nil {
        h.t.Error("expected error but got none")
    }
    if !expectError && err != nil {
        h.t.Errorf("unexpected error: %v", err)
    }
}

func (h *TestHelper) AssertNoError(err error) {
    h.t.Helper()
    if err != nil {
        h.t.Errorf("unexpected error: %v", err)
    }
}

// Mock and stub utilities
type MockUserRepository struct {
    users map[int]*User
    err   error
}

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

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

func (m *MockUserRepository) Create(user *User) error {
    if m.err != nil {
        return m.err
    }
    
    m.users[user.ID] = user
    return nil
}

func (m *MockUserRepository) Update(user *User) error {
    if m.err != nil {
        return m.err
    }
    
    m.users[user.ID] = user
    return nil
}

func (m *MockUserRepository) SetError(err error) {
    m.err = err
}

func (m *MockUserRepository) AddUser(user *User) {
    m.users[user.ID] = user
}

// Integration test utilities
func TestWithTimeout(t *testing.T, timeout time.Duration, testFunc func(t *testing.T)) {
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()
    
    done := make(chan bool)
    go func() {
        testFunc(t)
        done <- true
    }()
    
    select {
    case <-done:
        // Test completed successfully
    case <-ctx.Done():
        t.Fatal("test timed out")
    }
}

// Example usage of test utilities
func TestUserService_GetUser(t *testing.T) {
    helper := NewTestHelper(t)
    mockRepo := NewMockUserRepository()
    service := &UserService{repo: mockRepo}
    
    // Test data
    testUser := &User{
        ID:    1,
        Name:  "John Doe",
        Email: "john@example.com",
    }
    mockRepo.AddUser(testUser)
    
    tests := []struct {
        name        string
        userID      int
        setupMock   func(*MockUserRepository)
        expectError bool
        errorCode   ErrorCode
    }{
        {
            name:        "existing user",
            userID:      1,
            setupMock:   func(m *MockUserRepository) {},
            expectError: false,
        },
        {
            name:        "non-existent user",
            userID:      999,
            setupMock:   func(m *MockUserRepository) {},
            expectError: true,
            errorCode:   ErrorCodeNotFound,
        },
        {
            name:   "repository error",
            userID: 1,
            setupMock: func(m *MockUserRepository) {
                m.SetError(errors.New("database error"))
            },
            expectError: true,
            errorCode:   ErrorCodeInternal,
        },
        {
            name:        "invalid user ID",
            userID:      -1,
            setupMock:   func(m *MockUserRepository) {},
            expectError: true,
            errorCode:   ErrorCodeValidation,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // Reset mock state
            mockRepo.SetError(nil)
            tt.setupMock(mockRepo)
            
            result := service.GetUser(tt.userID)
            
            if tt.expectError {
                helper.AssertError(result.Error, true)
                
                var appErr *AppError
                if errors.As(result.Error, &appErr) {
                    helper.AssertEqual(appErr.Code, tt.errorCode)
                }
            } else {
                helper.AssertNoError(result.Error)
                helper.AssertEqual(result.Value.ID, testUser.ID)
                helper.AssertEqual(result.Value.Name, testUser.Name)
            }
        })
    }
}

// Example of integration test with timeout
func TestUserService_Integration(t *testing.T) {
    TestWithTimeout(t, 5*time.Second, func(t *testing.T) {
        // This would be an actual integration test
        // that might involve real database connections, etc.
        time.Sleep(100 * time.Millisecond) // Simulate work
        
        if testing.Short() {
            t.Skip("skipping integration test in short mode")
        }
        
        // Integration test logic here
    })
}

Conclusion

Go’s strength lies in its simplicity and powerful concurrency primitives. The patterns and practices covered in this guide provide a foundation for building robust, scalable applications:

  1. Concurrency patterns like worker pools and pipelines enable efficient parallel processing
  2. Structured error handling with custom error types improves debugging and user experience
  3. Performance optimization through object pooling and memory management reduces resource usage
  4. Comprehensive testing with table-driven tests and mocks ensures code reliability

The key to effective Go programming is embracing its philosophy of simplicity while leveraging its powerful features appropriately. Start with simple, clear code and add complexity only when needed to solve specific problems.

Remember that Go’s greatest strength is its ability to handle concurrent operations safely and efficiently. Use these patterns to build systems that can scale with your needs while maintaining code clarity and reliability.