Инженерный английский Сообщения об ошибках и логи: как читать чужие и писать свои
0%

Сообщения об ошибках и логи: как читать чужие и писать свои

Сообщения об ошибках и логи: как читать чужие и писать свои

03:40. Алерт. В канале инцидента висит строка:

2026-07-16T09:14:22Z ERROR checkout failed to charge card: post https://api.pay/v1/charges: dial tcp 10.0.3.7:443: connect: connection refused

Инженер, который её читает, за следующие тридцать секунд должен ответить на четыре вопроса: что мы пытались сделать, где сломалось, кто отказал и есть ли смысл ретраить. Все четыре ответа в этой строке есть. Ни один не выражен по-английски так, как учат в языковой школе: здесь нет подлежащего, нет артиклей, нет ни одного глагола в личной форме. connection refused — это не «соединение отказалось». Это ярлык, приклеенный к коду ECONNREFUSED где-то в семидесятых, и означает он совершенно конкретную вещь: удалённый хост получил наш SYN и ответил RST, то есть машина жива, а слушателя на порту нет.

Эта глава про два навыка, которые выглядят как один. Читать чужие сообщения и логи так, чтобы извлекать максимум за минимум времени. И писать свои так, чтобы через полгода незнакомый человек в другом часовом поясе понял их с первого раза.

Оговорка про уровень, которую мы дали в обзоре трека: материал предполагает, что вы уже читаете технический текст со словарём. Мы не объясняем, что такое причастие; мы объясняем, почему connection refused — причастие и что из этого следует для отладки.

Почему это отдельный диалект

Английский сообщений об ошибках — не «упрощённый» и не «плохой» английский. Это устоявшийся регистр со своей грамматикой, ближайший бытовой аналог которого — газетные заголовки и телеграммы. Служебные слова выброшены, потому что занимают место и не несут информации.

Регистр Текст Где встречается
Разговорный The server we were trying to reach did not accept our connection. письмо коллеге
Документация The connection was refused by the remote host. текст в доках
Телеграфный connection refused строка ошибки

Все три верны. Третий — единственный, который вы увидите в логе, и единственный, который невозможно правильно прочесть, не зная конвенций. Что именно в нём выброшено:

  • Артикли. no such file or directory, а не there is no such file. Артикль возвращается, когда без него возникает двусмысленность: the connection was closed by the remote host — здесь the указывает на конкретное, уже упомянутое соединение.
  • Подлежащее. failed to open config — кто failed? Подразумевается «я, программа». То же опущение, что в русском «не удалось открыть».
  • Личная форма глагола. connection refused — это connection [was] refused: причастие прошедшего времени без связки. Отсюда главная ловушка, о которой ниже.
  • Пунктуация как синтаксис. Двоеточие в A: B: C — не знак препинания, а оператор вложенности: «A, а причина в том, что B, а причина в том, что C».

Причина, по которой регистр таков, прозаическая: строки ошибок пишутся внутри кода, там, где у автора нет ни места, ни контекста. Автор ECONNREFUSED не знал, что через полвека его два слова будут читать миллионы людей, для большинства которых английский — второй язык. Список системных строк из man 3 errno — вероятно, самый читаемый корпус английских «предложений» в истории вычислительной техники, и он же задал стиль всему остальному.

Анатомия строки: где заканчивается логгер и начинается человек

Любая строка лога состоит из двух текстов с разными авторами и разной грамматикой.

Анатомия строки ошибки: префикс лога и цепочка обёрток

Префикс формирует логгер. Его словарь конечен и учится один раз: уровни, имена компонентов, trace_id, caller. Читать его надо как таблицу, а не как текст.

Сообщение — цепочка обёрток, собранная снизу вверх: ядро сформулировало connection refused, сетевой слой обернул это в dial tcp …, HTTP-клиент — в post …, наш код — в failed to charge card. Отсюда правило чтения, экономящее больше всего времени:

Начало цепочки говорит, что вы хотели. Конец цепочки говорит, что помешало. Диагноз всегда в конце.

Русскоязычный читатель по привычке начинает с начала и цепляется за failed to charge card — за самое понятное, но наименее информативное звено. Приучите себя читать такие строки с хвоста.

