Привет, Хабр! Меня зовут Евгений Кермас, я главный эксперт по технологиям в Управлении развития технологий модельного риска в Сбере.

В этой статье я попробую ответить на вопрос: «Что делать, если вы, как архитектор, пришли на существующий проблемный проект в качестве кризисного-менеджера?» Расскажу о нескольких подходах и дам советы, которые могут помочь в принятии решений в создании архитектуры и планировании проекта. Для этого разберём один пример с максимальным количеством проблем. На входе у нас есть монолит с запутанным кодом, на legacy-инфраструктуре, с нецелевым техстеком и большим грузом проблем, как технологических, так и организационных.

А что не так с монолитом?

Давайте посмотрим на вопрос как enterprise-архитекторы, то есть сверху. Проблема не в монолите или legacy, или в неправильном техстеке, а в том, что «бизнес недоволен». Ему не интересна архитектура и то, насколько она соответствует идеалу, бизнес либо доволен продуктом, либо недоволен.

Почему мы говорим в первую очередь о бизнес-заказчике, а не о команде продукта? Ключевое слово тут «заказчик»: продукт должен удовлетворять его запросы и решать проблемы пользователей.

Пять стадий принятия

А что, если посмотреть на этапы работы над таким проектом через призму пяти стадий принятия?

Отрицание

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

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

Гнев

У бизнеса также нет вообще никаких проблем с гневом. На этой стадии можно очень хорошо поработать и собрать все проблемы и боли проекта, собрать и структурировать требования к системе, порой самые неожиданные. Например, команда не может найти квалифицированных разработчиков в проект. Это проблема архитектора? Да, потому что он отвечает за проект. И если неверно выбран техстек, то так будет продолжаться всегда.

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

Контрагентами выступали:

  • владелец продукта (через него напрямую проецируются проблемы пользователей);

  • продуктовая команда (аналитики продукта, тимлид и команда разработки и тестирования);

  • команда DevOps и Ops (внедрение и эксплуатация).

Чем недовольны контрагенты?

Пользователи мнение которых передаёт владелец продукта:

  • низкая скорость внедрения фич;

  • много багов;

  • неприемлемая оценка трудозатрат и сроков на доработку текущего решения до требований бизнеса;

  • низкая скорость и стабильность работы системы.

Тимлид и команда разработки:

  • качество и читаемость кода сравнялось с 20-строчным регулярным выражением;

  • сложно найти квалифицированных разработчиков в проект;

  • большой техдолг и клубки спагетти-кода по всей кодовой базе;

  • отсутствие или неактуальность документации.

DevOps/Ops:

  • нестабильная работа системы и невозможность горизонтального масштабирования;

  • сложности в установке и поддержке решения.

Все требования мы собирали в одном месте, доступном всем участникам процесса. В моём случае это был Confluence и таблица «Анализ проблемных зон проекта». Пример её заполнения:

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

  • определить успешность проекта по его завершении, так как цель любого проекта — удовлетворить запрос клиента;

  • перейти к следующему шагу: определению проблемных зон и проектированию решения.

Торг

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

Итогом этапа является текущая и целевая архитектура системы и список изменений, которые нужно внести в текущую архитектуру.

Сбор информации

Надеваем панаму археолога и занимаемся реверс-инжинирингом, рисуя картинки «как оно работает в реальности».

Какие артефакты считаю необходимым получить на этом этапе:

  • Список бизнес-функций системы: что она делает и какие особенности присутствуют. Это нужно для понимания предметной области и выделения доменов.

  • Диаграмма развёртывания в эксплуатацию: на какие стенды и как развёртывается система, какие виртуалки, БД, шлюзы и прочие сущности используются.

  • Интеграционная схема: с какими системами, как и по каким протоколам интегрирована система.

  • Компонентная схема: какие сервисы прикладного уровня с какими доменами присутствуют в системе и как они взаимодействуют между собой.

  • Схема рабочих окружений: какие стенды есть у команды, как они используются, какие интеграции есть на СТ, ИФТ, UAT и эксплуатационных стендах. Это нужно для понимания согласованности dev- и prod-стендов.

  • Схема рабочего процесса: как движется задача и как она реализуется, тестируется и доставляется в эксплуатацию. Это нужно для понимания рабочих процессов и выявления в них проблемных зон.

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

Приведу три примера:

Диаграмма развёртывания
Диаграмма развёртывания
Компонентная схема
Компонентная схема
Таблица техстека
Таблица техстека

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

Диаграмма развёртывания
Диаграмма развёртывания
Компонентная схема
Компонентная схема
Таблица техстека
Таблица техстека

Что мы имеем в итоге? 

Числа утрированы для поддержания саспенса, но реальность отражают.

  • Монолит из двух сервисов на виртуалке.

  • 100500 строчек малопонятного кода на Scala.

  • Коммиты от 64 разработчиков.

  • В проекте, который изначально запускали на вполне нормальном техстеке — Scala, Akka, Akka HTTP, — через четыре года разработки периодически меняющейся командой подрядчиков появился зоопарк из Akka, Akka HTTP, Akka Streams, Cats, Zio, Monix, Scalike jDBC, Quill, Monocle, Tapir, Play, Circe и ещ` десятка малоизученных эзотерических библиотек суммарно с сотней звёзд на Github.

Бизнес-требования

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

  • непрерывности разработки и вынесения изменений на пользователей;

  • бесшовной миграции со старой на новую версию;

  • одновременной работы двух версий системы;

  • сроков, к которым должна быть запущена работающая функциональность.

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

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

Выработка архитектурного решения и проектирование

По сути, займёмся принятием архитектурных и управленческих решений. Начинаем работать как архитекторы. Что используем? Естественно, паттерны и референсные архитектуры.

  • Применимые паттерны проектирования раскидываем по компонентам схемы с комментариями.

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

Какие группы принципов разработки и паттернов/антипаттернов можно использовать? 

  • Микросервисы. Открываем https://microservices.io/tags/pattern и изучаем всё по списку, задавая себе вопрос: «Какую проблему можно этим паттерном решить?»

  • Инфраструктура. Тут однозначного источника нет, я искал паттерны на основе своего опыта, например api-gateway отношу именно к инфраструктуре.

  • Базы данных — тут всё очевидно:

    • master-slave;

    • нативные возможности СУБД (хранимые процедуры, работа с JSON, ...);

    • шардирование;

    • партиционирование;

  • Интеграционные/SOA — рекомендую почитать различные Red book от IBM. Service/adapter — классический пример SOA-паттернов.

Целевую схему делаем по примеру вышеприведённых схем и таблиц. 

Диаграмма развёртывания с паттернами и анти-паттернами
Диаграмма развёртывания с паттернами и анти-паттернами
Компонентная схема
Компонентная схема
Таблица техстека с целевыми решениями, добавляем в существующую таблицу
Таблица техстека с целевыми решениями, добавляем в существующую таблицу

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

  • Postgres native publish/subscribe;

  • GraphQL как механизм представления данных;

  • смена схемы хранения данных и перекладка с помощью хранимых процедур;

  • использование Nginx как API-шлюз/strangler provider.

Хочу отметить важный вопрос: «Какой тип БД использовать?» Сейчас на рынке есть масса зрелых решений из разных классов: реляционные, колоночные, графовые, документные и т. д. Возникает соблазн сделать «модно-молодёжно». Мы смогли отказаться от идеи пойти в Mongo и, кажется, это было правильное решение. Классический Postgres сейчас обеспечивает великолепный уровень производительности и огромное количество возможностей по решению разных задач (поиск, партиционирование, шардирование, передача сообщений, работа с JSON, ...) сохраняя совместимость с SQL. Главное, знать об этих возможность и правильно их использовать.

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

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

Прототипирование

Прототипируем всё, что вызывает вопросы. У нас вопросы вызвали:

  • API-шлюз на nginx. Прототипирование показало что это возможно и работает хорошо, на практике так и вышло.

  • Прототип сервиса с нормализованной базой и представлением всей структуры в GraphQL.  Выявили много проблем и вопросов, которые итеративно закрывали. В итоге все проблемы решили и сэкономили много времени из-за того, что сначала делали прототип.

Депрессия

Оценка

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

Согласование с заказчиком

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

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

Управление рисками

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

Принятие

Целевая архитектурная концепция сформирована и согласована со всеми участниками, прототипирование успешно завершено, что теперь? Заканчивается ли на этом участие архитектора в проекте?

Нет, у нас есть этап реализации проекта, на котором у архитектора есть ещё функции поддержки команды:

  • актуализация архитектуры под влиянием изменений, исходящих от команды разработки;

  • надзорная функция: следить чтобы никто не уходил от архитектурной линии, которая была зафиксирована в начале, а если изменения требуются, то чтобы они были согласованы и зафиксированы архитектором;

  • помощь в «нерешаемых вопросах»: тех проблемах, которые команда сама не может решить или решает неоптимально.

Архитектор должен сопровождать разработку и принимать участие, как минимум, на этапе UAT, чтобы вовремя выявить проблемы, а в идеале — на этапе постановки задач, и регулярно проверять код и анализировать инфраструктурные изменения.

Приведу реальный пример. На этапе разработки мы поняли, что не сможем достичь нужной производительности SQL-запросов при работе с текущей структурой БД. Пришлось вернуться к архитектуре данных и реализовать новую версию архитектуры с нормализацией данных и перекладкой их из старой структуры с помощью триггеров. Это позволило:

  • не нарушать работу ядровых сервисов;

  • получить требуемую производительность поискового движка;

  • не включать в техстек новые технологии и сервисы.

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

Что я сделал в роли архитектора: реализовал рабочий прототип решения на классическом Postgres и показал его реализуемость.

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

Подведение итогов проекта

К чему привело изменение архитектуры и смена техстека приложения?

  • Почти полностью сменился состав команды backend-разработки: те скалисты, кто был готов сменить стек на Java? остались, а остальные ушли.

  • Стало легче нанимать нормальных бэкендеров за вменяемые деньги.

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

  • Увеличилось быстродействие — переход с Akka на Spring в среднем увеличил производительность REST в 1,5—2 раза.

  • На фронте упростилась разработка благодаря отказу от серверной части и NextJS.

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

Обработка критериев успешности

Расписываем результаты в таблице, заведённой на этапе анализ требований, и идём к заказчику за подтверждением успешности проекта.

Итоги проекта с точки зрения исправления проблемных зон
Итоги проекта с точки зрения исправления проблемных зон

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

Планы на развитие

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

Извлечённые уроки и выводы для архитектора

Какие риски выстрелили:

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

  • Архитектор должен плотно взаимодействовать с командой разработки и иметь авторитет, подкреплённый практикой: участие в прототипировании и анализе проблем.

Заключение

Эта статья — не руководство по конкретным паттернам или технологиям. Это рекомендация, план, который может помочь пройти путь с проблемным legacy-проектом проще и дешевле.

Комментарии (1)


  1. psergal
    30.05.2025 09:51

    забавно, что только на этой стадии

    На этой стадии можно начинать мыслить конструктивно