Как системный аналитик получил задачу на доработку высоконагруженной системы, которую никто не мог описать целиком
Мне передали задачу по развитию подсистемы аудита крупной отраслевой платформы.
Компания появилась относительно недавно, но сама информационная система формировалась на базе нескольких ранее существовавших решений. В разные периоды их создавали разные компании и команды без единого архитектурного центра. В результате получилась распределенная платформа из сотен микросервисов, обрабатывающая большие объемы данных и обеспечивающая взаимодействие между множеством внешних организаций.
Подсистема аудита в такой платформе — не вспомогательный журнал для разработчиков, а один из механизмов контроля системы. Она фиксирует действия пользователей и сервисов, сохраняет историю обработки запросов, позволяет искать события и получать данные для расследований, а также обеспечивает повторную обработку сообщений при технических сбоях.
На первый взгляд, задача выглядела стандартно: разобраться в текущей реализации, определить необходимые изменения и проверить требования к доработке. Через несколько дней стало понятно, что передавать команде пока нечего. Документации было много. В ней описывались назначение подсистемы, бизнес‑сценарии, атрибуты событий, отдельные операции поиска и выгрузки. Но она не отвечала на базовые технические вопросы:
-
из каких компонентов фактически состоит подсистема;
-
какой сервис отвечает за конкретный этап обработки;
-
где хранятся метаданные, а где тела запросов и ответов;
-
как связаны регистрация, поиск, выгрузка, проверка целостности и очистка;
-
что происходит при потере сообщения или ошибке обработки;
-
какие компоненты относятся к актуальной реализации, а какие остались от предыдущего поколения архитектуры.
Каждый хорошо знал свою часть: разработчики — код сервисов, тестировщики — проверяемые сценарии, архитекторы — целевую модель. Но система целиком оставалась ничьей зоной ответственности. Никто не собирал в одну картину бизнес‑потребность, интерфейсы, данные, код, инфраструктуру, эксплуатацию и миграцию.
Прежде чем описывать изменения, мне предстояло установить, что система делает на самом деле. Не по названиям компонентов и устаревшим диаграммам, а по репозиториям, конфигурации и фактическому поведению.
Задача, которая выглядела простой
Исходная задача состояла из двух частей. Часть действующей реализации планировалось перенести на новое хранилище — YDB. Одновременно требовалось сохранить существующую функциональность и расширить возможности поиска по накопленным событиям. На словах план выглядел последовательно:
-
Изучить текущую подсистему.
-
Определить функциональность, которую необходимо перенести.
-
Прокомментировать имеющуюся постановку.
-
Передать задачу команде разработки.
Но проблема возникла уже на первом шаге: невозможно определить, что именно переносить, пока не понятно, как существующая система работает целиком. Под названием «подсистема аудита» скрывался не один сервис. События формировались внутри прикладных микросервисов, передавались через Kafka, обрабатывались несколькими компонентами и сохранялись в разных хранилищах. Метаданные, необходимые для поиска, находились в MongoDB, а объемные тела запросов и ответов отдельно сохранялись в объектном хранилище Hitachi. Другие сервисы отвечали за поиск и выгрузку данных, сверку обработанных Kafka offsets и удаление информации после окончания срока хранения. Параллельно уже создавалась новая реализация на YDB. События предполагалось распределять по таблицам в зависимости от даты, а часть механизмов обработки ошибок переносить из Kafka в само хранилище. Речь шла не просто о замене подключения к базе данных. Менялись маршруты данных, ответственность компонентов и способы дальнейшего поиска.
Документации было много. Картины системы не было
Сначала я обратился к документации. Там действительно находилось много информации: бизнес‑требования, сценарии регистрации событий, правила поиска, таблицы атрибутов, примеры сообщений, требования к очистке данных и отдельные диаграммы взаимодействия. Но документация описывала, какие функции должна выполнять подсистема, а не то, как они реализованы в действующей системе.
Не существовало актуальной технической модели, которая связывала бы бизнес‑сценарии с конкретными репозиториями, Kafka‑топиками, базами данных и объектным хранилищем. Отдельную проблему создавало одновременное существование двух поколений архитектуры. Из документов нельзя было определить:
-
какие сценарии уже перенесены в YDB;
-
какие продолжают выполняться старыми сервисами;
-
какие компоненты планируется вывести из эксплуатации;
-
где проходит граница между старой и новой реализацией;
-
как должен работать поиск по данным, распределенным между разными хранилищами.
Подготовить требования к доработке только по документации было невозможно. Можно подробно описать каждый сервис и не объяснить путь события от возникновения до поиска через несколько лет. Можно нарисовать компонентную схему и не показать, кто отвечает за повторную обработку. Можно описать новую таблицу и не определить, как искать исторические данные.
Исходный вопрос звучал так:
Что нужно доработать?
Но сначала требовалось ответить на другой:
Что у нас вообще работает сейчас?
Где именно сломалась постановка задачи
Первым проблемным сценарием оказался поиск событий аудита. В документации он выглядел как отдельная операция одного сервиса. Фактически же результат собирался из нескольких источников. Метаданные события находились в MongoDB. Тела запросов и ответов хранились в Hitachi. Информация об асинхронных запросах на выгрузку находилась в PostgreSQL. Чтобы вернуть пользователю полный результат, сервису требовалось связать данные из разных систем.
После перехода на новую реализацию задача усложнялась еще сильнее. Новые события планировалось сохранять в YDB, причем в отдельных таблицах по месяцам. Исторические данные при этом оставались в MongoDB и Hitachi. Значит, поиск должен был одновременно работать с двумя поколениями архитектуры. В исходной постановке это отдельно не учитывалось.
Оставались и другие вопросы:
-
какие функции старых сервисов уже реализованы в новом;
-
какие компоненты должны быть выведены из эксплуатации;
-
где проходит граница между записью, поиском и восстановлением данных;
-
кто отвечает за пропущенные Kafka‑сообщения;
-
как определяется хранилище конкретного события;
-
какие ошибки должны приводить к повторной обработке;
-
что считать полным результатом поиска;
-
в каком виде этот результат нужен пользователю.
Без ответов на эти вопросы нельзя было корректно описать ни API, ни алгоритм поиска, ни ответственность нового сервиса.
Почему нельзя было просто спросить разработчиков
Самый очевидный вариант — собрать разработчиков, тестировщиков и архитекторов и попросить их рассказать, как работает подсистема. Такие обсуждения действительно помогали, но не решали проблему полностью. Разработчик одного сервиса мог подробно объяснить, как сообщение читается из Kafka и записывается в MongoDB. Разработчик другого — как формируются архивы для Hitachi. Кто‑то знал механизм поиска, кто‑то — новую реализацию на YDB. Но каждый ответ начинался и заканчивался на границе конкретного компонента.
Для распределенной системы это нормально. Человек отвечает за определенный сервис и глубоко знает именно его. Он не обязан помнить все зависимости, исторические решения и особенности соседних компонентов. Проблема появлялась в момент, когда из этих локальных знаний требовалось собрать единую последовательность:
Возникновение события-> формирование сообщения-> передача через Kafka-> запись метаданных-> сохранение тела запроса-> контроль пропущенных событий-> поиск-> выгрузка-> очистка-> миграция на новое хранилище
Ни один отдельный ответ не покрывал эту цепочку целиком. Кроме того, устные объяснения могли отражать не текущее состояние системы, а первоначальный замысел или состояние конкретного сервиса на определенный момент времени. Поэтому встречи позволяли подтверждать найденные факты, но не могли заменить исследование.
Момент, когда задача изменилась
Изначально от меня требовалось актуализировать документацию и проверить постановку на доработку. Но такая работа предполагает, что аналитик уже понимает текущее состояние системы и может описать переход к целевому. Здесь не было достоверно определено ни первое, ни второе.
План был такой:
-
определить актуальные компоненты подсистемы;
-
найти их репозитории и используемые ветки;
-
восстановить реальные точки входа;
-
проследить движение данных между сервисами;
-
связать DTO, Kafka‑топики, базы данных и объектное хранилище в единую цепочку;
-
определить механизмы повторной обработки и восстановления пропущенных событий;
-
отделить действующую реализацию от legacy‑компонентов;
-
понять, какая функциональность уже перенесена в YDB;
-
построить целостную архитектурную модель.
Только после этого можно было возвращаться к требованиям. В этот момент стало понятно, что обычного чтения документов и проведения встреч недостаточно. Требовался источник, который не описывает намерения и не зависит от памяти участников. Источник фактического поведения системы. Когда документация заканчивается, системный аналитик начинает читать код.
Что было дальше
На этом первая часть истории заканчивается. У меня уже был список компонентов, перечень открытых вопросов и понимание того, что существующая документация не позволяет восстановить систему целиком. Но самой архитектуры у меня все еще не было.
Дальше началась другая работа. Я получил доступ к репозиториям, начал разбирать ветки, точки входа, Kafka listeners, DTO, адаптеры, настройки и Helm‑чарты. Постепенно из отдельных фрагментов стала складываться реальная цепочка обработки событий. И почти сразу выяснилось, что названия сервисов, документация и фактическая реализация описывают не одно и то же. Некоторые компоненты выполняли больше функций, чем было указано в документации. Другие существовали, но уже почти не участвовали в актуальном процессе. Часть логики была распределена между библиотекой, несколькими сервисами и конфигурацией. А отдельные сценарии, которые выглядели простыми на схеме, в реальности проходили через несколько хранилищ и независимых механизмов восстановления.
В этот момент задача окончательно перестала быть обычной актуализацией документации. Теперь нужно было восстановить архитектуру по исходному коду и доказать, как система работает на самом деле. И только после этого стало возможным вернуться к исходному вопросу:
Что именно нужно переносить в новый сервис?
Во второй статье я расскажу, как искал актуальные репозитории и рабочие ветки, восстанавливал реальные точки входа, связывал код с конфигурацией и в итоге построил архитектурную модель подсистемы, которой до этого не существовало ни в одном документе.

ссылка на оригинал статьи https://habr.com/ru/articles/1064482/