Как искать информацию Когда документации нет: исходники, история, трекер
0%

Когда документации нет: исходники, история, трекер

Когда документации нет: исходники, история, трекер

Три ситуации, в которых предыдущая глава не помогает:

  • Внутренний сервис вашей компании. Есть README на четыре строки, написанный при создании репозитория, и человек, который его писал, уволился.
  • Заброшенная, но незаменимая библиотека. Последний релиз три года назад, документация — сгенерированный список методов без описаний.
  • Недокументированное поведение живого проекта. Документация есть и хорошая, но про интересующий вас случай в ней ничего нет.

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

Порядок раскопок

Копать надо не как попало, а по возрастанию стоимости. Каждый уровень отвечает на свой вопрос и часто закрывает вопрос целиком.

Обратите внимание: пунктирные стрелки выходят с каждого уровня. Большинство вопросов закрывается на втором или четвёртом, и до истории дело не доходит. Идти сразу в историю — распространённая ошибка увлечённых: она интереснее, но дороже.

Тесты как справочник

Первое, что стоит открыть в проекте без документации, — не исходники, а тесты.

Причины, по которым тест лучше примера из README:

  • Тест проверяется на каждом коммите. Пример из README может не компилироваться уже два года; тест — нет.
  • Тесты покрывают границы. В README счастливый путь; в тестах — пустой ввод, отрицательные числа, таймауты, конкурентный доступ. Это ровно те случаи, из-за которых вы и пришли.
  • Тест показывает намерение. Название теста — это утверждение о том, что система обязана делать. TestClientRetriesOnlyIdempotentRequests отвечает на вопрос лучше любого абзаца.
  • Табличные тесты — готовая таблица поведения. В экосистемах, где принят табличный стиль, один такой тест содержит два десятка пар «вход — ожидаемый выход».
# найти тесты, относящиеся к интересующему символу
rg -n --type go 'func Test.*Retry' .
rg -n -A20 'describe\(.retry' test/

# в Go: запустить один тест и посмотреть, что он делает
go test -run TestClientRetries -v ./internal/client/

# найти тестовые данные — часто они информативнее самих тестов
fd -t d 'testdata|fixtures|__snapshots__'

Приём: если тест непонятен, запустите его под отладчиком или добавьте печать. Тест — это минимальная воспроизводимая программа, использующая интересующий вас API; ничего лучше для экспериментов не существует. Про чтение чужого кода ради обучения — отдельная глава соседнего трека; здесь нас интересует извлечение конкретного ответа, а не построение общей модели.

Чтение реализации под вопрос

Читать незнакомый код целиком — плохая идея и обычно невыполнимая. Читать под конкретный вопрос — выполнимая задача на двадцать минут.

Тактика:

  1. Найти точку входа по имени. Публичная функция, которую вы вызываете. Семантический переход к определению (LSP, go to definition) точнее текстового поиска.
  2. Идти вглубь только по той ветке, которая относится к вашему случаю. Всё остальное — шум. Читать не «функцию целиком», а путь исполнения при ваших параметрах.
  3. Останавливаться на первом объяснении. Как только найдено условие, объясняющее наблюдаемое поведение, чтение прекращается.
  4. Проверять понимание экспериментом, а не дальнейшим чтением. Гипотеза «оно так себя ведёт, потому что вот эта проверка» проверяется за минуту изменением входа.

Два инструмента, которые заменяют часы чтения:

# трассировка: что программа делает на самом деле, без чтения кода
strace -f -e trace=network,openat -o trace.log ./service
ltrace -e 'malloc+free' ./tool          # вызовы библиотечных функций

# для интерпретируемых языков — трассировка на уровне вызовов
python -X importtime -c 'import mypkg'  # что и сколько импортируется
python -m trace --trace script.py | head -50

