С появлением ИИ-инструментов код в проектах начал расти быстрее. Новый сервис, багфикс или интеграцию теперь можно получить быстрее чем согласование изменений, но цена архитектурной ошибки на этом фоне только растёт.
Проблема не в том, что ИИ пишет плохой код, а в том, что он ускоряет накопление расхождений между тем, какой систему задумывали изначально, и тем, во что она превращается по мере изменений. Если архитектурные ограничения не формализованы и не проверяются автоматически, команда рано или поздно начинает проигрывать собственной скорости.
Архитектура очень часто живет отдельно от реальной разработки. У кого-то схема лежит в Confluence, у кого-то в Miro, где-то всё держится в голове. Пока изменений мало, это ещё работает, но сейчас такой подход ломается. Документация стареет еще быстрее, а тот кто делает ревью, уже не успевает за объёмом изменений и вишенка на торте это желание передать задачи по созданию нового функционала полностью искусственному интеллекту ведь у нас есть полный? контроль над тем как и что он делает.
Из-за этого проблема уже не сводится только к качеству кода, тестам или ревью. Код может компилироваться, а ревью может быть формально пройдено, но реальная архитектура системы в это время продолжает меняться и всё дальше уходит от той которую считали целевой. Поэтому сегодня вопрос не в том, нужно ли вообще управлять архитектурой и следить за тем, что с ней происходит, а в том, как встроить такой контроль в процесс разработки и поставки изменений так, чтобы он не держался только на ручных проверках архитектора или команд разработки.
Fitness functions to the rescue
Один из ответов на этот вопрос появился в подходе эволюционирующей архитектуры. Именно оттуда пришёл термин фитнес-функций, это автоматизированные проверки, которые помогают понять, остаётся ли система в рамках тех архитектурных ограничений, которые команда считает важными. Эта идея описана в книге Building Evolutionary Architectures и в материалах Thoughtworks Technology Radar on architectural fitness function, где такие проверки рассматриваются как практический способ сделать архитектурные решения исполнимыми, а не декларативными. Мы можем автоматически проверять, что один слой не лезет в другой, что модуль не начинает зависеть от того, от чего зависеть не должен, и что внутри системы не появляются запрещённые связи и тд.
Один из самых известных инструментов для такого рода проверок — ArchUnit. Архитектурное правило оформляется как обычный тест и начинает выполняться вместе с остальным набором тестов в CI.
Условный пример такого локального правила
@AnalyzeClasses(packages = "com.example.shop") public class ArchitectureTest { @ArchTest public static final ArchRule application_should_not_depend_on_infrastructure = noClasses() .that().resideInAPackage("..application..") .should().dependOnClassesThat() .resideInAPackage("..infrastructure.."); }
Если правило уже один раз формализовано, дальше его не нужно удерживать в голове при ревью. Упавший билд лучше подскажет, что не так с кодом. В этом и состоит сильная сторона таких проверок. Они хорошо работают там, где архитектурное ограничение можно проверить на уровне структуры кода, снимая с команды часть ручного контроля и переводя важные договорённости в формализованную форму. Но именно здесь и проходит их граница.
Где локальных правил уже недостаточно?
Даже если в проекте есть солидный набор архитектурных тестов, это ещё не гарантирует, что система в целом будет развиваться в рамках задуманной архитектуры. Локальные правила хорошо ловят нарушения внутри конкретного сервиса или модуля, но они не дают целостной картины того, как со временем меняется вся система и какие связи в ней постепенно становятся нормой.
Проблема обычно начинается не с явной ошибки и не с того, что кто-то сознательно решил нарушить правила. Гораздо чаще команда торопится выпустить новую функциональность, из-за этого выбирается самый короткий путь и каждое такое решение может выглядеть вполне разумным. Внутри проекта всё может оставаться формально чистым, потому что слои не смешаны, сборка проходит, тесты зеленые. Но сам сервис при этом начинает обращаться к соседям так, как изначально никто не планировал, а в системе появляются обходные связи, новые зависимости, которых в исходной архитектуре просто не было.
Это особенно заметно в распределенных системах, где уже есть несколько сервисов, очереди, внешние вызовы и отдельные бд. В таком окружении локальная проверка может подтвердить, что каждый отдельный проект устроен как надо, но она не покажет, что реальная форма всей системы уже изменилась и начала расходиться с тем вариантом, который когда-то был принят как целевой.
В качестве примеры давайте рассмотрим вот такую архитектуру. Мобильное приложение не знает про внутренние сервисы и работает через BFF, сценарий оформления заказа собран в Checkout, Orders и Payments владеют своими базами, а рекомендации и уведомления получают события из шины. Тут все просто и сразу видно, где проходит граница ответственности и куда нужно добавлять новые возможности.

