В Go ошибки — это часть обычного потока выполнения, а не исключение “где-то в фоне”. Поэтому качественная обработка ошибок напрямую влияет на стабильность, читаемость и поддержку кода.
Почему в Go ошибки возвращаются явно
Go придерживается принципа простоты: функция возвращает error, а вызывающий код сам решает, что делать дальше. Это делает поведение программы предсказуемым и упрощает отладку.
Типичный паттерн:
result, err := doWork()
if err != nil {
return fmt.Errorf("doWork failed: %w", err)
}
1. Всегда добавляйте контекст к ошибке
Одна из лучших практик — не возвращать ошибку “как есть”, а оборачивать её через %w.
return fmt.Errorf("failed to read config: %w", err)
Так вы получаете:
- понятную цепочку ошибок
- больше информации в логах
- возможность проверять первопричину через
errors.Isиerrors.As
2. Используйте errors.Is вместо сравнения строк
Сравнивать err.Error() — плохая практика. Текст ошибки может измениться.
if errors.Is(err, os.ErrNotExist) {
// файл не найден
}
Это надёжнее и соответствует idiomatic Go ✅
3. Для кастомных типов — errors.As
Если нужно извлечь конкретный тип ошибки:
var pathErr *os.PathError
if errors.As(err, &pathErr) {
log.Println(pathErr.Path)
}
errors.As полезен, когда важно поведение или поля ошибки, а не только факт её наличия.
4. Sentinel errors — осторожно
Иногда удобно объявить ошибку как переменную:
var ErrUserNotFound = errors.New("user not found")
Это подходит для известных бизнес-сценариев. Но не стоит плодить десятки sentinel errors без необходимости — API становится хрупким.
5. Не логируйте одну и ту же ошибку много раз
Частая проблема — ошибка логируется на каждом уровне вызова. В итоге в логах шум.
Хороший подход:
- либо оборачивать ошибку и логировать её один раз на верхнем уровне
- либо логировать только там, где есть реальное решение или реакция
6. Не игнорируйте ошибки
Конструкция _ = someFunc() допустима редко. Особенно опасно игнорировать:
Close()- запись в файл или сеть
json.Marshal/Unmarshal- работу с БД
Даже “маловероятная” ошибка в проде рано или поздно проявится 💥
7. Ошибки должны быть полезными для пользователя и разработчика
Для внешнего клиента:
- без утечки внутренних деталей
- с понятным сообщением
Для логов и мониторинга:
- максимум технического контекста
- request ID, параметры, источник сбоя
8. Panic — не для обычных ошибок
panic в Go используют для действительно аварийных ситуаций:
- нарушение инвариантов
- критическая ошибка инициализации
- баг в программе
Для бизнес-логики, сетевых ошибок и проблем ввода-вывода нужен обычный error, а не panic 🚨
Итог
Хорошая обработка ошибок в Go строится на 4 принципах:
- явный возврат
error - оборачивание с контекстом через
%w - проверка через
errors.Isиerrors.As - минимум шума в логах
Именно это делает Go-код надёжным, поддерживаемым и удобным в эксплуатации 🔍🛠️
Подборку каналов про IT — с практикой, архитектурой, Go и разработкой — стоит сохранить отдельно в закладки 📚