ИИ пишет код быстро, но за недели превращает проект в спагетти. Мы (инженерная лаборатория «Микрофабрика») попробовали решить это с помощью C4 Model и Structurizr DSL — и получили рабочий инструмент, который помогает и людям, и ИИ‑агентам удерживать структуру проекта. Рефакторинг, на который мы закладывали неделю, с его помощью занял один вечер. В статье — что такое C4, почему графическое программирование не спасает, почему Structurizr не просто «ещё один рендерер диаграмм», и как всё это работает на практике.

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


Проблема: ИИ пишет быстро, но не думает об архитектуре

Мы пишем код в связке с ИИ (работаем через DeepSeek Harness и OpenCode; модель Kimi K2.7 Code через провайдеров). Это ускоряет разработку в разы. Но есть и негативный эффект, который проявляется не сразу, а через несколько недель работы.

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

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

Можно возразить: а почему просто не дать ИИ весь код в контекст? Контекст ограничен, между сессиями памяти нет, но главное — даже имея весь код, ИИ не выделяет из него структуру. Он видит текст, а не карту. Структура — это отдельное представление, и его надо дать явно.

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

Отсюда вывод: ИИ нужен тот самый листочек, но в цифровом виде. Не просто ещё один файл с кодом, а отдельное описание структуры: что за что отвечает и как связано.

Почему графическое программирование не помогло

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

Сначала был текст. Он прекрасно масштабировался до определённого предела, а потом для борьбы со сложностью начала применяться декомпозиция: разбиение на файлы, каталоги, модули. Это помогло, но возникла новая проблема — с ростом объёма текста становится всё труднее удерживать в голове связи между его частями.

Закономерно возникла идея записывать программу как граф — блоками и стрелочками. Получаем наглядную структуру, на которой видны связи элементов программы. Идея получила развитие: блок‑схемы, Visual Basic, LabVIEW, Simulink, Scratch, а теперь ещё Low‑code и No‑code. Список длинный, но у всех этих подходов одна и та же проблема — графы плохо масштабируются.

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

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

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

Проблему усугубляет смешение семантики и расположения. В большинстве визуальных редакторов граф и его расположение хранятся вместе. Изменил одну связь — поехала вся картинка, а git diff показывает мусор вместо смысла.

И наконец, графика плохо описывает алгоритмическую логику. Циклы, условия, рекурсия, generics — всё это словами точнее и компактнее, чем мышкой. for i in range(n) — одна строка; тот же цикл в узлах — это узел‑счётчик, узел‑условие, тело, обратная связь.

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

C4 Model: четыре уровня, чтобы не утонуть

Оказалось, что мы не первые, кто задумывался над этой проблемой. Есть C4 Model — методология описания архитектуры от Саймона Брауна. Название — аббревиатура от четырёх уровней: Context, Containers, Components, Code. Идея простая: описывать систему не одной гигантской диаграммой, а четырьмя уровнями абстракции.

  • C1 — System Context. Кто пользователи системы? С какими внешними системами она общается? Уровень «для заказчика», где не нужно объяснять код.

  • C2 — Containers. Из каких высокоуровневых «коробок» состоит система: бэкенд, фронтенд, база данных, кэш, внешние API. Уровень архитектурных решений.

  • C3 — Components. Что внутри конкретного контейнера: какие бизнес‑модули и за что каждый отвечает.

  • C4 — Code. Классы и методы. Этот уровень в C4 рисуют редко: источник правды здесь — сам код, а диаграммы классов быстро устаревают.

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

Structurizr: почему не draw.io и не Mermaid

Для работы с C4 нам нужен инструмент, который удовлетворяет требованиям:

  • диаграммы как код: у диаграмм есть текстовое представление, которое можно хранить в Git;

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

  • иерархическое представление: нативная поддержка уровней C4, а не плоский граф.

Structurizr — это инструмент, который написал тот же человек, что придумал C4 — Саймон Браун. И это принципиально важно — когда методологию и инструмент создаёт один человек, между ними нет зазора в понимании. Structurizr не «поддерживает C4 как одну из диаграмм» — его DSL и есть C4.

В проекте мы создаём директорию docs/architecture/ со следующей структурой:

docs/architecture/
├── workspace.dsl         ← семантика (git-friendly, текстовый файл)
├── workspace.json        ← расположение блоков + состояние редактора
└── mermaid/              ← для GitHub-рендеринга

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

Пример (сокращённый):

workspace "URL Shortener" "Example microservices architecture" {
    model {
        user = person "User" "Creates short links and views stats"

        urlShortener = softwareSystem "URL Shortener" "Shortens URLs" {
            apiGateway = container "API Gateway" "Go" "Reverse proxy to services"
            postgres = container "PostgreSQL" "PostgreSQL 16" "Storage"
            redis = container "Redis" "Redis 7" "Cache"

            linkService = container "Link Service" "Go" "CRUD links" {
                linkHandler = component "Link Handler" "Go" "HTTP handlers"
                linkRepo = component "Link Repository" "Go" "DB access"

                linkHandler -> linkRepo "Reads/Writes"
            }

            user -> apiGateway "Uses"
            apiGateway -> linkService "REST API"
        }
    }

    views {
        systemContext urlShortener "SystemContext" {
            include *
            autolayout lr
        }
        container urlShortener "Containers" {
            include *
            autolayout lr
        }
    }
}

Сохранили файл — Structurizr перерисовал диаграммы. Изменили связь в DSL — картинка обновилась. А ручное расположение блоков, которое вы задали мышкой в интерфейсе Structurizr, сохранилось: оно лежит в workspace.json и не теряется при следующем рендере.

Вот в этом и отличие от альтернатив. draw.io даёт полный контроль над расположением, но смешивает семантику и координаты в одном файле: git diff показывает мусор. Mermaid вообще не хранит расположение элементов, поэтому оно нестабильно: при каждом рендере картинка перестраивается заново, и блоки прыгают. Structurizr разделяет их: семантика в workspace.dsl, расположение в workspace.json.

Таким образом, если вы хотите описывать архитектуру продукта — используйте Structurizr. А если вам понадобится вставить диаграмму связей в README — экспортируйте её в Mermaid.

Как мы это используем

Мы попробовали разные способы работы со Structurizr и в итоге пришли к тому, что запускаем Structurizr в Docker и просто смотрим диаграммы локально в браузере:

cd docs/architecture

# Сгенерировать workspace.json из DSL
structurizr export -workspace workspace.dsl -format json -output .

# Запустить локальный веб-сервер
docker run -it --rm -p 8080:8080 \
  -v "$(pwd):/usr/local/structurizr" \
  structurizr/structurizr local

# Открыть http://localhost:8080/workspace/1/diagrams

Диаграммы обновляются при каждом сохранении workspace.dsl. Расположение блоков сохраняется в workspace.json, который тоже коммитится в Git. Если файла workspace.json ещё нет, Structurizr создаст его сам при старте. Плюс в браузере есть навигация между уровнями — можно щёлкать по элементам и проваливаться в содержимое.

От автоматической генерации PNG/SVG отказались — статичные файлы проигрывают живым диаграммам: в браузере они обновляются на лету. Плюс ко всему старый structurizr‑cli из AUR (Arch Linux) не умеет рендерить PNG/SVG — он умеет только текстовые экспорты (PlantUML, Mermaid, JSON, DOT). Технически картинки можно получить связкой Structurizr + Puppeteer (headless Chromium), но это отдельный пайплайн, и ради статичных файлов мы его не держим.

Mermaid‑экспорт для GitHub тоже не держим. Диаграмма и так живёт в workspace.dsl — её в любой момент можно открыть в Structurizr. А Mermaid‑файл в репозитории — это статичный снимок, который устареет на следующей правке и начнёт врать. Если понадобится показать диаграмму в README, экспорт делается одной командой.

Что в итоге:

Планировали

Реально используем

Единый DSL как источник правды

✅ Да, постоянно

C4 для рефакторинга архитектуры

✅ Да, главный кейс

Локальный Structurizr для просмотра

✅ Да, каждый раз

Автогенерацию PNG в CI

❌ Нет, в браузере удобнее смотреть, плюс есть навигация между слоями

Mermaid‑экспорт для GitHub

❌ Нет, разве что для README

C4 для мелких правок

❌ Нет, не детализировали уровень «Code»

Пример из практики: как C4 спас рефакторинг