Дальше система начинает развиваться. Бизнес просит быстро выкатить новую фичу, оплату в один клик, у Payments же есть стабильный API и тесты, поэтому быстро добавляется прямой вызов из мобильного приложения(Кому-то этот пример может показаться притянутым за уши, возможно, но в жизни бывает всякое). Такое изменение может пройти ручной контроль и тесты, из-за загрузки или по каким-то другим причинам, плюс фича действительно нужна бизнесу у нас KPI!. И если никто не сверяет изменение с целевой архитектурной моделью, то появляется новый обходной путь, которого в исходной картинке не было.

Сам по себе один такой вызов ещё не обязательно выглядит как катастрофа. Проблема в том, что дальше система продолжает усложняться. Потом, внезапно захотят для нового сценария в Рекомендациях начать синхронно ходить в Orders и Payments, поддержке в их CRM может понадобится подключиться напрямую к Orders и тд. Все эти решения по отдельности тоже можно объяснить ведь API есть, да и задачу надо закрыть.
Но в сумме меняется уже не один маршрут, а форма всей системы. BFF перестаёт быть единственной точкой входа, появляются дополнительные синхронные зависимости, растёт связность между командами, становится сложнее менять контракты и выпускать новый функционал и тд. Если опираться только на проверки внутри одной команды, созвоны и ручное ревью, такие изменения можно пропустить, а когда их становится критически много, больно уже и командам и бизнесу, который получает всё более дорогую и медленную поставку изменений.
Будем проверять дрифт на уровне всей системы!
Такая проверка нужна потому что локальных проверок уже недостаточно и команде важно видеть не отдельные нарушения в коде, а то, насколько реальная система в целом продолжает соответствовать задуманной архитектуре.
Для этого нужна не одна проверка, а целый процесс сопоставления. С одной стороны у нас есть целевая архитектурная модель, в которой заранее описано, из каких компонентов состоит система, как они связаны между собой и какие ограничения для неё считаются нормой. С другой стороны мы собираем фактическую модель из исходного кода, структуры решений и проектов, зависимостей и описания инфраструктуры. Сканеры проходят по репозиторию, извлекают признаки сервисов и модулей, анализируют вызовы, зависимости, а затем всё это приводится к общей модели, с которой уже можно работать дальше.
В результате мы видим как задумка отличается от фактической реализации. Например, в системе может появиться новый компонент, которого не было в целевой модели, или возникнуть новая связь, которую никто не планировал и которая меняет общий маршрут взаимодействия между частями системы как в примере выше. А что самое важное такое изменение будет поймано сразу же, как только оно появилось, а не на этапе тестирования перед релизом или уже в проде.
Как это выглядит на практике
Вернёмся к примеру с оплатой и обходом BFF. В FlowConsole целевая схема не остаётся просто картинкой, она описана как модель, с которой потом можно сравнить то, что реально появилось в коде.
При запуске проверки из кода в репозитории строится фактическая модель. Сначала проверяются структура проектов, ссылки, зависимости, директории, конфигурации, зависимости между проектами. Затем языковые сканеры разбирают исходники и извлекают архитектурные признаки - классы, интерфейсы, эндпойнты, импорты, обращения к БД, внешние вызовы, чтение конфигураций и тд. Например, для C# это .sln/.slnx, .csproj, Directory.Build.props файлы, ASP.NET эндпойнты, бэкграунд воркеры и другие признаки.
На выходе получается не просто список файлов, а набор признаков, найден такой-то сервис, такой-то эндпойнт, такая-то зависимость, такой-то доступ к данным. Эти признаки нормализуются в общий формат, затем проецируются в граф, в нашем примере это Mobile App, BFF, Checkout, Payments, Orders, их эндпойнты и отношения между ними. После этого сопоставляется фактическая модель с целевой и в результате обнаруживаются расхождения, например, что в коде появился вызов Payments из Mobile App, которого не было в целевой архитектуре.

Ценность здесь в том, что архитектурная проверка становится воспроизводимой и перестаёт зависеть только от ручного контроля. Команда получает формализованную модель в git, которую можно прогонять в процессе сборки, видеть конкретные отклонения и уже работать с ними.
Появляется и ещё один важный эффект. Такая модель даёт актуальный архитектурный контекст не только людям, но и агентам, которые участвуют в разработке. Если у нас есть живая модель системы, то к ней можно обращаться за актуальной информацией о границах, связях, ролях компонентов и допустимых маршрутах взаимодействия. Это уже не просто схема для чтения, а источник архитектурного контекста. Через MCP тул агент может запросить нужный фрагмент модели, а не загружать в контекст весь репозиторий или архитектурные схемы. Это снижает расход токенов, уменьшает объём контекста, который нужно держать в запросе, и ускоряет работу агента.
Архитектурный контроль как единый слой в процессе поставки
При этом локальные проверки и проверка архитектуры на уровне всей системы закрывают разные части одного процесса и не взаимоисключают друг друга. Первые помогают удерживать порядок внутри конкретного сервиса или модуля, вторые показывают, как меняется система целиком(по коду, инфраструктуре и рантайм-поведению). Когда эти уровни связаны между собой, архитектурный контроль перестаёт быть отдельной активностью после релиза и становится частью поставки, а команда быстрее видит последствия изменений, безопаснее адаптирует систему под новые требования и при этом продолжает поставлять ценность бизнесу.
Какие изменения происходят прямо сейчас?
ИИ меняет не только скорость написания кода, но и саму цену неявных договорённостей. Пока разработчик делает одну задачу руками, часть архитектурного контекста можно удержать в голове, уточнить на созвоне или поймать на ревью. Когда часть работы выполняет агент, который в любом случае быстрее сгенериует код, такой неформальный контекст начинает теряться.
Поэтому архитектурные правила должны быть доступны не только людям, но и инструментам, которые участвуют в разработке. Агенту недостаточно сказать “быстренько запили фичу X!”, ему нужно дать модель, где описаны границы, какие маршруты допустимы, какие зависимости запрещены, какие решения уже были приняты. Без этого ИИ будет не столько помогать развивать систему, сколько ускорять только создание кода, а не весь процесс в целом.
Именно поэтому уже недостаточно просто писать код быстрее. Нужно, чтобы сама архитектура становилась частью процесса поставки изменений через тесты на уровне сервиса или модуля, целевой модели системы и механизма, который позволяет сравнивать замысел с тем, что реально получилось в коде и инфраструктуре и почему. В этом смысле автоматический архитектурный контроль постепенно становится такой же базовой инженерной практикой, какой когда-то стали автоматические тесты. Не отдельной активностью где-то рядом, а нормальной частью сборки, проверки и доставки изменений.
Именно из этой логики и развивается FlowConsole. Не как генератор диаграмм, а как источник живой архитектурной модель, которую необходимо использовать для описания системы, визуализации, проверок и контроля дрифта в процессе поставки ценности.
