При обсуждении процессов у одного из клиентов возник жаркий спор о функции архитектуры в процессе производства. Мои оппоненты считали, что архитектор делает две вещи: «рисует» архитектуру там, где попросят и осуществляет архитектурный надзор. Я же с такой постановкой категорически не согласен.
В этой статье порассуждаю о явлении коррупции архитектуры, следствиях и причинах её возникновения. Эта статья будет полезна как архитекторам для осознания важности их работы, так и руководителям продуктовой разработки – людей, которые управляют и выстраивают процессы разработки.
Коррупция архитектуры — это постепенная деградация изначально продуманной структуры системы под влиянием краткосрочных решений, приводящая к усложнению сопровождения, снижению отказоустойчивости и потере преимуществ выбранного архитектурного стиля. Она проявляется в нарушении границ модулей или сервисов, создании жестких зависимостей, отказе от принципов версионирования и тестирования, а также в появлении хаотичных локальных изменений и оптимизаций, которые ухудшают целостность системы / ИТ-ландшафта в долгосрочной перспективе. А также, повышении связанности частей системы, решения, ИТ-ландшафта.
Причины.
Причин может быть много, и я их изложу ниже, но за всеми ними торчат уши непонимания двух простых вещей:
· Не понимание инженерной составляющей процесса разработки. Сначала думаем-потом делаем, а не делаем сразу и думаем в процессе.
· Не понимания факта существования решения во времени. С решениями нам нужно дальше жит, эксплуатировать. Развивать. Мы не решаем проблему здесь и сейчас, мы должны думать как наши изменения повлияют на решение в будущем..
Очень часто, к коррупции архитектуры приводит коррупция продуктовой разработки. Когда команды, а фактически менеджеры продуктов мотивированы не на развитие продуктов, а на пиление фич в моменте.
Второй, наиболее часто встречающейся причиной, является низкий технологический уровень ИТ. Это не значит, что плохие люди делают плохие решения. Просто в условиях отсутствия или низкого уровня стандартов и процессов компания дает возможность для неуместной самореализации или решения проблем без оглядки на ландшафт и следствия для него.
Последнее – это проектное управление. Проектное управление не является чем-то плохим. Но если компания мыслит эволюционными итерациями размером в проект и есть сильная мотивация к завершению проекта несмотря ни на что, то это логично приводит к тому, что решение закрывает цели проекта, не соответствуя, при этом, стандартам и целям развития ИТ-ландшафта в целом.
Ну и последнее, если вы видите dark-IT в компании, когда команды делают, что-то не публично и эта намеренная активность – коррупция архитектуры точно идет в комплекте.
Следствия и локальные причины.
Глобальным следствием является реализация кучи антипаттернов, которые приводят к повышению связанности на всех уровнях архитектуры, начиная с бизнеса и заканчивая инфраструктурой. Худшим проявлением может стать превращение решения или всего ландшафта в распределенный монолит, что в какой-то момент приводит к кумулятивному падению показателей ТТМ и lead time.
Нарушение границ компонентов.
Нарушение границ компонентов происходит, когда логика одного модуля или сервиса начинает проникать в другой, размывая изначально спроектированные границы. Это может выражаться в дублировании кода, внедрении бизнес-логики в неподходящие части системы или неявных зависимостях. В результате архитектура теряет модульность, компоненты становятся тесно связанными, что затрудняет поддержку и эволюцию системы.
Основные причины этого явления — срочные бизнес-требования, заставляющие разработчиков искать кратчайший путь для реализации новой функциональности, а также отсутствие архитектурного контроля. Часто нарушение границ вызвано неудачной декомпозицией на этапе проектирования, когда границы сервисов определены неправильно. Дополнительно, нехватка знаний у команды может привести к ошибкам в применении принципов модульности и SOLID.
Нарушение интеграционных паттернов и повышение связанности.
Интеграционные паттерны помогают обеспечить устойчивое взаимодействие между компонентами системы. Когда они нарушаются, это ведет к хаотичным зависимостям между модулями, затруднению тестирования и увеличению времени реакции на изменения. Например, прямые HTTP-вызовы между микросервисами без использования брокеров сообщений или API-шлюзов могут привести к каскадным сбоям и плохой масштабируемости.
Основными причинами такого нарушения являются недостаток опыта работы с распределёнными системами, желание упростить реализацию на первых этапах, а также давление бизнеса, требующее "быстрых" решений. Иногда проблема возникает из-за нехватки времени на рефакторинг, когда временные решения закрепляются в коде как постоянные.
Ручное управление зависимостями.
Ручное управление зависимостями означает, что адреса сервисов, версии библиотек или конфигурации жестко прописаны в коде, а не управляются через сервис-дискавери, пакетные менеджеры или инфраструктурные инструменты. Это приводит к сложностям в развертывании, делает систему менее гибкой и увеличивает риск ошибок при обновлениях.
Причинами могут быть недостаточное понимание DevOps-практик, желание сократить сложность инфраструктуры на ранних этапах разработки, а также сопротивление изменениям в зрелых системах. Иногда это вызвано недостатком инструментов или ресурсов для автоматизации, что заставляет команды идти по пути наименьшего сопротивления.
Рост «богатых» вспомогательных компонентов.
Вспомогательные сервисы (например, аутентификация, логирование, биллинг) часто начинают обрастать бизнес-логикой, которая изначально должна находиться в других компонентах. Это приводит к тому, что такие сервисы становятся точками отказа, чрезмерно загруженными и зависимыми от множества других модулей. Кроме того, стоит помнить, что команды разрабатывающие сервисные компоненты часто мотивированы не бизнесовыми целями, а своими более техническими метриками.
Причинами могут быть удобство централизованного хранения логики, непроработанная архитектурная стратегия, а также желание сократить дублирование кода без учета архитектурных последствий. Такой рост вспомогательных сервисов особенно распространён в системах без четко сформулированных границ ответственности.
Локальные изменения в ущерб глобальной архитектуре.
Когда команды оптимизируют работу своих отдельных модулей, не учитывая влияние на всю систему, это может привести к деградации архитектуры. Например, внедрение кэширования на уровне одного сервиса без учета консистентности данных между микросервисами может вызвать ошибки.
Основные причины — узконаправленные KPI команд, отсутствие встраивания архитектуры в процесс и слабая коммуникация между разработчиками. В больших организациях разные команды могут работать автономно, принимая решения, которые противоречат общей стратегии развития системы.
Слабая документация и знания в головах сотрудников.
Когда система развивается без достаточного документирования API, контрактов взаимодействия и архитектурных решений, новые разработчики вынуждены изучать её методом проб и ошибок. Это ведёт к непониманию архитектурных ограничений и принятию решений, ухудшающих систему.
Основными причинами являются нехватка времени, низкий приоритет документации в процессах разработки, а также высокая текучесть кадров. Иногда разработчики сознательно избегают документации, считая, что код говорит сам за себя, что приводит к проблемам при масштабировании команды.
Отказ от тестирования.
Без автоматизированных тестов система становится хрупкой: любое изменение может привести к неожиданным багам. Особенно это критично в микросервисной архитектуре, где изменения в одном сервисе могут затронуть несколько других.
Причины отказа от тестирования включают давление сроков, нехватку компетенций в тестировании, а также отсутствие культуры качества. В ряде случаев команде сложно убедить бизнес в необходимости тестирования, так как его ценность не всегда видна сразу.
Игнорирование принципов версионирования.
Изменение API без поддержки старых версий может привести к сбоям в работе зависимых сервисов. Это особенно опасно в микросервисной архитектуре, где разные части системы развиваются независимо.
Причинами являются спешка при выпуске новых функций, отсутствие инструментов для работы с несколькими версиями API, а также недостаток культуры совместимости между сервисами. В командах без чётких стандартов версионирования разработчики могут менять контракты API без оценки последствий.
Выход из кризиса.
Самое ужасное во всем этом то, что руководители разработки, владельцы продуктов и прочие стейкхолдеры следствие проблемы видят, а вот причины осознать не могут. И те мероприятия, которые описаны ниже воспринимаются как блаж ИТ-шников, которая отнимает и без того ограниченные ресурсы у продуктовой разработки.
Преодоление коррупции архитектуры невозможно, если осознание проблемы остаётся только в технических командах. Важно донести до бизнеса, что архитектура — это не абстрактная "блажь IT-шников", а фундамент, от которого зависит скорость поставки, качество продукта и, в конечном счёте, прибыль. Для этого можно использовать метрики, демонстрирующие влияние технического долга: время вывода фичи на рынок, количество инцидентов, затраты на поддержку и исправление багов.
Самое удивительное, что основной буст дают вложения в гигиену разработки. Когда процесс разработки формализуется, автоматизируются процессы CI/CD, тестирование, стандартизируется инфраструктура и тех. стек.
Так же, существенный буст дает design-first или хотя бы API-first подход. И это первый шаг к выстраиванию архитектуры в организации. Архитектура должна строится от кода и расти вверх, а не спускаться сверху от корпоративной архитектуры к системной в виде бесполезных артефактов-мутантов.
Очень важное – транзит ответственности. Любые архитектурные артефакты должны быть полезны и проверяемы на каждом следующем этапе разработки. Бизнес-требования через метрики должны проецироваться на архитектуру решений, архитектура решений должна однозначно проецироваться на системную, например за счет контрактов и спецификаций. В идеальной картине мира архитектурные артефакты должны использоваться as-is на этапах тестирования. Спецификации – на уровне контрактных тестов, solution-контракты на этапе e2e тестирования и так далее.
Простота принятия архитектурных решений. Хаотичные архитектурные решения часто принимаются потому, что у команд нет понятных ориентиров. Важно не просто навязать архитектурные требования, а встроить их в процессы так, чтобы они поддерживали разработку, а не мешали ей. Это может быть, например, каталог одобренных технологий, внутренняя документация с паттернами использования и регулярные архитектурные обзоры в формате RFC (Request for Comments).
То, что ни кто не умеет – системная работа с тех. долгом. Технический долг неизбежен, но важно, чтобы его устранение было частью стратегии, а не реакцией на кризисы. Можно внедрить практику Tech Review Board (совет по техдолгу) или архитектурного бюджета, когда часть ресурсов всегда выделяется на рефакторинг и улучшение системы.
Ну и как бы очевидное: архитектура – это непрерывный процесс. Архитектура не должна быть разовым проектом или статичным документом. Это непрерывный процесс, включённый в жизненный цикл продукта. Лучший способ борьбы с коррупцией архитектуры — встроить архитектурные практики в ежедневную работу команд.
За сим все, всем красивых стрелочек и квадратиков!