Неделя после Хабра: email разработчика нашёлся в его коммитах, а скоринг раздавал 50 баллов бесплатно
Шесть дней назад я рассказывал о resume2human - Windows-приложении, которое превращает резюме в список подходящих вакансий, а затем пытается найти по каждой из них одного-трёх реальных людей: нанимающего менеджера, рекрутера или руководителя команды. Идея простая: не ограничиваться откликом через форму, а дать пользователю возможность написать конкретному человеку напрямую.
За неделю проект получил 21 коммит в девяти pull request. Количество тестов выросло с 345 до 746. При этом выяснилось, что часть прежних обещаний оказалась слишком оптимистичной. Кроме того, несколько ошибок заставляли программу честно выполнять не ту задачу, которую от неё требовали.
Вкратце: отклик через форму попадает в общий ящик, который нередко обрабатывает автоматическая система. Письмо конкретному инженерному менеджеру или рекрутеру работает иначе. Но вручную найти релевантную вакансию, затем нужного человека и его публичный контакт занимает слишком много времени. Именно эту рутину и пытается сократить resume2human.
Программа анализирует резюме, собирает предложения из 24 источников - среди них ATS-доски 111 компаний, региональные площадки, LinkedIn, Telegram, Threads и обычный веб-поиск, - рассчитывает объяснимое совпадение резюме с вакансией и формирует отчёт. Автоматической рассылки нет: письмо пользователь составляет и отправляет сам. Такой подход отличается от сервисов, где [поиск работы в IT](https://habr.com/ru/articles/1080228/?utm_campaign=1080228&utm_source=habrahabr&utm_medium=rss) сводится к массовой отправке откликов без понимания, кому именно они адресованы.
Что изменилось в первоначальной концепции
Изначально проект позиционировался прежде всего как поисковик вакансий. Теперь он заметно ближе к рабочему инструменту для всей воронки найма: появились статусы, напоминания, несколько аналитических срезов и история действий. Выяснилось, что сохранить найденные вакансии в файл недостаточно. Важно понимать, что с ними произошло дальше: отправлен ли отклик, был ли ответ, требуется ли повторное обращение.
Обещание не отправлять письма за пользователя осталось в силе. Не изменилась и позиция по поводу автоматической генерации адресов: догадки теперь существуют как отдельная ступень, но по умолчанию отключены и явно помечаются. А вот утверждение, что приложение не ведёт статусы откликов и не заменяет CRM, пришлось отменить.
Показательно, что необходимость этих изменений обнаружилась не во время проектирования, а при личном использовании программы. README продолжал описывать старую версию, тогда как код уже двигался в другую сторону. Между изменениями в поведении приложения и обновлением документации прошло два pull request. Поэтому отдельная задача недели была посвящена тому, чтобы витрина проекта не вводила пользователей в заблуждение.
Git как открытый каталог инженерных контактов
Первая версия механизма поиска людей отвечала только на вопрос: кто связан с вакансией или компанией? Но следующий вопрос - как с этим человеком связаться - оставался без ответа. В отчёте появлялись имя, должность и ссылка на профиль, однако написать человеку напрямую было невозможно.
Платные сервисы вроде ContactOut, Lusha, Apollo и RocketReach решают задачу за счёт коммерческих баз данных. Но для инженеров существует менее очевидный и часто бесплатный слой данных - Git. Каждый коммит содержит поле `author.email`. Пользователь может скрыть адрес в настройках GitHub, однако старый коммит, сделанный несколько лет назад, уже мог попасть в публичный репозиторий с реальным адресом автора.
Это не означает, что любой email из истории Git следует безоговорочно считать рабочим или уместным для контакта. Адрес может быть устаревшим, техническим, корпоративным или принадлежать другому человеку. Поэтому найденный контакт требует проверки и корректного использования. Но сам принцип важен: открытые инженерные данные нередко уже существуют, просто они распределены по репозиториям, истории изменений и метаданным.
Именно здесь появилась бесплатная ступень поиска контактов, не зависящая от платных API. Она не обещает гарантированный результат, зато позволяет обнаруживать адреса, которые разработчик когда-то опубликовал собственными действиями. Подробное описание этой архитектуры и связанных ограничений доступно в материале о [поиске контактов разработчиков через Git](https://habr.com/ru/articles/1080228/?utm_campaign=1080228&utm_source=habrahabr&utm_medium=rss).
Как скоринг раздавал 50 баллов ни за что
Самой неприятной проблемой оказался не сбор данных, а оценка совпадения резюме с вакансией. В скоринге была логическая ошибка: пустое множество учитывалось как полностью совпавшее. Формально проверка покрытия действительно могла возвращать единицу, если проверять условие на отсутствие элементов. Но в контексте рейтинга вакансий это означало, что отсутствие данных превращалось в максимальное соответствие.
В результате некоторые предложения получали до 50 лишних баллов. Система выглядела уверенной именно там, где у неё не было достаточной информации. Это хороший пример того, почему автоматический поиск вакансий нельзя строить только на сумме формальных совпадений. Любой показатель должен учитывать не только положительные признаки, но и качество исходных данных.
После исправления пустые наборы перестали считаться доказательством совпадения. Скоринг разделили на содержательные признаки, а неопределённость вынесли в отдельные предупреждения. Для пользователя это важнее красивого итогового числа: он должен понимать, почему вакансия попала в верхнюю часть списка.
Три ошибки, которые искали не то
За неделю обнаружились и другие проблемы. Один из тестов был флаки-тестом: он проверял не то поведение, которое предполагалось, и иногда проходил случайно. После исправления тестовая база выросла более чем вдвое, а проверки стали ближе к реальным сценариям.
Вторая ошибка возникла при передаче слишком большого списка аргументов системе. Ограничение `Argument list too long` проявилось в витрине со ссылками на девять несуществующих изображений. Программа формировала чрезмерный командный вызов, а пользователю в итоге показывалась пустая или повреждённая часть отчёта.
Третья проблема была связана с интерфейсом: сочетание Ctrl+C не работало при русской раскладке. Отдельно пришлось поправить интернационализацию. Вместо gettext применён более прямой подход: русские строки стали самостоятельными ключами, что упростило поддержку небольшой локальной утилиты и снизило вероятность рассинхронизации переводов.
Отклики, метрики и доверие к результатам
В проекте появилась полноценная воронка: найденная вакансия, подготовленный контакт, отправленный отклик, ответ и следующий шаг. Для писем используются два шаблона, а интервалы между повторными действиями помогают не превращать коммуникацию в спам.
Для оценки результатов добавлен интервал Уилсона. Он полезнее простой доли ответов, особенно когда наблюдений мало. Если из двух писем ответил один человек, это ещё не означает конверсию 50%. Статистический интервал показывает, насколько нестабильна такая оценка и почему малые выборки нельзя интерпретировать слишком уверенно.
Отчёты теперь можно открыть обратно в приложении. Это позволяет вернуться к прежнему результату, проверить аргументацию скоринга и продолжить работу без повторного сбора всех данных. Для доверия к файлам используются SHA-256 и подпись, хотя даже здесь обнаружилась проблема: в самом важном абзаце документа появилась ссылка, возвращавшая 404. Проверка целостности бесполезна, если пользователь не может прочитать объяснение результата.
Ограничения, которые остались
Инструмент по-прежнему не делает рассылку автоматически и не пытается заменить человека в коммуникации. Он также не гарантирует, что найденный адрес действителен, что контакт готов обсуждать вакансию или что компания действительно нанимает сейчас. Контактные данные нужно проверять, а сообщения составлять с учётом контекста.
[Анализ резюме онлайн](https://habr.com/ru/articles/1080228/?utm_campaign=1080228&utm_source=habrahabr&utm_medium=rss) тоже не превращается в магическое решение: программа может выделить навыки, роли и признаки соответствия, но не знает всех обстоятельств кандидата. Поэтому подбор вакансий по резюме должен оставаться объяснимым. Пользователь имеет право увидеть, какие навыки совпали, каких данных не хватило и почему конкретная позиция получила высокий или низкий балл.
Следующий этап - улучшение качества источников, контроль устаревших контактов и более аккуратная работа с приватностью. Публичность адреса не отменяет этических ограничений. Один короткий, персональный и уместный запрос лучше десятков одинаковых сообщений. Так автоматизация помогает сэкономить время, но не подменяет профессиональную коммуникацию.
Проект можно попробовать в текущем виде, учитывая его ограничения. Главный вывод недели оказался не в количестве коммитов и не в числе тестов: хороший инструмент поиска должен уметь не только находить вакансии, но и честно показывать, где заканчиваются данные, начинаются предположения и требуется решение самого пользователя.