В Go цепочка строится явно, через %w, и конвенция формулировок вытекает прямо из механики склейки:

func chargeCard(ctx context.Context, id string) error {
    if err := c.post(ctx, "/v1/charges", id); err != nil {
        // Плохо: глагол отказа в каждом звене даёт в логе
        // "failed to charge card: failed to post: failed to dial: connection refused"
        //   return fmt.Errorf("failed to charge card: %w", err)

        // Хорошо: звено — именная группа. Слово "failed" произносит один раз тот, кто печатает лог:
        // "charge card cus_42: post https://api.pay/v1/charges: connection refused"
        return fmt.Errorf("charge card %s: %w", id, err)
    }
    return nil
}

Это не стилистическая придирка, а следствие грамматики: цепочка через двоеточие читается как один синтаксический период, и повтор глагола в каждом звене его разрушает. Официальная формулировка — в Go Code Review Comments: строки ошибок не начинаются с заглавной буквы и не заканчиваются знаком препинания, именно потому что их почти всегда вставляют внутрь большего текста. Механику %w, errors.Is и errors.As разбирает блог Go про ошибки 1.13; то же правило и по той же причине действует в Rust — см. C-GOOD-ERR.

В Java и .NET конвенция противоположная: сообщение исключения — полноценное предложение с заглавной буквы и точкой, потому что печатается отдельной строкой, а не вклеивается в чужую. Если пишете на обоих языках, держите различие в голове: одинаковая формулировка будет ошибкой стиля в одном из них.

Глаголы отказа: карта территории

Английский различает виды отказа тоньше, чем русское «не получилось». Каждое из этих слов — гипотеза о причине, и подмена одного другим стоит часов отладки.

Сетевые отказы

Четыре строки, которые вы будете видеть чаще всего, означают четыре разные вещи:

Строка Errno Что произошло физически Ретраить?
connection refused ECONNREFUSED Хост жив, ответил RST: на порту никто не слушает Да, но сначала проверьте, запущен ли сервис
connection timed out ETIMEDOUT Ответа не было вовсе: пакеты уходят в никуда Да, с backoff; вероятен файрвол или перегрузка
connection reset by peer ECONNRESET Соединение было установлено и оборвано на ходу Осторожно: запрос мог быть выполнен
no route to host EHOSTUNREACH Маршрутизация не знает, куда слать Нет, ретрай не поможет: чините сеть

Обратите внимание на by peer в третьей строке. Это редкий случай, когда английская строка ошибки называет агента, и именно поэтому она информативнее остальных. peer здесь — не «коллега», а «противоположная сторона соединения». Практический вывод: reset и timed out означают, что вы не знаете, выполнился запрос или нет; что с этим делать — в главе про идемпотентность и гарантии доставки.

Отказы доступа

Строка Смысл Типичная ошибка чтения
permission denied (EACCES) Не хватает прав на объект: файл, каталог, сокет Путают с «нет такого файла» — а это разные ветки кода
operation not permitted (EPERM) Операция запрещена в принципе, нужен root или capability Кажется синонимом предыдущего, но приходит из других мест
401 Unauthorized На деле «не аутентифицирован»: мы не знаем, кто вы Слово тянет к «нет прав» — признанная ошибка именования
403 Forbidden Мы знаем, кто вы, и вам нельзя Путают с 401 в обе стороны

401 — идеальный учебный пример: имя статуса выбрано неудачно, и документация прямо это признаёт — семантика статуса «unauthenticated». Когда стандарт вынужден объяснять собственное название, доверяйте определению, а не слову. Механизмы за этими статусами — в аутентификации и авторизации.

Отказы данных и состояния

invalid / malformed / unrecognized / unsupported — четыре диагноза, которые в русском схлопываются в «некорректный». malformed JSON — распарсить не удалось вообще, сломан синтаксис. invalid email address — распарсили, но нарушено смысловое правило. unrecognized field "usr_id" — синтаксис в порядке, но такого поля мы не знаем. unsupported media type — знаем, что это, но не умеем обрабатывать. А разница между unsupported, not yet supported и no longer supported — это разница между «никогда», «будет позже» и «было и убрали».

Родственная тройка про жизненный цикл API, которую путают постоянно: deprecatedобъявлено устаревшим, но работает, это предупреждение, а не отказ (русское «устарело» звучит как «сломалось», отсюда паника на ровном месте); obsolete — поддержки нет, но код может лежать; removed — вызов упадёт.

Слова про состояние — метафоры, и буквальный образ помогает удержать точный смысл: stale — «чёрствый», данные не испорчены, просто старые (stale read); dangling — «болтающийся», ссылка есть, объекта нет; orphaned — «осиротевший», объект жив, а владелец умер; leaked — ресурс выделен и не освобождён. И три слова про нестабильность, которые в русском все становятся «плавающим»: flaky (тест падает случайно), intermittent (проявляется время от времени), transient (кратковременный, проходит сам). Только transient error означает «ретрай уместен».

Пассив и пропавший агент

Главный источник непонимания — не словарь, а синтаксис: английская строка почти всегда прячет того, кто совершил действие. Permission denied — кем? The request was rejected — кем? В русском переводе агент исчезает так же, и читатель по инерции достраивает «мне отказали» — и идёт проверять свои права. А отказать мог прокси, а не целевой сервис.

Приём, который стоит довести до автоматизма: прочитав строку, спросите себя «кто?» и попробуйте назвать агента вслух.

Строка Скрытый агент Что это меняет
connection refused Ядро на удалённой машине Идти надо на удалённый хост, а не в свой код
permission denied Ядро локальной машины, проверка на inode Проверять права файла, а не токен
403 Forbidden Сервис или стоящий перед ним прокси Сначала проверьте, дошёл ли запрос до сервиса
certificate verify failed Ваш TLS-клиент Проблема на вашей стороне: не тот store доверия

Отсюда прямое правило для сообщений, которые пишете вы: если знаете агента — назовите его. payments-api rejected the request информативнее, чем request rejected, и стоит трёх лишних слов. Тот же совет даёт руководство по стилю сообщений PostgreSQL — один из лучших документов на эту тему вообще: активный залог, конкретный субъект, никаких bad, illegal и unknown вместо точных слов.

Процедура: как разобрать незнакомую ошибку

Это не «понимание английского», а алгоритм. Он работает, даже если половина слов вам незнакома.

Ключевой шаг — предпоследний, и он не про язык. Строка ошибки — уникальный ключ к исходному коду. Она почти всегда существует в репозитории зависимости ровно в одном месте, и код вокруг неё отвечает на вопрос «при каких условиях это печатается» точнее любой документации:

# Инвариантная часть — без путей и чисел; -F отключает regex
rg -F 'connection refused' "$(go env GOMODCACHE)"
grep -rn 'invalid literal for int' "$(python -c 'import sys; print(sys.prefix)')/lib"

Выделение инварианта — навык, который экономит часы. В строке

failed to load config from /srv/app/releases/20260716T091422Z/config.yaml: yaml: line 42: did not find expected key

инвариант — это did not find expected key. Всё остальное — ваши данные, которых нет ни у кого в интернете. Люди, которые ищут строку целиком, ничего не находят и делают вывод «уникальная проблема».

Чтение стектрейсов: направление имеет значение

Python прямо пишет направление чтения в первой строке, и её почти все игнорируют:

Traceback (most recent call last):
  File "app/handlers.py", line 42, in create_order
    total = int(request.form["amount"])
ValueError: invalid literal for int() with base 10: ''

most recent call last — «самый недавний вызов последним», то есть читать снизу: внизу место, где сломалось, вверху — как мы туда попали. Само сообщение непрозрачно даже для тех, кто знает слова: literal — текстовая запись значения, with base 10 — разбор шёл в десятичной системе (у int() есть параметр base). Итог: пустая строка не является записью числа. Никакой мистики, но фразу надо один раз развернуть.

Java устроена наоборот:

java.lang.IllegalStateException: Failed to execute CommandLineRunner
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:790)
	... 12 more
Caused by: org.postgresql.util.PSQLException: FATAL: password authentication failed for user "app"

Caused by: — «вызвано тем, что», и цепочка идёт вниз. Читать надо последний Caused by: верхнее исключение почти всегда бесполезная обёртка фреймворка. ... 12 more — «ещё 12 таких же», JVM свернула повторяющийся хвост стека. И обратите внимание на FATAL: внутри сообщения PostgreSQL: это его собственный уровень серьёзности, попавший внутрь чужого текста. Уровни вкладываются друг в друга, и ERROR снаружи не обязан совпадать с FATAL внутри.

Уровни как шкала

Слова уровней — шкала с определённым порядком, стандартизованная в RFC 5424: emerg, alert, crit, err, warning, notice, info, debug. warning — «что-то не так, но работа продолжается»; если после него процесс падает, слово выбрано неверно. fatal и panic — «процесс сейчас завершится»; fatal в английском значит не «ужасный», а «смертельный для этого процесса» — Go log.Fatal буквально вызывает os.Exit(1), слово описывает механику, а не эмоцию. Типичная ошибка — писать ERROR там, где ничего не сломалось: дежурный привыкает к красному цвету и перестаёт его видеть, и арифметику этого привыкания разбирает глава про алерты.

Логи: где именно в них живёт английский

Строка структурированного лога — не предложение, а запись. Английский в ней сосредоточен в двух местах: в поле msg и в именах полей.

{"ts":"2026-07-16T09:14:22Z","level":"error","msg":"charge card","service":"checkout",
 "customer_id":"cus_42","attempt":3,"timeout_ms":2000,"error":"connection refused","trace_id":"a1b2c3"}

Вид глагола: -ing против -ed

Английское причастие несёт то же различие, что русский совершенный и несовершенный вид, — и у русскоязычных инженеров здесь есть готовая интуиция.

Формулировка Значение Когда писать
starting migration процесс начался и идёт перед началом длинной операции
migration completed завершилась успешно после успеха
migration failed завершилась отказом после отказа
retrying request повторяем прямо сейчас перед повтором
connecting / connected «подключаемся» / «подключились» пара строк вокруг установления соединения

Правило: -ing — операция идёт, причастие прошедшего времени — операция закончилась. Хуже всего смешивать: если половина кодовой базы пишет started migration, а половина starting migration, по логу невозможно понять, зафиксирован факт запуска или факт завершения. Русское «Запуск миграции» так же двусмысленно — тот случай, когда английский строже родного языка.

Порядок слов в именах полей

Английское составное существительное строится так, что главное слово стоит последним, а всё перед ним — определения. retry count — это count, а retry уточняет какое.

Правильно Неправильно Почему
retry_count count_retry главное слово — count, оно последнее
request_id id_request то же самое
duration_ms ms_duration единица измерения — суффикс, а не префикс
user_email email_user «email пользователя», а не «пользователь почты»

id_request — калька с русского «идентификатор запроса», где главное слово идёт первым; носитель прочитает это как «запрос идентификатора», то есть наоборот. Конвенции имён метрик формализованы в Prometheus naming practices (базовые единицы, суффикс _total для счётчиков), имена атрибутов — в семантических соглашениях OpenTelemetry (http.request.method, server.address). Брать эти словари готовыми выгоднее, чем изобретать свои: половина споров об именовании исчезает вместе с необходимостью их вести. Про саму телеметрию — глава про наблюдаемость.

msg — константа, значения — в полях

import logging

log = logging.getLogger("checkout")

# Плохо: английский собирается на лету и ломается на числах,
# а агрегатор видит миллион уникальных сообщений вместо одного
log.error(f"Deleted {n} items from cart {cart_id}")   # "Deleted 1 items" — носитель споткнётся

# Хорошо: msg — неизменяемая именная группа, всё переменное вынесено в поля
log.error("delete cart items", extra={"deleted": n, "cart_id": cart_id})

Причин две, и они складываются. Инженерная: постоянный msg — ключ группировки, интерполяция делает каждую строку уникальной и ломает агрегацию. Языковая: английское согласование числа не выживает в интерполяции (1 items, 1 files were deleted), а костыли вроде item(s) выглядят как признак того, что автору было всё равно. Вынести число в поле — единственное честное решение: deleted=1 не имеет грамматического числа и потому не может с ним не согласоваться. Третья причина всплывает при локализации: собранная из кусков фраза непереводима, потому что в других языках другой порядок слов.

И то, чего в логах быть не должно: пароли, токены, заголовки Authorization, номера карт, персональные данные. Лог уходит в хранилище с другими правами доступа, живёт месяцами и попадает в скриншоты в чатах; failed to authenticate user with password hunter2 — не языковая ошибка, а инцидент безопасности (см. управление секретами).

Как писать сообщение, которое поймут

Формула, к которой сходятся почти все руководства по стилю:

Что мы делали + что ожидали + что получили на самом деле + что теперь делать.

Не обязательно все четыре части в одном сообщении, но если нет ни одной — сообщение бесполезно.

Правый верхний угол устроен одинаково: точно названный объект плюс очевидное следующее действие. port 8080 already in use не содержит инструкции, но действие выводится однозначно — и это нормально.

Одно сообщение — один факт. Failed to load config or connect to database означает, что автор сам не знает, что случилось.

Показывайте значения, а не характеристики значений. expected an integer, got "abc" вместо invalid value. Пара expected … got … — самая ценная конструкция жанра: она задаёт направление сравнения. Первое — то, чего хотел код, второе — то, что пришло на самом деле; перепутать их местами при чтении вывода тестов — классическая ошибка.

Активный залог и названный субъект. payments-api returned 503 вместо a 503 was returned.

Никаких please, oops и восклицательных знаков. В сообщении об ошибке please не считывается как вежливость — оно считывается как шум между читателем и информацией; так же считают руководство Google и Microsoft Writing Style Guide. Отдельно: русская привычка ставить запятую после «пожалуйста» переносится в Please, try again — в английском запятой там нет, и она сразу выдаёт неносителя.

Не обвиняйте читателя и не извиняйтесь вместо объяснения. You entered an invalid datedate must be in YYYY-MM-DD format, got 16/07/2026. Разница не в вежливости: вторая версия содержит спецификацию формата, первая — нет. А Sorry, something went wrong — это отказ сообщать информацию, оформленный как вежливость.

Одно слово на одну вещь. Если в документации сущность называется workspace, ошибка не имеет права говорить project: читатель потратит время, выясняя, одно это или разное. Та же дисциплина терминологии, что в документировании требований.

Стабильный код + изменяемый текст. Формулировку вы будете улучшать; те, кто грепает ваши логи, — нет. Дайте им error_code, и текст можно переписывать свободно. Для HTTP API это оформлено стандартом RFC 9457 Problem Details: type — стабильный идентификатор, title — короткая человеческая формулировка, detail — конкретика случая. Обратите внимание, как стандарт распределяет регистры: title телеграфный и постоянный, detail — полное предложение. Та же двухуровневая схема, что в PostgreSQL, где primary message пишется строчными без точки, а detail и hint — законченными предложениями.

Планка, на которую стоит смотреть: компилятор Elm сделал качество сообщений отдельной инженерной целью (Compiler Errors for Humans), Rust пошёл тем же путём, и сегодня error[E0382]: borrow of moved value с подчёркнутым фрагментом и блоком help: — фактический стандарт индустрии. Разберите структуру их вывода: заголовок телеграфный, объяснение обычным английским, подсказка императивом. Три регистра в одном сообщении, каждый на своём месте.

Мастерская: как это читает носитель

Реальные формулировки из кода русскоязычных команд. Для каждой: как её прочтёт носитель, почему так вышло и как переписать.

1. Oops! Something went wrong. Please try again later. Читается дружелюбно и уклончиво, как сообщение маркетплейса; в инженерном контексте — «мы не знаем, что случилось, и не собираемся выяснять». Переписать: failed to create order: payments-api timed out after 5s (request_id=a1b2c3).

2. Please, check the correctness of entered data. Читается как машинный перевод. Три сигнала сразу: запятая после Please (в английском её нет), тяжёлая номинализация the correctness of entered data вместо глагола, отсутствие детерминатива перед entered data. Переписать: email must contain @, got "user.example.com".

3. Impossible to connect to database. Читается как обрывок: конструкция требует формального подлежащего (it is impossible to…), без него фраза повисает. Плюс impossible — «невозможно в принципе», а не «сейчас не вышло». Переписать: cannot connect to database: dial tcp 10.0.3.7:5432: connection refused.

4. Not enough of memory. Читается как лишний предлог: enough управляет существительным напрямую. Ошибка родом из русского родительного падежа: «недостаточно памяти» → «of memory». Переписать: out of memory: tried to allocate 2 GiB, limit is 1 GiB.

5. The operation was finished with error. Читается как пассив с оттенком «кто-то извне её завершил», плюс пропущенный артикль (with an error). Носитель для этого смысла использует один глагол. Переписать: import failed after 1200 of 5000 rows.

6. In case of error contact to administrator. Читается как калька: contact в английском переходный, предлог to после него — ошибка («обратиться к»). Плюс сама рекомендация бесполезна: какому администратору, как. Переписать: contact your workspace owner or open a ticket in #platform-support.

7. Wrong login or password. Читается дважды мимо: login — это действие входа, а не имя пользователя (username), а wrong в этом контексте звучит по-детски, стандартное слово incorrect. Переписать: Incorrect username or password. — и намеренно не уточнять, что именно неверно: расплывчатость здесь не небрежность, а защита от перебора учётных записей (user enumeration).

8. Error: error occurred while processing of request. Читается как текст, написанный чтобы что-нибудь стояло в этом месте: слово error дважды, номинализация вместо глагола, лишний of. И Data is absent из соседней строки туда же: absent в английском говорят про людей на собрании, про данные — missing. Переписать: process order 4711: decode payload: unexpected EOF.

9. Timeout Читается как существительное без контекста: чей таймаут, какой лимит, сколько ждали, что делали — ничего. Переписать: read timeout after 30s waiting for payments-api /v1/charges.

10. User not found в логе аутентификации Читается нормально — но одновременно бесполезно (какой пользователь, в какой системе) и опасно: подтверждает существование учётной записи тому, кто читает логи. Переписать: в лог — authenticate: no user for subject=sub_9f2 in realm=corp; наружу — общая формулировка из пункта 7.

Ложные друзья, которые дорого стоят

Не список слов, а места, где неверное понимание меняет решение.

  • actual — «фактический», не «актуальный». В expected 3, actual 5 речь о том, что пришло на самом деле; русское «актуальное значение» — это current value.
  • eventually — «рано или поздно», причём гарантированно, а не «возможно». eventually consistent — не «может быть, будет согласовано», а «согласуется через некоторое время»; понимание этого слова как «возможно» ведёт к неверным выводам о гарантиях системы.
  • resolve — в контексте DNS «разрешить имя в адрес», а не «решить проблему». failed to resolve host — не смогли получить IP.
  • validate vs verify — первое про соответствие правилам формы (валидируем email по шаблону), второе про соответствие истине (верифицируем подпись).
  • assert — «объявить инвариант». assertion failed — не «утверждение провалилось», а «инвариант нарушен».
  • graceful — «с соблюдением процедуры». graceful shutdown — не «изящное», а «с доработкой текущих запросов».
  • fails silently — «падает, ничего не сообщая». Это диагноз, а не наречие образа действия.

Более широкий разбор межъязыковой интерференции — в главе про типичные ошибки.

Один отказ — два текста

Одно событие требует двух разных сообщений, и самая частая ошибка — написать один текст и показать его обоим адресатам.

Инженерное и пользовательское сообщения расходятся по трём осям: словарь (технический против бытового), длина (телеграмма против предложения) и содержание (диагноз против последствий и действия). Пользователю важно ровно одно, чего нет в техническом тексте: деньги не списаны. Инженеру важно ровно одно, чего нет в пользовательском: какой хост отказал. Как писать пользовательскую половину — предмет главы про текст в интерфейсе; здесь важен сам факт разделения: если ваш err.Error() попадает пользователю на экран, у вас нет ни одного из двух текстов.

Жизненный цикл в логах

Схема делает видимыми три вещи, которые чаще всего забывают. Различие transient и permanent должно попадать в текст: дежурному не нужно гадать, ретраил ли код — card declined окончательно, retrying in 2s нет. Номер попытки — поле, а не часть фразы: attempt=2 агрегируется, second attempt нет. И «сдались» — отдельное состояние с собственным сообщением, по которому строится алерт.

Английский giving up в логах абсолютно нормален и не звучит неформально. Это к вопросу о том, что телеграфный регистр не равен «формальному»: он допускает give up, bail out, fall back, back off, drop, но не допускает please и oops.

Что смотреть на ревью

Короткий список для чтения чужого дифа. Формулировки самих замечаний — тема главы про комментарии на ревью; здесь только предмет.

  • Сообщение называет конкретный объект — файл, поле, хост, идентификатор?
  • Есть ли expected … got … там, где сравниваются значения?
  • Не потерян ли исходный err при оборачивании (в Go — есть ли %w)? Не повторяется ли глагол отказа в каждом звене?
  • Регистр и точка соответствуют конвенции языка? msg — константа, переменное вынесено в поля?
  • Нет ли в сообщении секретов, токенов, персональных данных?
  • -ing и -ed использованы последовательно с остальным кодом?
  • Пользовательский текст отделён от инженерного? Слово из сообщения совпадает со словом из документации?

Мини-практика на неделю

  1. Разбор цепочки. Возьмите пять строк ERROR из прода за неделю. Для каждой отрежьте префикс, найдите последнее звено, назовите агента вслух по-русски. Не смогли — кандидат на переписывание.
  2. Инвариант. Возьмите три ошибки из чужих библиотек, выделите инвариант, найдите его в исходниках через rg -F и прочитайте условие возникновения. Почти всегда оно оказывается не тем, что вы предполагали.
  3. Аудит msg. Грепните кодовую базу на интерполяцию внутри сообщений логов (f", fmt.Sprintf внутри log.) и перенесите переменные в поля хотя бы в одном модуле.
  4. Чтение образцов. Спровоцируйте три разные ошибки компилятора Rust или Elm и прочитайте вывод целиком, включая блоки help: и note:. Это самый концентрированный корпус хорошо написанного инженерного английского, доступный бесплатно и локально.

Упражнений на «расширение словаря» здесь намеренно нет: слова из сообщений об ошибках запоминаются намертво, когда стоили вам двух часов отладки, и почти не запоминаются из карточек. Как поддерживать язык системно — последняя глава трека.

Итог

  • Английский сообщений об ошибках — телеграфный регистр: без артиклей, без подлежащего, с причастиями вместо личных глаголов. Он ближе к газетному заголовку, чем к речи.
  • Цепочка через двоеточия читается с конца: начало — намерение, конец — диагноз.
  • Агент почти всегда скрыт пассивом. Привычка спрашивать «кто?» после каждой строки отказа экономит часы, потраченные не в том месте.
  • Точность словаря отказов — инженерная информация: refused, timed out, reset, unreachable — четыре разные гипотезы, а не синонимы.
  • Строка ошибки — уникальный ключ к исходному коду зависимости: выделяйте инвариант, ищите в исходниках, читайте условие.
  • Своё сообщение стройте как «что делали + что ожидали + что получили + что делать»: называйте объект и субъект, показывайте значения, не извиняйтесь и не пишите please.
  • В логах msg — константа, переменное — поля; -ing для процесса, причастие прошедшего времени для результата; главное слово в имени поля — последнее.
  • Инженерный и пользовательский тексты об одном отказе — два разных текста. Один вместо двух означает, что нет ни одного.

Источники

Что дальше

Мы разобрали текст, который пишется для машины и читается человеком в спешке. Следующий жанр устроен наоборот: он полностью человеческий, крайне короткий и при этом читается чаще всего остального, что вы пишете, — заголовок коммита живёт в истории проекта десятилетиями и попадает в глаза каждому, кто делает git log или git blame.

Коммиты и pull request: короткий текст, который читают чаще всего

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов