Go

Lesson 03

Error handling and wrapping

Return errors with useful context and wrap underlying errors when callers should be able to inspect them.

Good Code

reviews/repository.go
var ErrReviewNotFound = errors.New("review not found")

func (r *Repository) Find(ctx context.Context, id string) (Review, error) {
    review, err := r.store.FindReview(ctx, id)
    if errors.Is(err, store.ErrNotFound) {
        return Review{}, fmt.Errorf("find review %s: %w", id, ErrReviewNotFound)
    }
    if err != nil {
        return Review{}, fmt.Errorf("find review %s: %w", id, err)
    }

    return review, nil
}

Bad Code

reviews/repository.go
func (r *Repository) Find(ctx context.Context, id string) (Review, error) {
    review, err := r.store.FindReview(ctx, id)
    if err != nil {
        log.Println("find review failed", err)
        return Review{}, errors.New("failed")
    }

    return review, nil
}

Review Notes

What to review

Good Code

The good version adds operation context and preserves an inspectable error chain for callers that use errors.Is.

Bad Code

The bad version logs and replaces the original error with a vague message. Callers lose the reason and cannot make a precise decision.

Takeaways

  • Errors are values in Go; review both the message for humans and the wrapping contract for callers.