Мы работаем над проектом «Трафаретка» — сервис, который позволяет заказать бумажный трафарет по вашему изображению (картинка превращается в полутон из точек, поэтому трафарет масштабируется — хоть на всю стену). Загружаешь картинку, настраиваешь преобразование в полутон, указываешь адрес — и через какое‑то время получаешь готовый трафарет.

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

Мы сгенерировали workspace.dsl как снимок текущего состояния — не чтобы дальше генерировать его на каждой правке, а чтобы один раз увидеть картину целиком. Посмотрели на диаграмму — и стало видно, где что должно жить. Рефакторинг занял один вечер вместо недели, которую мы на него закладывали. Без диаграммы это был бы долгий разговор «а кто за что отвечает», с диаграммой — решение нашлось за один обзор экрана.

Но C4 — не серебряная пуля. Диаграмма показывает структуру, а не поток данных: у нас был случай, когда стрелка «Editor → Order» на диаграмме есть, а параметр marginHorizontalMm не сохранялся в состоянии. Баг вылез только в runtime. Тесты и логи C4 не заменяет.

Сама «Трафаретка» сейчас в тестовом режиме: весь путь пользователя можно пройти от начала до конца, но реальные заказы пока не исполняются. Если хотите попробовать: https://trafaretka.website.yandexcloud.net/

C4 и ИИ: главное, зачем это всё

Когда ИИ‑агент работает с проектом, у него нет общей картины: он видит только тот код, который ему дали.

C4-модель эту картину даёт. Она позволяет описать архитектуру системы, а Structurizr — сохранить описание в файл workspace.dsl. Это не «весь код в контексте», а компактная модель с явными границами между модулями. Теперь ИИ увидит, что Order отвечает за данные заказа, а не за отрисовку. Что логика переходов живёт в Coordinator, а не размазана по обработчикам кнопок. Что Frame Geometry — отдельная сущность, а не набор магических чисел в коде.

Чтобы ИИ нашёл эту карту, нужна точка входа — AGENTS.md в корне репозитория. Файл с правилами: как запускать проект, какой стиль кода, где посмотреть архитектуру. ИИ читает его и перестаёт гадать. Это де‑факто стандарт, поддерживается в Cursor, OpenCode и других средах. И в этом же файле стоит записать правила работы с архитектурой — чтобы ИИ их не забывал:

  • Меняешь архитектуру — обновляй workspace.dsl. Иначе диаграмма врёт.

  • C4 не заменяет тесты, логи и внимательность.

  • C4 — не для мелких правок: для CSS и рефакторинга одной функции он не нужен.

Заключение

Мы начинали эту историю с простой боли: ИИ пишет код быстро, но за недели превращает проект в кашу. А закончили тем, что нашли ту самую «цифровую версию листочка» — файл workspace.dsl в репозитории, который видит и человек, и ИИ‑агент.

Главный вывод: графическое программирование проиграло как способ писать код, но нашло свою нишу — описывать структуру. C4 Model + Structurizr DSL — это не «ещё один инструмент для диаграмм», а способ удержать архитектуру в проекте, где код генерируется быстрее, чем вы успеваете его читать.

Мы тоже ещё в процессе: продолжаем осваивать Structurizr и улучшать подход к коду с ИИ. Но даже в таком виде это уже экономит время. Если работаете с ИИ — попробуйте: один файл workspace.dsl, AGENTS.md с правилами, две команды для запуска. Дальше Structurizr сам покажет, что у вас в проекте творится. У нас на «Трафаретке» это заняло один вечер — и сэкономило неделю рефакторинга.

А как вы работаете с архитектурой проекта? ?


Больше заметок про инженерные эксперименты, ЧПУ, ИИ и DevOps — в нашем сообществе во ВКонтакте: https://vk.ru/microfabrica.

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


  1. vvovas
    24.10.2026 08:17

    Первая версия и дальнейшие апдейты workspace.dsl делаются руками или ИИ?


    1. ZloAlien Автор
      24.10.2026 08:17

      ИИ справляется неплохо, но надо следить, что бы он не терял детали архитектуры. Рекомендую после формирования первой версии архитектуры спускаться по уровням и просить ИИ проверить соответствует ли данный модуль коду.