ИИ пишет код быстро, но за недели превращает проект в спагетти. Мы (инженерная лаборатория «Микрофабрика») попробовали решить это с помощью 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.
vvovas
Первая версия и дальнейшие апдейты workspace.dsl делаются руками или ИИ?
ZloAlien Автор
ИИ справляется неплохо, но надо следить, что бы он не терял детали архитектуры. Рекомендую после формирования первой версии архитектуры спускаться по уровням и просить ИИ проверить соответствует ли данный модуль коду.