В предыдущих статьях я уже разобрал личный опыт вхождения в IT, когда это еще не было мейнстримом, а также привел советы по увеличению откликов на ваше резюме. Всего я запланировал 7 статей в серии помощи поиска работы в IT для начинающих специалистов.
Собесбот - это Telegram-бот для подготовки к собеседованиям для специалистов уровня Junior, Middle, а также для тех, у кого нет коммерческого опыта в IT. В этой статье я разберу принцип работы бота, почему я решил его сделать, и трудности, с которыми я столкнулся при разработке.
На данный момент есть оценка и рекомендации по: C#, Java, Kotlin и JavaScript. В целях добавить Python, конечно же.
Зачем?
Количество выпускников онлайн-школ продолжает расти, однако многие сталкиваются с трудностями при поиске первой работы в IT. Да и джунам, у которых есть небольшой опыт приходится несладко (статистики у меня нет, просто по общим наблюдениям в соц. сетях). У них встает резонный вопрос: Как подготовиться к собеседованию в IT? Хотелось сделать продукт, от которого будет польза при подготовки к собесам, но который бы обходился для меня минимумом затрат.
Кроме того, хотелось разработать свой продукт от идеи и до запуска. В итоге, в ходе разработки я испытал все, даже хайринг людей в команду.
Почему именно бот в Telegram?
Как любой разработчик с опытом, я наслышан о кладбище пет-проектов. Люди начинают писать пет-проекты, которые так и остаются незаконченными. У меня таких есть тоже. Именно поэтому я какое-то время вообще избегал разработки своих проектов. Основная проблема, с которой я сталкиваюсь при разработке проектов — склонность к усложнению, углубление в детали, что в итоге приводит к потере мотивации. В этот раз мне очень хотелось углубиться в разработку. Но я решил пойти по пути наименьшего сопротивления. Один из моих страхов - это фронтенд-разработка. Поэтому я решил делать frontendless решение. К тому же, Telegram бот - удобный формат для пользователей. Не нужно скачивать мобильное приложение или открывать сайт. И с точки зрения разработки меньше шансов на оверинжинирить. К тому же опыт разработки Telegram ботов и желание применить AWS в реальном продукте также повлияли на мой выбор.
Все начиналось со скрипта
В 2021 году я сделал мини-курс по подготовке к собеседованиям. Это были 4 насыщенных вебинара, каждый из которых включал практические задания для закрепления материала. Мой первый продукт, в который я вложил много души. Возможно, именно после него я и выгорел. Запустил курс один раз, но дальше дело не пошло. В этом году я пересмотрел материалы курса и понял, что многие концепции остаются актуальными и полезными даже сейчас. В курсе был скрипт для генерации квизов с вопросами для собеседований на Google Forms. Он случайным образом собирал вопросы из большой базы вопросов с группировкой по областям. Готовые формы я скидывал участникам. У каждого был уникальный набор вопросов и на основании результата можно было определить какие области и навыки стоит подтянуть. На основе этого скрипта и родилась идея создания бота в Telegram для подготовки к собесам.
Принцип работы
Пользователь регистрируется в боте, указывает свою специализацию и уровень.
У него есть возможность определить свой уровень с помощью скринингов (доступно 9 специальностей).
Скрининг — это серия тестов в Google Forms. Обычно это от 14 до 17 тестов с 4-11 вопросами каждый. Вопросы охватывают определенную область знаний. Например, это может быть LINQ для C# .NET или Firebase для Android. Пользователю нужно ответить на вопросы, которые бывают разного типа: одиночный выбор, множественный выбор, сопоставление, или вопросы с коротким текстом.
После прохождения каждого теста пользователь возвращается в бот и переходит к следующему шагу. Можно пропустить тест или остановить скрининг. Прохождение скрининга можно завершить только один раз, без временных ограничений.
По итогам скрининга бот создает разбивку по навыкам, показывая, какие области нужно подтянуть. Главная фича для новичков — это персональные рекомендации на основе результатов. Они включают статьи, видео, бесплатные курсы, код и документацию.
Также можно запросить мок-собеседование (пока доступно только по .NET) и карьерную консультацию. Я выступаю в роли ментора.
С прогрессом и прохождением нескольких скринингов профиль пользователя наполняется новыми навыками, и рекомендации становятся более точными.
-
Навигация в боте происходит через inline-кнопки, и все взаимодействие строится по принципу "одно сообщение в момент времени". Это очень похоже на обычный SPA или мобильный клиент. Есть текущее View и мы можем переходить из него на другие.
Выбор стека
Для начала оговорюсь, что это мой первый бот в Telegram. Я никогда раньше работал с AWS. И первый раз нанял разработчика для совместной работы над своим проектом. Что оказалось не такой плохой идеей. Ну и в заключении, я выбрал Shift-right testing, чтобы не тратить время на интеграционные тесты и поскорее зарелизить бота.
Самый близкий мне язык и фреймворк это C# и ASP.NET Core. При выборе инфраструктуры руководствовался минимизацией затрат и масштабированием. Для взаимодействия с ботом выбрал вебхуки. Каждый Update обрабатывает ASP.NET Core приложение задеплоеное на AWS Lambda. В качестве БД Amazon DynamoDB. Выбор пал в пользу этих сервисов в силу больших лимитов во Free Tier и желания написать свой пет-проект на AWS. Выбрал GitHub как систему контроля версий и Notion в качестве трекера задач. Деплоил со своей машины.
Итого внешних зависимостей:
AWS Lambda
DynamoDB
Google Forms
Telegram API
Разработка
Разработка началась в мае 2024-го года. На протяжении всего лета я посвящал свободное время разработке этого бота. Первый бот был готов уже через 3 дня. Он повторял логику скрипта по генерации опросника . Как человек, в первый раз написавший бота в Telegram, да еще и в первый раз слепил что-то в AWS я безумно радовался.
Интеграция с Google Forms
Однако, потом я начал думать над следующими шагами и пришел к цепочке неприятных рассуждений.
Проблема №1
Если мы генерируем тесты в Google Forms из базы, то как избежать дублирования вопросов и при этом обеспечить откалиброванную подборку по сложности?
Решение: Я понимал, что риску попасть в ловушку усложнения. Решил не генерить тесты на лету, а заранее создавать наборы, сгруппированные по специализации. Но это пораждает новую проблему - по какому принципу группировать вопросы в тесте?
Также встал вопрос, насколько будет удобно пользователю переключаться между Google Forms и Telegram? Вместе с этим следующая проблема:
Проблема / Вопрос №2
А почему Google Forms, если Telegram дает возможность создавать квизы?
Ответ: К сожалению, в квизах можно использовать только вопросы с одиночным выбором. Нет сопоставлений, короткого текста и множественного выбора. Google Forms дает возможность удобного хранения и управления вопросами, их быструю и легку правку, а также просмотра статистики ответов. Отвечает принципам frontendless.
Ничего не оставалось как смириться с потенциальными неудобствами для пользователей. Конечно же, такое сомнение толкает к следующему вопросу:
Проблема / Вопрос №3
Почему бы не сделать веб или мобильное приложение, где нет ограничений?
Ответ: Я старался избегать фронтенд-разработки, чтобы минимизировать риск попадания в ловушку избыточной сложности. Я решил не изучать новую технологию, чтобы не растягивать сроки разработки. К тому же, Telegram бот довольно удобный формат для пользователей - всегда под рукой и не надо скачивать.
Казалось, что разработка бота с использованием внешней зависимости в виде Google Forms - это гиблое дело. Но, следуя принципам frontendless приложений, чтобы не увеличивать срок разработки на изучение фронтенда и дизайн сайта, а быстрее выпустить MVP - я решил рискнуть.
Многие пользователи, особенно из мира IT любят оставлять минимум информации о себе. Соответственно,лучше не запрашивать дополнительные данные, например логин в Google аккаунт при ответе на вопросы в квизе. Скорее всего, это не самый приоритетный запрос, который стоило брать в разработку. Но я его взял.
Проблема №4
Как сопоставить ответы пользователя в форме без аутентификации в Google Forms?
Решение A: Копировать квиз для каждого пользователя и отдавать ссылку на уникальный квиз. Это решение полностью решает проблему. Однако это дополнительный запрос к API Google Forms для создания дубликата формы. Со временем бесполезных форм становится все больше и нужна будет периодическая чистка Google Drive.
Решение Б: Использовать prefillLink с предзаполненным поле в форме. Это текстовое поле содержит идентификатор квиза в базе данных, по которому мы сопоставляем ответы пользователя и квиз. Тоже решает проблему, однако не создает лишних форм, уменьшает запрос к Google Forms API, а еще мы получаем статистику ответов. Минус такого подхода: лишнее поле, очистив которое бот не сможет сопоставить ответы и придется проходить квиз заново.
Слоистая архитектура
Для своих сервисов я выбрал трехслойную архитектуру, основывающуюся на DAO, DTO и Rich Domain Model. Однако, несмотря на преимущества такого подхода, он добавил значительный оверхед, что привело к усложнению разработки. Каждый раз после нового запуска бота я тратил по несколько дней на его отладку. Одной из главных причин ошибок оказался некорректный маппинг, для которого я использовал Mapperly. Эта отличная библиотека значительно сэкономила мне время. Но из-за частых изменений в моделях и множества слоев - конечно я постоянно наступал на грабли.
Переход на микросервисы
Еще одной слабостью, перед которой я не смог устоять это разделение функциональности на микросервисы. В итоге их получилось 4:
Сам бот (там же сейчас и управление пользователями),
Сервис работы с квизами через Google Forms
Сервис оценки результатов скринингов
Сервис рекомендаций
Да, это добавило сложности и увеличило время разработки. При этом это упростило процесс тестирования и ввода данных. Можно было легко протестировать каждый сервис по отдельности, пусть даже и вручную. Большая часть ошибок было конечно же в сервисе бота. Но он и спроектирован с 2-мя ответственностями, вместо одной. Это тоже записал в технический долг.
Работа в паре
Это было действительно нестандартное решение. Я понимал, что несмотря на мои усилия по самоконтролю сложность бота растет и он рискует никогда не выйти в публичный доступ. Хотелось технически подкованного человека с горящими глазами, который бы разделил ответственность и владение ботом.
Денег у меня не было. Я не надеялся, что кто-то откликнется на работу без оплаты, но все-таки выложил объявление о поиске разработчика в чатики с вакансиями. Отозвались 10 человек, из которых 3 согласились работать без оплаты. Провел со всеми собеседования. Было особенно волнительно, в итоге отобрал одного джуна. Он не мог найти работу разработчиком больше года и готов был посвящать все свое время проекту. Круто, подумал я, хотя понимал, что как только он найдет работу его желание вкладываться куда-то еще упадет.
Мы проводили ежедневные звонки и синхронизировали статус задач. Вели задачи в Notion. В основном на звонках я рассказывал и объяснял скоуп задач. Для меня это был своеобразный метод утенка, а для него по сути менторская сессия и работа над проектом. Кроме того, даже сам факт присутствия у тебя в команде другого человека, с которым вы периодически созваниваетесь - хороший мотиватор (Accountability partner). Однако,
Проблема №5
Напарнику не хватает опыта быстро уловить суть, проще сделать самому, чем объяснить.
Решение: Дать один из второстепенных компонентов под полную ответственность. У него будет больше времени и больше автономии. И это сработало… частично. После череды юнит-тестов он переключился на один из сервисов, который был дальше по плану. Однако после первого ревью стало очевидно, что он не понял структуру данных и ему нужна более подробная задача.
Несомненно, работа в паре приносит пользу - появляется какая-то скрытая ответственность перед человеком, не хочется подводить. Но пользы будет больше для джуна, чем для лида. А еще через месяц после совместной работы над ботом мой напарник получил оффер.
Трекинг задач в Notion
В Notion мы вели трехуровневую систему задач с разделением на эпики, фичи и что-то похожее на юзер стори. Использовали Kanban доску.
Проблема №6
Нужна приоритезация. Но в Notion нельзя легким кликом отправить задачу вверх колонки на доске.
Решение: реализовать приоритезацию на основе собственного приоритета задачи и ее родителя. Например, если эпик имеет приоритет 1. После умножения его приоритета на 100 все его задачи получают приоритет 100. Внутри этого эпика могут быть фичи и их приоритеты умножаются на 10. Это то, что пришло в голову и получилось слепить на коленке. Подход имеет свои минусы, но в большинстве случаев рабочий.
Библиотека для обработки событий от Telegram бота
Первые пару недель я изучал возможности Telegram API и разные решения для работы через вебхуки.
Взаимодействие Telegram-бота через вебхуки происходит следующим образом: бот регистрирует URL вебхука, на который Telegram отправляет каждое новое событие (объект update), например, сообщение пользователя или нажатие кнопки. Объект update содержит всю информацию о событии (ID чата, текст сообщения, данные о пользователе и т.д.).
К сожалению, не нашел готовых библиотек, упрощающих разработку с бота на .NET через вебхуки. Пришлось делать свою. Долго разбирался, какие существуют подходы, в итоге бросил, и реализовал следующую связку:
Любое взаимодействие с ботом, кроме /start происходит через нажатие кнопки из встроенного в сообщение меню и передачи callbackData. При этом слой View - это текст и кнопки у каждой из который свой callbackData. Команды содержат примитивные параметры, а их обработчики основную логику бота. Вроде бы рабочая схема, но на этапе отладки я узнал, что:
Проблема №7
В callbackData можно засунуть максимум 64 символа.
Решение: В качестве костыля я хэшировал данные, для уменьшения объема данных. По-хорошему можно сопоставить команду записи в БД через идентификатор, который умещается в callbackData. Но я решил взять это в технический долг.
Разобравшись с Telegram API я написал небольшую библиотеку, реализующую вышеуказанный процесс обработкий апдейтов от бота. Туда же добавил отображение сообщения об ошибке вместо зависания и подсчет запросов на нереализованные фичи. Если в боте, есть какая-либо нереализованная фича, то пользователь может “записаться” в лист ожидания и проголосовать за эту фичу. Самые популярные фичи можно в последующем приоритезировать. С этим проблем не было, но как правильно разработать навигацию?
Проблема №8
Если в callbackData нельзя хранить большие команды, то как реализовать команду “Назад”?
Решение: Здесь уже пришлось хранить команду в БД. После такой реализации усилилось желание переделать предыдущий костыль. Но смиренно я сдержался.
Первые версии бота со скринингами на базе этой библиотеки появились уже через полтора месяца. Удивительно, но при полном отсутствии интеграционных тестов тогда я не так много потратил времени на отладку. Мне понравилось - работает как ожидал. Анимация удаления и создания сообщения выглядит красиво. Но через пару дней мой напарник заметил, что бот стал зависать. Полез в логи и увидел, что почему-то Telegram ругается на удаление сообщения. Кое-как нашел информацию про максимальный срок для удаления сообщений бота в Telegram.
Проблема №9
Telegram API не дает удалить сообщения старше 48 часов отправленные ботом
Решение: Вместо удаления реализовал редактирование последнего сообщения. Идентификатор сообщения храню в базе данных.
Забегая вперед, скажу, что через пару месяцев это решение породило следующую проблему.
Проблема №10
С течением времени бот сползает вниз в списке чатов в Telegram, так как используется только одно сообщение для взаимодействия с пользователем и оно было создано давно.
Решение: Вернуть удаление. Но сделать его умнее. Если сообщение младше 48 часов, но старше 24 часов, то у нас еще есть окно для удаления и создания сообщения. Если же сообщение старше 48 часов - то заменяем текущее сообщение следующим текстом и добавляем новое. Этот метод я назвал мягким удалением.
Больше проблем при работе с Telegram API я не припомню. Разве что странный markdown, но будем считать это особенностью.
Метрики и воронки
Перед запуском бота и публикацией его в различные Telegram каналы и сообщества я сделал небольшой сервис для подсчета основных метрик. Мне было интересно сколько всего пользователей их разбивки по грейдам и специализациям. Самая важная для меня метрика - это количество завершенных скринингов, потому что завершенный скрининг приносит наибольшую ценность для того, кто готовиться к собеседованию в IT. Там и разбивка по скиллам и рекомендации. Для того, чтобы мотивировать людей на начало нового скрининга или прохождение уже начатого я реализовал небольшой сервис для “продвижения” людей в воронке.
Заключение
Через 4 месяца я уже достаточно подустал от разработки. Мне хотелось скорее его запустить. Я получил очень ценный опыт разработки своего MVP. И пусть он не монетизируется, но зато приносит людям пользу даже пока я сплю.
Через пару недель после выпуска Собесбота я также решил создать группу в Telegram для джунов и стажеров, которые готовятся к собеседованиям в IT.
Вступайте в нашу группу в Telegram - там обсуждаем вопросы, связанные с подготовкой к собеседованиям, даем обратную связь и помогаем друг другу.