Перезапуск Вебиума

Начали с большого старого проекта на руби, поддерживаемого аутсорсерами, а оставили отлично документированный и покрытый тестами проект с техническим директором и внутренней командой разработки.

Вебиум — онлайн-школа для подготовки к ЕГЭ. 30 тысяч школьников, тысяча наставников, 20 тысяч вопросов и 2 тысячи домашних заданий на сотни тем.

В 2021 году у Вебиума уже была рабочая система на Ruby, которую разрабатывали подрядчики-аутcорсеры. К нам они обратились с привычной проблемой — подрядчики медленно пилят фичи. Посмотрев код и оценив возможности команды, мы поняли, что рефакторить существующий код — долго и дорого, и решили перезапустить систему своими руками на привычном стеке — Django и Nuxt.js. Справились за год: с сентября 2022 все ученики покупают и проходят курсы в новой системе.

Перед нами стояли 3 задачи:

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

  2. Нанять технического директора и создать внутреннюю команду разработки, чтобы в будущем не зависеть от аутсорса.

  3. Сделать всё это «на лету», без остановки образовательного процесса и потерь для бизнеса.

Главная страница Вебиума — онлайн-школы, которую ждал нескучный перезапускГлавная страница Вебиума — онлайн-школы, которую ждал нескучный перезапуск

Дедлайн всего проекта — сентябрь 2022. Министерство образования не станет переносить ЕГЭ из-за того, что мы не успели доделать, например, отправку уведомлений в VK.

Чтобы не подписываться сразу на гигантский годовой проект, мы разбили работу на 2 больших этапа — запуск нового магазина и запуск новой платформы обучения (Learning Management System, LMS). Магазин — часть сайта, где школьники и родители покупают курсы. В LMS школьники учатся: смотрят вебинары и учебные материалы, решают задания и общаются с наставниками.

Благодаря такому разделению мы смогли запустить магазин не дожидаясь разработки новой LMS. Но есть и сложность — пришлось подружить новый магазин со старой LMS, которую разрабатывает чужая команда.

Часть первая: перезапуск магазина. Сентябрь 2021 — февраль 2022

Перезапуск магазина давал два ключевых профита бизнесу: корзину и прозрачную аналитику. В старом Вебиуме было серьёзное продуктовое ограничение — можно было купить только один курс в одном чеке. Хочешь второй курс — проходи всю корзину ещё раз. Это плохо и для бизнеса и для учеников. Для бизнеса — это низкий средний чек, а для учеников это ухудшает качество наших услуг — ни один ВУЗ не принимает студентов по результатам только одного предмета. Технический ВУЗ может требовать информатику и математику, фармацевтический — химию и биологию, и почти везде требуется русский язык.

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

Вот так выглядит корзина в новом магазинеВот так выглядит корзина в новом магазине

Архитектура

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

При переносе пользователей заметили отклонение от правила Парето — мы перенесли 99% (!) пользователей и считали свою работу успешной. Но выяснилось, что 1% оставшихся пользователей делали у нас кучу заказов, и они настолько важны для бизнеса, что без них задачу нельзя было считать решённой. На перенос этого 1 золотого % у нас ушло больше времени, чем на 99% остальных!

Система, которая как-то работает, у Вебиума уже была. Перед нами стояла задача сделать систему, которую будет удобно поддерживать и развивать будущей ин-хаус команде Вебиума. Новая система получалась сложной, поэтому мы с самого начали наняли Антона Давыдова на роль архитектора. Он разобрался в деталях бизнеса и спроектировал основные составляющие системы ещё до того, как мы написали первую строчку кода, а самое главное — оставил понятную документацию, которая сильно ускоряет погружение новых программистов в работу над Вебиумом даже после нашего ухода.

Кусочек функциональной схема добавления товара в корзину с учетом скидок. Нужно учесть много edge-кейсов — вся схема в 5 раз больше.Кусочек функциональной схема добавления товара в корзину с учетом скидок. Нужно учесть много edge-кейсов — вся схема в 5 раз больше.

У старой и новой системы 2 ключевые точки соприкосновения: пользователи и покупки. Для пользователей мы придумали однонаправленный поток данных с новым магазином в качестве источника правды. Регистрация и изменение пользователей происходит на стороне магазина. События об изменениях стримятся в старую LMS. Если студент заходит на страые страницы управления профилем в LMS — мы его редиректим на магазин.

Открытие доступов к курсам тоже работает однонаправленно:  при покупке курса в новом магазине создается событие, в ответ на которое старая LMS открывает доступ студенту. Отправка событий построена на RabbitMQ.

Инфраструктура

Для маршрутизации трафика мы использовали уже проверенную на Снобе комбинацию из traefik и Express.js. Express.js выполняет роль BFF (Backend for Frontend). Это быстрый прокси-сервер, у которого несколько важных функций:

  1. SSR (Server Side Rendering) важных с точки зрения SEO-страниц. Поисковики — важный источник трафика для Вебиума.

  2. Управление аутентификацией. Мы вместе с аутсорс-командой старой системы спроектировали и запрограммировали систему кросс-аутентификации: вход/выход в одной системе автоматически приводит к входу/выходу в другой системе.

  3. Маршрутизация запросов между новой и старой системой. Благодаря прокси комбинация из нового магазина и старой LMS выглядела единой системой для пользователя.

Все неизвестные пути Express.js направляет на обработку в legacy-систему. Если после запуска нужно откатиться на старую систему — мы просто отключаем прокси. Старая система продолжает работать как ни в чём не бывало.

Схема маршрутизации трафика с помощью прокси на Express.jsСхема маршрутизации трафика с помощью прокси на Express.js

На схеме показаны примеры 3 видов запросов:

  1. /privacy-policy/ (желтый) — статичная страница нового магазина. Express.js обрабатывает запрос и отдает результат клиенту.

  2. /api/v2/courses/ (синий) — запрос к новому бэкенду. Запрос проксируется в новый Python-бэкенд, результат отдается клиенту.

  3. /certificates/ (красный) — страница из LMS. Express.js приложение ничего не знает про такой путь. Запрос проксируется в Ruby-приложение, результат отдается клиенту.

Запуск

Запуск новой большой системы — всегда риск. Особенно когда система напрямую отвечает за продажи.

Мы запускались в преддверии старта продаж Весеннего Фреша — второго по важности и прибыли курса Вебиума. Сломать флоу оплаты на старте совершенно неприемлемо — один день простоя стоит миллионы рублей. Упускать выгоду от запуска корзины удобной оплатой и классной системой скидок тоже не вариант — бизнес у Вебиума сезонный.

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

До Вебиума у нас был опыт большого перезапуска Сноба. Cноб нам пришлось откатывать три раза — поэтому мы подготовились по полной.

Чеклист запуска. Мы подготавливаем чеклисты к каждому запуску нового продукта.Чеклист запуска. Мы подготавливаем чеклисты к каждому запуску нового продукта.

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

Ни один пользователь не столкнулся с проблемами непосредственно во процессе оплаты — все, кто хотели заплатить Вебиуму деньги, смогли это сделать. Мы этим гордимся.

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

Часть вторая: Перезапуск LMS. Март — сентябрь 2022

После запуска магазина перед нами стояла задача перезапустить LMS — платформу, где ученики смотрят вебинары, решают задания и общаются с наставниками.

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

Интерфейс общения учеников и наставников — у каждого решенного задания своё обсуждениеИнтерфейс общения учеников и наставников — у каждого решенного задания своё обсуждение

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

  1. Работа с курсами, расписанием занятий и календарём, отдельно для учеников и для наставников.

  2. Автоматическое и ручное распределение учеников по группам.

  3. Учебный контент: просмотр вебинаров и уроков, решение и проверка заданий.

  4. Просмотр и рассылка уведомлений на сайте и в VK для учеников и сотрудников.

  5. Чаты с обсуждением заданий: отдельно для учеников и наствников.

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

Так выглядит страница курса у ученикаТак выглядит страница курса у ученика

Архитектура

Когда мы делали магазин, мы сразу думали про новую LMS. Со стороны магазина ничего не менялось — он по прежнему складывает события в RabbitMQ и не думает, какая LMS их потребляет: новая или старая.

Высокоуровневая архитектура нового Вебиума из архитектурной документации, которую мы передали вместе с проектомВысокоуровневая архитектура нового Вебиума из архитектурной документации, которую мы передали вместе с проектом

С точки зрения архитектуры LMS разделена на следующие элементы:

  1. Курсы и их жизненный цикл

  2. Распределение учеников по группам

  3. Аутентификация и авторизация и управление ролями сотрудников

  4. Аутентификация и авторизация учеников

  5. Уведомления

  6. Учебный контент

  7. Чаты наставников и учеников

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

Коммуникация между ключевыми элементами LMS. Стрелки в левой части ведут из магазина.Коммуникация между ключевыми элементами LMS. Стрелки в левой части ведут из магазина.

Инфраструктура

Код нового Вебиума хранится в 6 репозиториях:

  1. Бэкенд магазина

  2. Фронтенд магазина

  3. Бэкенд LMS и админки LMS

  4. Фронтенд LMS

  5. Фронтенд админки LMS

  6. Инфраструктура

Мы полностью разделяем кодовые базы магазина и LMS. Это даёт гибкость — нам не нужно учитывать маркетинговые фичи в сущностях учебного процесса и наоборот. В старой системе такого разделения не было — это мешало быстро тестировать продуктовые гипотезы.

Мы храним бэкенд LMS и админки в одном репозитории, а фронтенды — в разных. Бэкенд общий, потому что данные довольно сильно размазаны между системами. К примеру, с точки зрения бэкенда домашнее задание, которое решает ученик и проверяет наставник — одна и та же сущность. Фронтенды отдельные, потому что с точки зрения интерфейса решение домашки и её проверка — совершенно разные сценарии. У фронтенда LMS и админки разные дизайн-системы, разные пользователи, разные требования к качеству интерфейса и оптимизации.

Для мониторинга  и трекинга ошибок мы используем проверенные инструменты — Datadog и Sentry. Для CI/CD впервые использовали GitHub Actions (раньше использовали Circle) и остались довольны.

Мониторинг в DatadogМониторинг в Datadog

Запуск  LMS

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

В итоге систему запускали частями

  1. webium.ru — сайт магазина (уже был)

  2. lms.webium.ru — сайт LMS. До запуска там была старая LMS, после запуска — новая.

  3. Админка LMS.

  4. Запасной домен, на который переедет старая LMS после запуска.

С админкой LMS всё просто — сайт просто нужно включить.

Перенос LMS для учеников мы разбили на 2 этапа. На этом этапе перенесли старую систему на отдельный домен. На основном домене LMS включили новую систему, но она редиректила все запросы на старый. С точки зрения инфраструктуры работала новая LMS, а с точки зрения продукта и пользователя — старая. Это позволило нам отловить баги инфраструктуры и старой системы, в которой местами был захардкожен адрес. Когда все баги отловили — мы просто выключили редирект и ученики попали в новую систему.

Разделение на этапы помогло нам снизить количество действий и суету на запуске. Базовые проблемы с инфраструктурой и проблемы продуктом можно было решать в разное время. Ну и конечно, мы добавили себе ментального спокойствия — когда есть возможность откатиться назад одной командой, чувствуешь себя гораздо увереннее.

Мы настолько дотошно подготовились к продакшену, что запуск получился очень спокойным. Вот такую обратную связь оставляли ученики в чатах Вебиума.

Обратная связь от учеников в день запускаОбратная связь от учеников в день запуска

Конечно потом вылезли проблемы — где-то не хватало фич для критичных процессов, какие-то части системы падали от нагрузки. В течение месяца после запуска мы исправили все эти проблемы.

Что дальше

За год мы перезапустили всю техническую платформу Вебиума без остановки учебного процесса. Теперь у бизнеса есть возможность бодро тестировать новые продуктовые гипотезы, а по прозрачной аналитике видно, в какие из них стоит вкладываться.

За время перезапуска Вебиум с нашей помощью нанял технического директора и построил внутреннюю команду разработки. Ребята программировали платформу бок о бок с нами, теперь они будут развивать уже знакомую им систему дальше.

Сейчас у Вебиума есть не только классные руководители, маркетологи, продакты, проджекты, дизайнеры, наставники и преподаватели, но и классные программисты. А мы очень гордимся проделанной работой. И готовы двигаться дальше.

Команда

  • Никита Алешников, бэкенд-разработчик

  • Андрей Бацунов, фронтенд-разработчик

  • Алексей Богословский, фулстек-разработчик

  • Фёдор Борщёв, технический директор

  • Тимур Брачков, фронтенд-разработчик

  • Михаил Бурмистров, ведущий фронтенд-разработчик, руководитель проекта

  • Самат Галимов, технический директор

  • Антон Давыдов, архитектор

  • Николай Кирьянов, бэкенд-разработчик

  • Никита Лазаренко, бэкенд-разработчик

  • Вячеслав Набатчиков, бэкенд-разработчик

  • Александр Нестеров, фронтенд-разработчик

  • Ксения Сафронова, менеджер проекта

  • Эдуард Степанов, бэкенд-разработчик

  • Владимир Тарановский, фронтенд-разработчик

  • Алексей Чудин, ведущий бэкенд-разработчик

Вебиум — наш заказчик:

  • Роксана Боровик, генеральный директор

  • Виктория Гармаш, продакт-менеджер

  • Никита Савостин, технический директор

  • Александр Евграфов, ведущий дизайнер

  • Богдан Пилявец, ведущий аналитик

  • Андрей Алейников, аналитик

  • Михаил Герун, менеджер проекта

  • Евгений Новиков, менеджер проекта

  • Кирилл Стариков, фронтенд-разработчик

  • Сергей Волков, фронтенд-разработчик

  • Елена Микиртумова, верстальщица

  • Роман Ковалев, ведущий SEO-специалист

  • Ольга Рокоссовская, техподдержка и тестирование

  • Катерина Климова, техподдержка и тестирование

  • Павел Романов, технический директор на этапе подготовки перезапуска

  • Евгений Юрьев, разработчик legacy-системы

  • Илья Конаныхин, разработчик legacy-системы

  • Михаил Сахно, бэкенд-разработчик

Автор статьи: Михаил Бурмистров

© Habrahabr.ru