Backend Meetup от OneTwoTrip: жизнь в монолите
18 сентября 2025 прошёл наш офлайн-митап для бэкендеров. Рассказываем, о чём была презентация Димы Камынина, который подробно обсудил с аудиторией монолитный подход к разработке и объяснил, почему его команда придерживается именно монолита, хотя в OneTwoTrip практикуют и микросервисный подход, а также какие инструменты использует.
Запись выступления
О спикере
Дмитрий Камынин — backend-разработчик в команде «Отели». В OneTwoTrip он работает больше двух лет. Решает задачи, связанные с поиском и бронированием отелей, нотификациями, промокампаниями, подборками, отзывами, ценообразованием — в общем, со всем, что интересует пользователей, которые хотят выбрать на OneTwoTrip жильё.
Почему именно монолит?
Монолит решает все наши задачи. Во-первых, для нас важна скорость доставки задач на прод. В монолите очень просто катить релизы, поэтому в команде «Отели» нам удается релизить два раза в неделю. Во-вторых, с ним удобнее тестирование: одна ветка на один репозиторий, все базы и функционал там. В-третьих, с монолитом у нас нет лишних сетевых взаимодействий, а значит, опять же высокая скорость. При просадках сети гораздо меньше аффекта на сервер. А ещё монолит — это целостность и согласованность данных, потому что отсутствуют распределённые транзакции.
В общем, для нас — одни плюсы.
Общие подходы к разработке
Основной язык у нас в компании — JavaScript, то есть динамически типизируемый язык. Но его можно и нужно типизировать через JSDoc. На компиляции это не скажется, но поможет отлавливать ошибки в IDE и не работать вслепую. Типы можно создавать как внутри модуля инструментами JSDoc, так и выносить общие типы в отдельные .d.ts файлы и использовать во всём проекте.
Если же типизация не помогла или что-то пошло не так — а это бывает, когда речь идёт о проектах с большой связанностью модулей (вы потрогали что-то в одном месте, а в другом всё сломалось) — на помощь приходят feature-флаги. На данный момент они у нас не просто такие, которые могут включить или выключить функционал — мы сделали их достаточно гибкими, то есть параметрическими. Так что мы можем отключать часть функционала в зависимости от точек продаж, типа оплаты, стран и так далее. То есть новые функции мы покрываем feature-флагами и выкатываем на прод — и если что-то идёт не так, необязательно откатывать всё: мы можем выключить определённый кусок и спокойно дальше дорабатывать функционал в своём режиме. Важно: feature-флаги должны быть типизированы и быстро конфигурируемы.
Следующий важный момент в нашем подходе к разработке — тесты. Причём тут хочется упомянуть не юнит-тесты, которые подвержены изменениям при рефакторинге. Гораздо лучше помогает e2e-тестирование. Мы на практике сталкивались с тем, что при достижении определённого покрытия e2e-тестами рефакторинг становится гораздо проще.
Не менее важно грамотно продумывать логирование функционала. Кроме того, мы активно используем мониторинг и алерты: ключевые метрики необходимо наблюдать в реальном времени, а все алерты должны иметь смысл (то есть всем должно быть ясно, кто, как и когда на них реагирует). Важно на практике подобрать оптимальное количество алертов: если их мало, можно что-то пропустить, если слишком много — глаз замыливается, и на них перестают реагировать.
А ещё важно описывать изменения структуры БД через миграции, чтобы пропускать это через ревью и в случае чего быстро откатиться до нужной версии.
Конфигурация
Какие задачи мы ставим перед нашей конфигурацией? Нам нужно большое количество конфигов (сейчас в проекте — больше 3 000), независимое тестирование и возможность быстро всё менять (в течение минуты, а то и нескольких секунд). На 07:50 можно увидеть, как мы все эти задачи решали.
Отметим, что если у вас всего одна база, достаточно много тестовых окружений и много разработчиков, которые все с этой базой работают, то образуется довольно высокая степень зависимости. Один разработчик что-то поменял, а все остальные не понимают, что происходит. Чтобы этого избежать, можно придумывать различные регламенты, как-то договариваться, кто и что может менять — а можно пойти дальше и предусмотреть изолированность конфигураций. Мы именно так и сделали, и с 9:15 можно посмотреть и послушать, что в итоге получилось.
Нагрузочное тестирование
Теперь расскажем, как мы всё это нагрузочно тестируем. Потому что и юнит-тесты, и e2e-тестирование — это, конечно, хорошо, но когда мы что-то трогаем и боимся, что это замедлит поиск, нам ни то ни другое не поможет. И единственный выход — именно нагрузочное тестирование.
Для него мы выбрали инструмент K6 от Grafana. Во-первых, в нём используется синтаксис, очень схожий с JS. Во-вторых, в него низкий порог входа благодаря прекрасной документации и лёгкому подключению. В-третьих, в коробочной версии есть подробный отчёт тестирования.
Что можно делать в K6:
- Задавать количество потоков.
- Задавать различные сценарии нагрузки.
- Устанавливать трешхолды (если хотя бы один упадёт, то всё останавливается, и мы больше ничего не нагружаем).
- Устанавливать assret-ы (то есть ручные проверки, как в обычном тестировании).
- Импортировать в Kibana и в live-режиме смотреть, как меняются графики и как себя ведёт наш сервер.
Чуть подробнее по каждому пункту — с 12:20. А с 13:35 можно увидеть, что именно есть в отчёте.
Мы в OneTwoTrip используем K6 для регресса и проверки оптимизаций. То есть когда мы что-то правим и думаем, что могли как-то при этом затронуть функционал — K6 позволяет проверить, так ли это. Можно прогонять локально, на тестовом окружении или на нескольких тестовых.
А ещё мы с K6 проверяем наши возможности, то есть когда хотим узнать, сколько выдержим rps. Здесь нужна либо инфраструктура, идентичная продакшену, либо прогон на проде. Мы используем именно последний вариант, подробнее — с 15:14 (спойлер: это не так опасно, как может показаться).
И что в итоге?
Монолит решает все задачи, которые мы ставим, поэтому смысла переезжать на микросервисы именно в нашей структуре мы не видим. Ведь благодаря монолиту мы получаем:
- Быстрое тестирование и релизы.
- Отсутствие лишнего сетевого взаимодействия.
- Нет распределённых транзакций.
Однако у нас большой проект с высокой степенью связанности, поэтому важно предусмотреть несколько моментов:
- Конфигурация должна быть гибкая, динамическая и независимая.
- Важно покрыть всё тестами, по необходимости и нагрузочными тоже.
- Должен быть качественный мониторинг (дашборды, логи, алерты).
- Нужна подробная документация (типизация, миграции).