Good Code
The good version adds operation context and preserves an inspectable error chain for callers that use errors.Is.
Lesson 03
Return errors with useful context and wrap underlying errors when callers should be able to inspect them.
// Sentinel error lets callers check the not-found case.
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) {
// %w preserves an error chain for errors.Is/errors.As.
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
}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)
// The original reason is lost behind a vague error.
return Review{}, errors.New("failed")
}
return review, nil
}The good version adds operation context and preserves an inspectable error chain for callers that use errors.Is.
The bad version logs and replaces the original error with a vague message. Callers lose the reason and cannot make a precise decision.