Skip to content

Instantly share code, notes, and snippets.

@ydm
Created July 14, 2025 07:19
Show Gist options
  • Save ydm/697ffde82c6df333969c5b67db01db31 to your computer and use it in GitHub Desktop.
Save ydm/697ffde82c6df333969c5b67db01db31 to your computer and use it in GitHub Desktop.

Best Practices Summary

  • Always wrap errors when crossing package boundaries
  • Include operation context (“unmarshal”, “dial”, “fetch”)
  • Include relevant identifiers (userID, filename, URL)
  • Don’t include sensitive data (passwords, tokens, secrets)
  • Be consistent within your codebase
  • Consider your audience (internal vs external errors)
  • Use structured logging for detailed debugging info instead of cramming everything into error messages

Error Wrapping with Context

// DO: Add context when wrapping errors
if err := json.Unmarshal(data, &result); err != nil {
    return fmt.Errorf("unmarshal config: %w", err)
}

// DON'T: Just pass through without context
if err := json.Unmarshal(data, &result); err != nil {
    return err
}

When to Add Context:

  • Add context when: The error location/cause isn't obvious from the call stack
  • Skip context when: The error is already clear and you're just passing it up one level
  • Always add context when: Crossing package boundaries

Including Additional Information

Safe Information to Include

// ✅ SAFE: Include non-sensitive operational data
return fmt.Errorf("dial database: %w, host=%s, port=%d", err, host, port)

// ✅ SAFE: Include identifiers and operation context
return fmt.Errorf("fetch user: %w, userID=%d", err, userID)

// ✅ SAFE: Include file paths (usually)
return fmt.Errorf("read config file: %w, path=%s", err, configPath)

// ✅ SAFE: Include request/operation details
return fmt.Errorf("HTTP request failed: %w, method=%s, url=%s", err, method, url)

Information to Avoid

// ❌ UNSAFE: Don't include sensitive data
return fmt.Errorf("auth failed: %w, password=%s", err, password)

// ❌ UNSAFE: Don't include tokens/secrets
return fmt.Errorf("API call failed: %w, token=%s", err, apiToken)

// ❌ UNSAFE: Don't include internal system details in user-facing errors
return fmt.Errorf("query failed: %w, sql=%s", err, sqlQuery)

Common Conventions

Function-Based Context

// Pattern: "operation: %w"
func loadConfig(path string) error {
    data, err := os.ReadFile(path)
    if err != nil {
        return fmt.Errorf("read config file: %w", err)
    }
    
    var cfg Config
    if err := json.Unmarshal(data, &cfg); err != nil {
        return fmt.Errorf("unmarshal config: %w", err)
    }
    
    return nil
}

// Pattern: "operation failed: %w, context=value"
func dialDatabase(host string, port int) error {
    conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
    if err != nil {
        return fmt.Errorf("dial database: %w, host=%s, port=%d", err, host, port)
    }
    return nil
}

Resource-Based Context

// Pattern: Include resource identifiers
func processUser(userID int) error {
    user, err := db.GetUser(userID)
    if err != nil {
        return fmt.Errorf("get user: %w, userID=%d", err, userID)
    }
    
    if err := validateUser(user); err != nil {
        return fmt.Errorf("validate user: %w, userID=%d", err, userID)
    }
    
    return nil
}

Example: Complete Error Handling Chain

// HTTP Handler
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
    userID := getUserID(r)
    user, err := h.service.GetUser(userID)
    if err != nil {
        log.Printf("get user failed: %v", err) // Log full error
        http.Error(w, "user not found", 404)   // Return safe error
        return
    }
    // ... success handling
}

// Service Layer
func (s *UserService) GetUser(userID int) (*User, error) {
    user, err := s.repo.GetUser(userID)
    if err != nil {
        return nil, fmt.Errorf("get user: %w, userID=%d", err, userID)
    }
    return user, nil
}

// Repository Layer
func (r *UserRepo) GetUser(userID int) (*User, error) {
    user, err := r.db.QueryUser(userID)
    if err != nil {
        return nil, fmt.Errorf("query user: %w, userID=%d", err, userID)
    }
    return user, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment