Что делать с найденным: воспроизводимый след
Простая арифметика. Разведка под серьёзный вопрос стоит от получаса до дня. Через полгода вы столкнётесь с тем же вопросом — сами или в лице коллеги. Если следа не осталось, вы заплатите ту же цену второй раз, а иногда и третий, потому что помните, что «где-то это выяснили», и потратите дополнительное время на попытки вспомнить.
Наблюдение, которое стоит принять всерьёз: память о том, что вы что-то знали, сохраняется гораздо дольше, чем само знание. Это худший из возможных вариантов: он не даёт вам ответа и одновременно мешает начать искать заново с нуля.
Эта глава — про минимальные артефакты, которые делают разведку однократной. Про заметки как средство обучения и внешнюю память написана отдельная глава соседнего трека; здесь другое — след, привязанный не к вашему обучению, а к решению, которое вы приняли.
Что такое след
Формулировка, которая задаёт всё остальное:
След — это артефакт, по которому другой человек (или вы через год) может восстановить, что было установлено, откуда это взято и как это проверить.
Три части в определении важны все:
- что установлено — утверждение, а не тема;
- откуда — источник с точным местом и версией;
- как проверить — команда, тест, эксперимент.
Запись «разобрались с таймаутами в клиенте» не является следом ни по одному из трёх пунктов.
Пять форм следа
Упорядочены по убыванию силы. Сила здесь означает одно: насколько вероятно, что след сработает тогда, когда понадобится.
в ходе разведки"] --> B{"Можно ли выразить
это проверкой?"} B -->|"да"| C["1. Исполняемый след:
тест, скрипт, цель в Makefile"] B -->|"нет"| D{"Это объясняет
конкретное место в коде?"} D -->|"да"| E["2. Комментарий в коде
со ссылкой и версией"] D -->|"нет"| F{"Это обосновывает
принятое решение?"} F -->|"да"| G["3. ADR или проектный документ"] F -->|"нет"| H{"Это пригодится
команде?"} H -->|"да"| I["4. Карточка в общей базе"] H -->|"нет"| J["5. Личная заметка"] C --> K["Ломается сама,
когда мир изменился"] E --> L["Находится тем, кто
читает это место"] G --> M["Отвечает на «почему так»
через годы"] I --> N["Находится поиском
по команде"] J --> O["Находится, только если
вы вспомните"]
1. Исполняемый след
Самая сильная форма, потому что у неё есть свойство, которого нет ни у одного текста: она ломается, когда мир меняется.
Пример. Вы выяснили, что клиент библиотеки по умолчанию не повторяет запросы при разрыве соединения, и построили на этом обработку ошибок. Текстовая запись об этом факте через год может оказаться неверной и промолчать. Тест — не промолчит:
def test_client_does_not_retry_on_connection_reset():
"""Фиксирует поведение зависимости, на которое мы опираемся.
Источник: docs/reference/retries, версия 4.2.
Если тест упал — поведение зависимости изменилось,
и наш обработчик ошибок надо пересмотреть.
"""
with failing_server(reset_after_bytes=10) as url:
attempts = counting_transport()
with pytest.raises(ConnectionError):
Client(transport=attempts).get(url)
assert attempts.count == 1
Такие тесты называют характеризующими (characterization tests): они не проверяют вашу логику, а фиксируют предположения о внешнем мире. Их место — отдельный набор, который запускается на обновлении зависимостей. Про тесты вообще — Принципы и терминология тестирования.
Другие формы исполняемого следа:
# цель в Makefile, воспроизводящая проверку одной командой
.PHONY: check-broker-ordering
check-broker-ordering: ## воспроизводит эксперимент из ADR-014
docker compose -f test/broker.yml up -d
go run ./test/ordering_probe.go --messages 10000
docker compose -f test/broker.yml down
# скрипт-репродьюсер рядом с описанием проблемы
scripts/repro/2026-07-tls-handshake.sh
# закреплённая версия как след решения
# (комментарий объясняет, почему версия закреплена)
2. Комментарий в коде
Второй по силе, потому что находится тем, кто читает именно это место, и ровно тогда, когда это нужно.
// Пул намеренно ограничен 25 соединениями, а не числом ядер:
// при большем значении сервер начинает отклонять соединения
// (лимит max_connections=100 делится на 4 реплики).
// Источник: docs/postgres/limits, версия 15; измерение — ADR-021.
// Проверка: make check-pool-saturation
const maxOpenConns = 25
Что делает этот комментарий хорошим: он объясняет почему не очевидное значение, называет источник с версией, ссылается на измерение и даёт команду проверки. Что он не делает: не пересказывает документацию и не объясняет, что такое пул соединений.
Правило: комментарий фиксирует то, чего нет в коде, — причину. Всё остальное код говорит сам.
3. Проектный документ
Когда разведка привела к решению, а не к факту, её место — в ADR или аналогичном документе: контекст, варианты, что выбрали и почему, какие источники это обосновывают. Через два года такой документ отвечает на вопрос «почему у нас так», который иначе стоит недели раскопок.
Жанр разобран подробно в главе про ADR трека технического письма. С точки зрения этого трека важно одно дополнение: в ADR должны попасть ссылки на первоисточники и способ проверки, а не только вывод. Иначе документ фиксирует решение, но не даёт возможности его пересмотреть, когда изменятся обстоятельства.
4. Карточка в общей базе
Для фактов, которые не привязаны к конкретному месту в коде. Структура — та самая, что разбиралась в главе 4:
## Порядок сообщений в брокере гарантируется только в пределах раздела
- **Установлено:** порядок сохраняется в пределах одного раздела и только
при отключённых параллельных повторах на стороне отправителя.
- **Источник:** docs/delivery-guarantees, раздел "Ordering",
версия 3.7 (постоянная ссылка на коммит документации).
- **Цитата:** "Messages are ordered within a partition only when
max.in.flight is set to 1."
- **Проверено:** эксперимент 2026-07-16, скрипт test/ordering_probe.go,
10 000 сообщений, 3 раздела — порядок нарушался при значении > 1.
- **Осталось неизвестным:** поведение при переизбрании лидера
во время отправки.
- **Тупики:** в блогах и в обзорах это условие не упоминается вовсе;
искать бесполезно, только первоисточник.
Семь элементов: утверждение, источник с точным местом, дословная цитата, способ и дата проверки, что осталось неизвестным, тупики. Каждый из них однажды окажется нужным.
5. Личная заметка
Слабейшая форма, потому что находится только если вы о ней вспомните. Тем не менее она лучше, чем ничего, и у неё есть своё место: черновики, вопросы без ответа, наблюдения, которые пока не сложились.
Что записывать, а что нет
Записывать всё — верный способ не записывать ничего. Критерий отбора формулируется двумя параметрами.
Практический критерий из одной фразы: записывается то, что было дорого добыть и что не находится повторно тем же запросом. Всё, что находится за минуту, записывать не надо — вы просто создадите себе вторую документацию, которая устареет.
Отдельно про тупики. Запись «искал X в местах A, B, C — там этого нет» кажется бессодержательной и является одной из самых полезных. Через месяц вы (или коллега) начнёте с тех же трёх мест. Тупик — это отрицательный результат, и в инженерии он ценен ровно так же, как в науке.
Ссылки, которые не протухают
Обычная ссылка на документацию через год ведёт в другую версию или в 404. Способы это пережить:
| Приём | Как |
|---|---|
| Постоянная ссылка на код | ссылаться на конкретный коммит, а не на ветку: в интерфейсе GitHub клавиша y превращает ссылку на файл в ссылку на ревизию |
| Версия в адресе документации | /v3.7/ вместо /latest/ |
| Устойчивый идентификатор | номер RFC, DOI, номер CVE, номер issue — они не меняются |
| Цитата в теле записи | одна-две строки дословно; если ссылка умрёт, останется содержание |
| Раздел вместо якоря | номер и название раздела переживают перестройку сайта |
| Архивирование | сохранить страницу в веб-архиве и записать обе ссылки |
| Копия артефакта | для критичных вещей — сохранить PDF или файл рядом с записью |
Дословная цитата стоит отдельного упоминания: это дёшево (две строки) и спасает чаще всего, потому что позволяет найти утверждение заново поиском по точной фразе, даже когда исходная страница исчезла.
Где след должен лежать
Правило одно: след живёт как можно ближе к решению, которое он обосновывает.
| Что установлено | Где хранить |
|---|---|
| Поведение зависимости, на которое опирается код | тест в репозитории + комментарий в коде |
| Почему выбрано это значение параметра | комментарий рядом со значением |
| Почему выбрана эта технология | ADR в репозитории |
| Как воспроизвести проблему | скрипт в scripts/repro/ + ссылка из issue |
| Факт про внешнюю систему, нужный всей команде | общая база знаний с датой и версией |
| Причина инцидента | постмортем |
| Наблюдение, которое пока никуда не приткнулось | личные заметки |
Антипаттерн: личная вики, в которой лежит знание, нужное команде. Оно там умирает — не потому, что плохое, а потому, что его никто не найдёт.
Второй антипаттерн: общая база, в которую пишут все и никто не убирает. Про её обслуживание — глава про поддержку документации.
У следа есть срок годности
Практический вывод из схемы: самый дешёвый способ поддерживать след в актуальном состоянии — сделать его исполняемым. Текстовая запись требует ручной ревизии, а её никто не делает. Тест ревизует себя сам при каждом обновлении зависимостей.
Для текстовых записей минимальная гигиена: дата и версия в каждой записи, и один проход раз в полгода по записям, которые касаются быстро меняющихся вещей. Записи без даты не подлежат ревизии в принципе — их невозможно оценить.
Обратный вклад
Последний вид следа — тот, что остаётся не у вас, а в корпусе.
Простое наблюдение: всё, чем вы пользовались на протяжении этого трека — ответы в трекерах, статьи, документация, обсуждения, — написано людьми, которые потратили на это своё время. Корпус состоит из вкладов, и он не самовоспроизводится.
Формы вклада, упорядоченные по стоимости:
- Комментарий в issue: «подтверждаю на версии X, обходной путь такой». Пять минут, а следующему человеку экономит час.
- Ответ на вопрос, который вы теперь знаете. Особенно на тот, который сами задавали.
- Правка документации. Один абзац, объясняющий то, на что вы потратили день. У большинства проектов это pull request на десять строк.
- Публичная заметка о том, что не описано нигде. Самый ценный жанр: разбор редкой комбинации, отрицательный результат, объяснение неочевидного поведения с воспроизведением.
- Постмортем или технический разбор от команды. Дорого, но это то, что двигает отраслевое знание.
Есть и прямой личный интерес: публичная запись — это след, который вы точно найдёте. Поиск по своему же блогу или по своим же комментариям в трекере работает лучше, чем поиск по памяти.
Как понять, что система следов работает
Три проверки, не требующие никакой методологии:
- Сколько раз за квартал вы искали то, что уже искали раньше? Если больше двух — следа нет или он не находится.
- Находится ли ваша запись тем запросом, которым вы будете её искать? Проверяется за десять секунд: попробуйте найти собственную запись поиском, не вспоминая, куда её положили.
- Ломается ли исполняемый след, когда мир меняется? Обновите зависимость в отдельной ветке и посмотрите, упало ли что-нибудь. Если ничего — ваши предположения о внешнем мире нигде не зафиксированы.
Минимальная система, если сил нет ни на что
Если всё описанное кажется слишком тяжёлым, начните с трёх вещей — они дают большую часть эффекта и стоят почти ничего:
- Комментарий рядом с любым неочевидным значением, с ссылкой и версией. Ноль дополнительной работы: вы уже пишете этот код.
- Один файл
docs/findings.mdв репозитории, куда падают карточки в свободной форме, но обязательно с датой, версией и ссылкой. Не структура, а привычка. - Один характеризующий тест на каждое предположение о внешнем мире, которое, если сломается, сломает вас.
Этого достаточно, чтобы перестать искать одно и то же дважды. Всё остальное — улучшения поверх.
Типичные ошибки
- Не оставлять следа вовсе. Самая частая и самая дорогая.
- Записывать тему вместо утверждения. «Разобрались с таймаутами» — не след.
- Ссылка без версии и без цитаты. Через год это будет 404 без содержания.
- Хранить командное знание в личных заметках.
- Записывать то, что находится за минуту. Так создаётся вторая документация, которая устареет.
- Не записывать тупики. Отрицательный результат экономит столько же, сколько положительный.
- Оставлять текстовый след там, где возможен исполняемый. Тест сам себя ревизует, текст — нет.
- Не возвращать ничего в корпус. Он состоит из вкладов, а не из воздуха.
Мини-итог
- Разведка без следа повторяется целиком; хуже того, память о том, что вы знали, переживает само знание.
- След — это утверждение, источник с точным местом и версией, и способ проверки. Три части, все обязательны.
- Пять форм по убыванию силы: исполняемый след, комментарий в коде, ADR, карточка в общей базе, личная заметка.
- Исполняемый след ломается сам, когда мир меняется, — это его главное преимущество перед любым текстом.
- Записывается то, что было дорого добыть и что не находится повторно тем же запросом. Тупики записываются наравне с находками.
- Ссылки делаются устойчивыми: постоянная ссылка на ревизию, версия в адресе, устойчивые идентификаторы, дословная цитата в теле записи.
- След живёт рядом с решением, которое он обосновывает, а не в личной вики.
- У записи есть срок годности; дата и версия обязательны, иначе ревизия невозможна.
- Корпус, из которого вы черпаете, состоит из чужих вкладов; вернуть в него — дёшево и в собственных интересах.
Источники
- Michael Nygard. Documenting Architecture Decisions (2011) — исходное описание жанра ADR: cognitect.com/blog/2011/11/15/documenting-architecture-decisions.
- Michael Feathers. Working Effectively with Legacy Code (2004) — характеризующие тесты как способ зафиксировать наблюдаемое поведение.
- Titus Winters, Tom Manshreck, Hyrum Wright. Software Engineering at Google (O’Reilly, 2020) — про знание, живущее в коде и в документах, и про его старение.
- Keep a Changelog — keepachangelog.com; Semantic Versioning — semver.org.
- Internet Archive, «Save Page Now» — архивирование страницы по требованию: web.archive.org.
Что дальше
Трек закончился. Тринадцать глав разбирали одно умение, разложенное на части: как превратить незнание в запрос, как донести запрос до индекса, где искать в коде и в истории, чем первоисточник отличается от пересказа, как проверять найденное, чего ждать от модели, как устроен академический слой, где живут ответы на других языках и у других людей, когда остановиться и что оставить после себя.
Куда идти дальше, зависит от того, что в вашей работе оказалось узким местом:
- Найденное не усваивается и забывается — Как учиться, особенно главы про извлечение из памяти и чтение технических текстов.
- Трудно оценивать чужие утверждения и аргументы — Логика и аргументация, в первую очередь Научное и инженерное рассуждение и Причинные и статистические ошибки.
- След получается, но его никто не читает — Техническое письмо: структура, ADR, постмортемы, поддержка документации.
- Тяжело даётся английский оригинал — Инженерный английский: документация, спецификации, issue, асинхронная переписка.
- Модель хочется использовать всерьёз, а не как справочник — ИИ: основы и ИИ-агенты.
- Разведка упирается в чужой код и историю — Git, особенно Расследование по истории.
- Поиск постоянно происходит в режиме инцидента — SRE: реагирование, постмортемы, наблюдаемость.
А чтобы выбрать следующий предмет по карте, а не по тому, что попалось на глаза первым, — Дорожная карта портала.