Как заново начать разрабатывать известный продукт с длинной историей и большим количеством пользователей.

Юрий Пенкин

руководитель отдела разработки серверных решений CommuniGate Pro

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

В этой статье расскажем, как мы работали с исходным кодом CommuniGate Pro, какие задачи перед нами стояли, с какими трудностями столкнулись и как их решали.

Часть 1: Смотрим

Сохранить пользовательский опыт

Что у нас есть? Исходный код on‑premise продукта, имеющий даже по публичным данным большое количество инсталляций. Для взаимодействия с пользователем, кроме стандартных IMAP/CalDAV, есть встроенный веб‑интерфейс. Из клиентских протоколов, кроме традиционных почтовых и календарных протоколов, также есть некоторое количество известных приложений на собственном протоколе XIMSS (и некоторое количество неизвестных), скрипты и инструменты администрирования на CLI, внутренние тяжелые интеграции на их основе (в основном, у крупных клиентов).

Также есть механизмы кастомизации пользовательского интерфейса и внутренней логики сервера на CG/PL.

Плюс CommuniGate Pro исторически ориентирован на администраторов, понимающих, как работает почта и календари, отсюда богатство внутренних настроек.

Довольно скоро нам стало понятно — API шире, чем описано в документации, и не все опции запуска документированы (что для продукта с долгой историей скорее правило). И, конечно, нашлись пользователи, которые это уже используют.

Отдельной строкой идёт опыт администрирования системы. Структура хранения данных традиционно «текстовая», подразумевающая легкую манипуляцию отдельными папками, часто средствами операционной системы. Фактически, это ещё один неявный «интерфейс администратора».

Восстановление контекста

Полная смена команды — явление сложное само по себе. В работающем, крупном, развивающимся продукте с длинной историей этот процесс ещё сложнее. Скачком падает внутренняя экспертиза по предметной области, теряются обсуждения с клиентами, которым что‑то обещали «на словах».

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

CommuniGate Pro, конечно, имеет деление на подсистемы, но ему часто не хватало описания внутренней логики «почему было сделано именно так» и сравнения с альтернативными вариантами решения проблемы. Часть из заложенных идей удалось восстановить путём анализа кода довольно скоро, часть описывается до сих пор.

На первом этапе сильно мешал значительный документационный и тестовый долг. И если тестовый долг легче планировать и масштабировать, то документационный становится узким местом, мешающим масштабированию, что выглядит как «медленное движение». На этом этапе было важно выиграть время и пережить этап погружения, понимая, что что‑то серьезное c наскока сделать не получится.

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

Монолит: сложно, но можно

В современных продуктах, реализующих схожую функциональность (за исключением пожалуй, Microsoft Exchange) можно разглядеть крупные блоки, реализующие функции платформы — базы данных, очередь сообщений, механизмы изоляции компонентов. CommuniGate Pro зарождался в то время, когда популярные сегодня реляционные базы данных только появлялись, а для языка С++ ещё не было стандарта ISO.

Положительные стороны монолита — полный контроль над всеми этапами обработки данных — от получения первых байт до сохранения данных, очень низкая зависимость от системного окружения (в пике CommuniGate Pro мог запускаться на дюжине поддерживаемых платформ, включая экзотические), доступность внутреннего представления на любом этапе обработки событий без накладных расходов на сериализацию и передачу, простота построения внутренних связей.

Дополнительным плюсом является простота развертывания и мониторинга — один пакет и базово один процесс, который выполняет все функции по приёму, обработке и хранению данных и взаимодействию в кластере.

Организационно для небольшой команды разработки также естественно «оставаться» в монолите, пока коммуникация достаточна, а внутренняя экспертиза по всему реализуемому функционалу равномерно сильна.

Но есть и отрицательные стороны: бо́льшая цена ошибки, отсутствие «естественных» границ подсистем, один язык программирования, сложная масштабируемость команды: сложно оставаться в рамках привычного маленького сервиса с простым API и уходить в его оптимизацию, приходится более явно отслеживать возможные побочные эффекты изменений.

Проблема обратной связи:

On‑premise “живёт” у клиента. Одно из неудобств этого — ограниченная возможность диагностики. Если ошибка не была найдена до момента поставки, а пользователя она беспокоит не настолько, чтобы обращаться за её решением, она не будет замечена. Существуют продукты, где передача диагностических данных включена по умолчанию, но у нас это не так.

«Никогда не переписывайте систему с нуля»

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

Отбросим экономический риск, отдав его на откуп скучным людям, которые умеют считать деньги, и оставим себе самое интересное — технический образ системы, которая состоит только из «чистого кода», написанного на самом современном стандарте, в котором всё ясно и понятно.

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

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

Часть 2: Делаем

Итак, у нас есть рамки, в которых мы можем двигаться: монолит, нет тестов, мало документации, пользователи, которые хотят «чтобы всё было, как раньше». Пользователи, которые хотят «добавить ещё что‑то, но остальное чтобы работало». И неизвестное количество инсталляций, где происходит всякое. Также возьмём за рамку желание выпускать релиз раз в квартал.

Собираем обратную связь

Ценность обратной связи гораздо выше, хотя количество данных меньше, чем, например, в SAAS, где все вызовы приходят в облаке, логи и диагностические данные лежат у разработчика. И ждать полной картины проблемы часто не получается. Иногда в суперзакрытых инсталляциях больших клиентов в качестве отправной точки поддержка получает только скрин экрана с ошибкой.
Поэтому большую роль здесь играет внутренняя экспертиза по продукту. Это не «промышленный» ресурс. Его трудно масштабировать, он даёт ограниченный процент успеха, но у нас это работает неплохо.

Выпускаем канареечные релизы

Если проблема стабильная, можно сделать отдельную сборку с фокусом на выяснение проблемы. При этом, мы понимаем, что это дополнительные усилия на сопровождение отдельной версии, и результаты, полученные в процессе, должны быть как можно раньше перенесены в основную ветку разработки.

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

Собираем опыт

Опыт эксплуатации текущих клиентов, профильные ресурсы, сторонние клиенты сервера, интеграции и скрипты, находящиеся в открытом доступе — все это можно задействовать как тест или описание поведения.

Увеличиваем тестовое покрытие

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

Параллельно «под прикрытием» такого тестирования можно делать юнит‑тесты, которые в свою очередь требует «распутывания».

Ускоряемся

С монолитом есть известная проблема — нельзя хорошо масштабировать команду его разработки.

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

Тут можно возразить, что CommuniGate Pro «монолит». Это верно лишь отчасти. Ядро монолитно, однако используемый им механизм программ‑помощников в общем случае не требует от самих помощников быть stateless, и они сами могут быть микросервисами.

Заключение

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