Наблюдение вместо чтения — часто самый быстрый ответ на вопрос «а что оно вообще делает». Сюда же относятся сетевые дампы (tcpdump, Wireshark, mitmproxy для HTTP) и повышение уровня логирования: у многих библиотек есть скрытый debug-режим, который печатает ровно то, что вам нужно. Про инструментальную сторону — глава про диагностику сети и про измерения.

История как источник знания

Здесь мы переходим к самому недоиспользуемому источнику. Механика инструментов Git подробно разобрана в главе про расследование по истории; ниже — их применение именно как приёмов разведки, с ответом на вопрос «какой вопрос закрывает каждый».

Вопрос разведки Инструмент Что получаете
Когда появилось это значение/строка/константа git log -S'строка' коммит, где строка добавлена или удалена
Когда менялось что-то по шаблону git log -G'regex' все коммиты, где дифф совпадает с регуляркой
Кто и зачем написал эту строку git blame -C -M коммит, автор, дата; дальше — сообщение и PR
Как жила именно эта функция git log -L :funcName:file.go история одной функции без шума
Когда сломалось git bisect run ./check.sh точный коммит, вносящий регрессию
Что менялось между релизами git log v1.4..v1.6 -- path/ список изменений в подсистеме
Что менялось в документации git diff v1.4..v1.6 -- docs/ смысловые изменения, отфильтрованные авторами
# когда в проекте появилось значение по умолчанию, которое нас удивило
git log -S'MaxIdleConns' --oneline -- transport.go

# что вообще меняли вокруг таймаутов за последний год
git log -G'[Tt]imeout' --since='1 year ago' --oneline

# история одной функции целиком, с диффами
git log -L :dialContext:net/http/transport.go

# кто трогал строку, игнорируя коммиты чистого форматирования
git blame -C -M --ignore-revs-file .git-blame-ignore-revs config.go

# от коммита к обсуждению: в сообщении почти всегда есть номер
git show a1b2c3d --stat | head -30

Приём git log -S (его называют pickaxe) заслуживает отдельного внимания, потому что закрывает самый частый исторический вопрос: «с какого момента это стало так». Ответ на него автоматически даёт датировку — то, чего не хватает большинству найденных утверждений.

Бинарный поиск по истории

Когда известно, что «в версии 1.4 работало, в 1.6 нет», а между ними триста коммитов, поиск виновника — это git bisect, и его стоит уметь запускать автоматически.

# скрипт-проверка возвращает 0, если всё хорошо, и 1, если воспроизводится баг
cat > /tmp/check.sh <<'EOF'
#!/bin/sh
go build ./... || exit 125     # 125 — «пропустить, не собирается»
./run-repro.sh
EOF
chmod +x /tmp/check.sh

git bisect start v1.6.0 v1.4.0
git bisect run /tmp/check.sh
git bisect reset

Логарифм от трёхсот — примерно восемь шагов. Автоматический bisect по большому диапазону обычно занимает меньше времени, чем чтение changelog в поисках подозрительного изменения, и даёт точный ответ вместо гипотезы. Код 125 в скрипте важен: он говорит «эту ревизию нельзя проверить», и bisect её пропускает вместо того, чтобы объявить плохой.

От коммита к намерению

Коммит отвечает на «что изменилось». На «почему» отвечает то, что вокруг него.

Что искать в сообщении коммита, чтобы попасть в обсуждение:

  • номер задачи или PR (#1234, PROJ-567) — прямая ссылка;
  • трейлеры Fixes:, Closes:, Reviewed-by:, Reported-by:, Link: — в проектах, живущих на рассылках, Link: ведёт прямо в архив письма;
  • упоминание CVE — значит, есть бюллетень с подробностями;
  • префиксы Conventional Commits (fix:, feat!:) — восклицательный знак означает ломающее изменение.

Если сообщение коммита пустое и ссылок нет, остаётся поиск по дате: посмотрите, что обсуждалось в трекере и рассылке в те же дни. Это работает чаще, чем кажется.

Недокументированное поведение: опираться или нет

Вы нашли, что система делает нечто полезное, но нигде не написано, что она обязана это делать. Вопрос: можно ли на это опираться?

Полезная формулировка — закон Хайрама (Hyrum Wright, hyrumslaw.com):

При достаточном числе пользователей API не имеет значения, что вы обещали в контракте: любое наблюдаемое поведение вашей системы будет использовано кем-то из них.

Это не разрешение опираться на что угодно, а описание того, что происходит с вами как с автором. Как с пользователем — решение принимается по чек-листу:

Вопрос Ответ «да» Ответ «нет»
Покрыто ли поведение тестом в проекте? это фактический контракт, менять его будет больно и авторам случайность реализации
Упоминается ли в changelog или в issue? о нём знают о нём не знают, и оно исчезнет молча
Публичный ли это символ (не _private, не internal)? шанс на стабильность выше явное приглашение к поломке
Есть ли явное «subject to change»? опираться нельзя нейтрально
Что будет, если это исчезнет? оцените цену это и есть ответ

Практический вывод: опираться иногда приходится, и это нормально. Но опора должна быть осознанной и записанной: комментарий в коде, тест, который сломается при изменении поведения, и запись в следе разведки (глава 13). Разница между инженерным решением и миной — в наличии этих трёх вещей.

Внутренний проект, автор которого ушёл

Самый частый практический случай — не заброшенная библиотека из интернета, а сервис вашей же компании без документации. Здесь корпус источников шире, чем кажется, и он специфический.

Источник Что оттуда достаётся Как искать
Инфраструктурный код реальная конфигурация: переменные окружения, лимиты, реплики rg по репозиториям с Terraform, Helm, манифестами
Конфигурация CI как это собирается, тестируется и выкатывается .github/workflows, Jenkinsfile, .gitlab-ci.yml
Дашборды и алерты что считается нормой и что считается поломкой определения алертов лежат в коде, их можно грепать
Тикеты и доски история изменений требований поиск по названию сервиса в трекере, включая закрытое
Разборы инцидентов как система ломается на самом деле внутренние постмортемы; см. жанр
ADR и проектные документы почему выбрано именно так поиск по названию сервиса в вики и в docs/ репозитория
Переписка в мессенджере обсуждения, которых нет нигде поиск по имени сервиса и по имени бывшего автора
Код ревью сомнения и компромиссы на момент написания комментарии к старым PR по этому репозиторию

Два приёма, специфичных для внутренней археологии.

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

Искать по имени ушедшего автора. git shortlog -sn даёт список тех, кто писал систему. Дальше — поиск по их сообщениям в трекере и по их комментариям в ревью. Это часто выводит на текст, который человек написал один раз в чате и который отвечает на ваш вопрос целиком.

И самое важное: у ушедшего автора почти всегда есть преемник по контексту — тот, кто с ним рядом работал. Пять минут разговора экономят день раскопок. Как формулировать такие вопросы, чтобы получить ответ, — глава 10.

Когда проекта больше нет

Отдельный случай: библиотека заброшена, сайт исчез, репозиторий удалён.

  • Форки. У популярного заброшенного проекта почти всегда есть живой форк. Ищите по имени плюс «fork», смотрите список форков с недавними коммитами и сеть зависимостей в реестре пакетов.
  • Пакеты дистрибутивов. Мейнтейнеры Debian, Fedora, Alpine и других дистрибутивов накладывают на исходники патчи и документируют их. Эти патчи — концентрат знаний о проблемах проекта: сборка на новых компиляторах, исправления безопасности, несовместимости. Смотреть их можно в исходном пакете дистрибутива.
  • Software Heritage. Архив, специально сохраняющий исходный код исчезнувших проектов: softwareheritage.org.
  • Веб-архивы. Документация исчезнувшего сайта часто сохранена в Wayback Machine.
  • Зеркала рассылок. Обсуждения старых проектов живут в архивах списков рассылки дольше, чем сами проекты.
  • Копия у вас на диске. Не забывайте самое очевидное: версия, которую вы используете, лежит в кэше пакетного менеджера вместе со всеми исходниками.

Реверс без исходников

Крайний случай — закрытый бинарник или чужой сервис. Полноценный реверс-инжиниринг выходит за рамки главы, но три дешёвых приёма стоит знать, потому что они часто закрывают вопрос:

# читаемые строки: пути сборки, версии, сообщения, иногда — эндпоинты
strings ./binary | rg -i 'http|version|error'

# какие библиотеки нужны и какие символы импортируются
ldd ./binary
nm -D ./binary | head -40

# что бинарник делает с системой
strace -f -e trace=file,network ./binary 2>&1 | head -60

# что ходит по сети (для HTTP удобнее прокси с подстановкой сертификата)
tcpdump -i any -A 'port 8080'

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

Мини-практика

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

  1. Какое значение по умолчанию у любого таймаута в ней и когда оно последний раз менялось? Инструмент: rg плюс git log -S.
  2. Какое поведение покрыто тестом, а какое — случайность реализации? Возьмите одну функцию и найдите её тесты.
  3. Есть ли в коде обход чужого бага? Ищите комментарии со словами workaround, hack, bug, TODO и номерами задач; откройте одну такую задачу целиком.
  4. Кто реально держит проект? git shortlog -sn --all, затем — сколько из этих людей коммитили за последний год.

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

Типичные ошибки

  1. Начинать с истории. Она интереснее и дороже. Сначала тесты и реализация.
  2. Читать код целиком. Читать надо путь исполнения при ваших параметрах, а не файл.
  3. Игнорировать тесты. Это самая точная и самая доступная документация проекта.
  4. Останавливаться на коммите. Коммит говорит «что»; «почему» лежит в обсуждении, на которое он ссылается.
  5. Опираться на недокументированное поведение молча. Опора без комментария и без теста — мина замедленного действия.
  6. Не проверять гипотезу экспериментом. Чтение кода даёт правдоподобную модель; правильность даёт запуск.
  7. Считать заброшенный проект тупиком. Форки, патчи дистрибутивов и архивы обычно живы.

Мини-итог

  • Порядок раскопок: README и примеры, тесты, публичная поверхность, реализация, история, обсуждение, люди. Большинство вопросов закрывается на втором и четвёртом уровне.
  • Тесты — лучшая документация недокументированного проекта: они проверяются, покрывают границы и формулируют намерение в названиях.
  • Читать реализацию надо под вопрос и по одной ветке исполнения, останавливаясь на первом объяснении.
  • Трассировка и дампы часто отвечают быстрее чтения: наблюдение вместо реконструкции.
  • История отвечает на вопрос «с какого момента»: log -S для строк, log -G для шаблонов, blame для авторства, log -L для функции, bisect run для регрессии.
  • От коммита к намерению ведут номера задач и трейлеры в сообщении; без них — поиск по дате в трекере и рассылке.
  • Опора на недокументированное поведение допустима, если она осознана, покрыта своим тестом и записана.
  • Заброшенный проект остаётся источником: форки, патчи дистрибутивов, архивы кода и веба, локальный кэш пакетов.

Источники

  • Hyrum’s Law — формулировка и обсуждение: hyrumslaw.com; подробный разбор — Titus Winters et al., Software Engineering at Google (O’Reilly, 2020), глава про эволюцию API.
  • Git Documentation, git-log (опции -S, -G, -L) — git-scm.com/docs/git-log.
  • Git Documentation, git-bisect и режим rungit-scm.com/docs/git-bisect.
  • Conventional Commits — соглашение о сообщениях коммитов: conventionalcommits.org.
  • Software Heritage — архив исходного кода: softwareheritage.org.
  • Архив списков рассылки ядра Linux с поиском — lore.kernel.org.

Что дальше

Источник найден. Теперь главный вопрос всего трека: можно ли ему верить. Проверка: дата, автор, воспроизводимость.

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

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

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

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