- беклог и план переехали в docs/tasks (38 задач, 11 целей), слаги переименованы с транслита на английские, 85 ссылок поправлены - conventions.md разобран в docs/conventions/, local-research.md — в docs/research/, review-journal.md — в docs/review.md с разделом настройки конвейера; заведены security.md, adr/ и .pm.json - шаг docs.py check добавлен в task gate; поведение в architecture.md помечено девятью маркерами долга, database.md получил настройки с числовым значением
2.3 KiB
2.3 KiB
Ошибки
- Только стандартный
errors+fmt.Errorf. Сторонних пакетов ошибок нет: контекст несётslog, стек-трейсы для домашнего сервиса избыточны. - Контекст добавляем обёрткой
%w— это дефолт, чтобыerrors.Is/Asработали сквозь слои.%v— только когда причину сознательно не раскрываем. - Стиль сообщения: со строчной, без точки, без «failed to». Контекст —
операция или субъект (
"open archive: %w"), каждый слой добавляет свой смысл, не повторяя нижний. - Граничные ошибки транслируем в доменные у источника:
sql.ErrNoRows→store.ErrNotFoundвнутриstore, чтобы выше не торчалdatabase/sql. - Sentinel (
var ErrNotFound = errors.New(...)) — для условий, на которые ветвится код. Типизированная ошибка — когда вызывающему нужны данные ошибки. Не плодим типы там, где хватает sentinel. - Наружу (HTTP) отдаём человекочитаемое сообщение по доменной ошибке, не
сырой
err.Error(). Маппинг доменная ошибка → статус живёт в одной точке вhttpapi; новая штатная ветвь отказа заводится sentinel'ом и добавляется туда, иначеdefaultотдаст 500 на нормальный конфликт. - Собрать независимые ошибки (валидация конфига — все проблемы разом) —
errors.Join. panic— только невосстановимое: нарушенный инвариант, сбой инициализации.recover— на верхней границе HTTP-обработчика.- Глушить ошибку без лога — только с однострочным комментарием «почему».