Backend Meetup от OneTwoTrip: жизнь в монолите

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 (спойлер: это не так опасно, как может показаться).

И что в итоге?

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

  • Быстрое тестирование и релизы.
  • Отсутствие лишнего сетевого взаимодействия.
  • Нет распределённых транзакций.

Однако у нас большой проект с высокой степенью связанности, поэтому важно предусмотреть несколько моментов:

  • Конфигурация должна быть гибкая, динамическая и независимая.
  • Важно покрыть всё тестами, по необходимости и нагрузочными тоже.
  • Должен быть качественный мониторинг (дашборды, логи, алерты).
  • Нужна подробная документация (типизация, миграции).
Начать дискуссию