← Back to Cecep Azhar
Cecep Azhar #golang#error-handling#best-practices#patterns#recovery

Go Error Handling: Best Practices yang Perlu Diketahui

Cecep Saeful Azhar • 16 Oct 2026
Go Error Handling: Best Practices yang Perlu Diketahui
Best practices error handling di Go. Custom errors, error wrapping, sentinel errors, dan patterns production-ready.

Error Handling di Go: Love It or Hate It

Go's explicit error handling memang kontroversial. Banyak developer mengeluh tentang if err != nil yang berulang. Tapi setelah beberapa tahun menggunakan Go, saya malah menghargai approach ini. Explicit error handling membuat code lebih predictable dan lebih mudah di-debug.

Dasar Error Interface

type error interface {
    Error() string
}

func main() {
    data, err := fetchData()
    if err != nil {
        log.Printf("Error fetching data: %v", err)
        return err
    }
}

Error Wrapping: Context is King

Salah satu best practices yang saya terapkan selalu adalah error wrapping. Error tanpa context sulit di-debug di production.

// Bad: error tanpa context
func GetUser(id string) (*User, error) {
    var user User
    err := db.First(&user, id).Error
    return &user, err
}

// Good: error dengan context
func GetUser(id string) (*User, error) {
    var user User
    if err := db.First(&user, id).Error; err != nil {
        return nil, fmt.Errorf("get user %s: %w", id, err)
    }
    return &user, nil
}

// Unwrap untuk cek error type
if errors.Is(err, gorm.ErrRecordNotFound) {
    return nil, ErrUserNotFound
}

Sentinel Errors: Konsisten dan Testable

var (
    ErrUserNotFound     = errors.New("user not found")
    ErrUnauthorized     = errors.New("unauthorized")
    ErrDuplicateEmail   = errors.New("email already exists")
    ErrInsufficientPlan = errors.New("plan upgrade required")
)

func handleGetUser(c *fiber.Ctx) error {
    user, err := svc.GetUser(id)
    if err != nil {
        switch {
        case errors.Is(err, ErrUserNotFound):
            return c.Status(404).JSON(fiber.Map{"error": err.Error()})
        case errors.Is(err, ErrUnauthorized):
            return c.Status(403).JSON(fiber.Map{"error": err.Error()})
        default:
            return c.Status(500).JSON(fiber.Map{"error": "internal server error"})
        }
    }
    return c.JSON(user)
}

Error Handling Anti-Patterns

Yang saya pelajari: error handling di Go memang verbose, tapi ini menjadikan codebase lebih maintainable dalam jangka panjang. Dan dengan Go 1.13+ error wrapping, tracing error menjadi jauh lebih mudah.

Custom Error Types dengan Stack Trace

Untuk debugging di production, stack trace sangat membantu. Saya biasanya buat custom error type yang menyimpan stack trace:

import "runtime/debug"

type AppError struct {
    Code       string
    Message    string
    StackTrace string
    Cause      error
}

func (e *AppError) Error() string {
    return fmt.Sprintf("[%s] %s", e.Code, e.Message)
}

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

func NewAppError(code, message string, cause error) *AppError {
    return &AppError{
        Code:       code,
        Message:    message,
        StackTrace: string(debug.Stack()),
        Cause:      cause,
    }
}

Dengan approach ini, setiap error yang terjadi di production bisa langsung di-trace ke baris kode yang menyebabkannya. Sangat membantu saat debugging issue di production.

Graceful Degradation Pattern

Tidak semua error harus menghentikan seluruh request. Kadang lebih baik degrade gracefully. Misalnya, jika cache service down, fallback ke database langsung:

func GetUserProfile(userID string) (*Profile, error) {
    // Try cache first
    profile, err := cache.Get("profile:" + userID)
    if err == nil {
        return profile, nil
    }
    // Fallback to database
    profile, err = db.GetProfile(userID)
    if err != nil {
        return nil, fmt.Errorf("get profile %s: %w", userID, err)
    }
    // Update cache asynchronously
    go cache.Set("profile:"+userID, profile, time.Minute*5)
    return profile, nil
}

Pattern ini membuat aplikasi lebih resilient terhadap partial failures.

← More articles from Cecep Azhar Central Content Hub Home →