Я написал, чего в системе не будет. Это оказалось важнее всего остального
Я запустил проект создания системы управления корпоративной архитектурой, в которой объекты и связи живут с полной историей изменений, как код в git: ветки, коммиты, сравнение состояний, слияние. Работаем вдвоём: я и ИИ‑агент. Я ставлю задачи, принимаю решения и отвечаю за результат; он пишет документы и код — и он же был соавтором архитектуры, которую я собрал в диалоге с ним ещё до того, как завёл репозиторий. Начну с постановки задачи: именно она связала мне руки сильнее, чем любое последующее архитектурное решение.
Что я хочу получить
Корпоративная архитектура обычно живёт в трёх местах сразу: в чьей‑то голове, в презентации двухлетней давности и в системе учёта, которая описывает не то, что спроектировано, а то, что кто‑то успел завести. Я хочу репозиторий, где ландшафт хранится как онтологическая модель — с типами объектов, правилами соединения и атрибутами, — и где у этой модели есть история.
Три свойства делают задачу нетривиальной.
Типы объектов определяет архитектор, а не разработчик. Не «мы добавили в схему таблицу для микросервисов», а «архитектор зашёл в интерфейс и описал новый тип объекта с атрибутами и правилами соединения». Развитие модели не должно зависеть от цикла разработки.
Версионирование как в git. Текущее состояние ландшафта, проектные изменения и целевое состояние на несколько лет вперёд существуют одновременно, в разных ветках одной модели. Их можно сравнить, слить, откатить.
Автоматика ничего не меняет сама. Данные из внешних систем превращаются в предложения с обоснованием, а решение принимает архитектор.
Как появилась архитектура
Готовой я её не получал и в одиночку не писал. На вход дал предмет, источники данных, перечень функций, требование пройти тестирование на уязвимости — и отдельным пунктом попросил задавать уточняющие вопросы.
Просьба окупилась не сразу. Первая редакция выглядела законченной: варианты сравнены, стек выбран, железо посчитано. Но заканчивалась она отдельным разделом — тремя развилками, где мой ответ изменит рекомендацию. Где разворачивается продуктивная среда? Какой стек ближе команде? Есть ли требования регуляторов? Ответы переписали топологию развёртывания и половину стека: продуктивная среда похудела почти вдвое, оркестратор уступил место связке виртуальных машин под управлением Ansible.
Дальше — то, о чём честнее сказать сразу. При подготовке окончательной версии из документа намеренно убрали сравнение вариантов хранилища, отклонённые СУБД, ветвления по целевой ОС и все вопросы с ответами; архитектурные решения переформулировали. Как документ на согласование он от этого выиграл. Но вместе с альтернативами исчезло основание для их пересмотра: вниз от финальной версии цепочка прослеживается полностью — требование ссылается на раздел архитектуры, задача на требование, тест на критерий приёмки, — а вверх обрывается. Через две недели мне понадобилось объяснить, почему метамодель не на ArchiMate и почему нет графовой СУБД, и ответа в репозитории не нашлось.
Разрыв я закрыл задним числом — только потому, что сохранился лог проработки. Теперь в проекте есть отдельный документ о происхождении архитектуры: что подавалось на вход, какие развилки пройдены, что отвергнуто и по какому основанию.
Строки про то, чего не будет
Самая полезная часть постановки — не перечень возможностей. Это перечень исключённого: коннектор к одной из смежных систем учёта, публичный API для чтения модели, многоязычность, атрибуты, вычисляемые из связей, двусторонняя синхронизация задач с внешним трекером, событийный импорт через вебхуки.
У каждой позиции написано основание. Это не сокращение объёма ради сроков, а фиксация условий, при которых исключение перестанет действовать.
«Коннектор не нужен, потому что после синхронизации источник и так будет содержать полный перечень объектов» — проверяемое утверждение с датой проверки. Если синхронизация не завершится, я это увижу и верну коннектор; а плагинную модель коннекторов на такой случай сохранил в архитектуре — новый источник добавляется отдельным классом, без правки ядра. «Многоязычность не требуется» — утверждение про мою организацию, а не про технологию.
Исключение без основания — не граница, а умолчание. Умолчание всплывает через полгода в форме «мы же вроде договаривались».
Кто‑то скажет: зачем ограничивать, это же ИИ, ему лишние строки кода написать несложно. Опыт показывает, что чем больше невостребованного функционала в системе, тем сложнее её дорабатывать с учётом всех связей между подсистемами, тем дороже её обслуживать и содержать, тем больше нужно мощностей и тем чаще случаются инциденты. Во внедрении информационных систем я всегда придерживался правила: лучше взять систему, которая подходит тебе на 80%, но компактнее, чем монстр, который сразу закроет 90% потребностей, но 60%, а то и все 80% его функционала так никогда и не будут востребованы. Разница не только в стоимости самой системы, но и в стоимости её поддержки. Поэтому, создавая систему или внедряя новую, всегда важно ограничить требования.
Числа вместо прилагательных
Я запретил себе слова «быстро», «удобно» и «надёжно». Вместо них в постановке измеримые показатели: сколько ждать открытия карточки объекта, за сколько фильтруется каталог полного размера, сколько занимает сравнение двух ветвей, какая допустима потеря данных при аварии, какому уровню защищённости соответствует приложение.
Каждый из них потом что‑то мне запретил.
Требование к скорости сравнения ветвей означает, что состояние ветки нельзя восстанавливать проигрыванием истории при каждом обращении. Значит, нужны материализованные снимки состояния. Значит, появляется второй источник истины — и обязанность регулярно доказывать, что он совпадает с первым. В рисках это записано отдельной строкой, а мерой стала ночная сверка и пересчёт снимка из истории как эталона.
Требование к обходу графа — та граница, за которой обычно начинается разговор про графовую СУБД. Первой идеей была TerminusDB — СУБД, которая хранит графы и объекты в модели, похожей на git: с ветками, слияниями и историей. Закончился разговор решением оставить одну PostgreSQL, а коммит‑граф, ветки и слияние реализовать в доменном слое приложения. Не потому, что так быстрее, а потому, что вторая СУБД — это второй контур эксплуатации, второй способ резервного копирования и второй источник расхождений. Цену я записал рядом с решением: рекурсивные запросы вместо готовых обходов и обязательные лимиты глубины, иначе мой же показатель превращается в способ уронить систему.
Что постановка предрешила
Три её пункта не оставили архитектуре выбора.
Модель задаётся в рантайме. Раз архитектор меняет типы объектов без разработчика, типы не могут быть таблицами схемы: изменение типа не должно требовать миграции. Отсюда двухуровневая модель — метамодель как данные, экземпляры как документы с валидацией по схеме, сгенерированной из метамодели. И отсюда же порождённый класс проблем: индексируемые атрибуты требуют динамического DDL, а динамический DDL — риск инъекции номер один в моей модели угроз. Он закрыт сразу несколькими мерами: проверкой формата кода на уровне БД, именами индексов из идентификаторов, экранированием, отдельной ролью БД и лимитом на число индексов.
Автоматика не изменяет модель. Ограничение задано как свойство предметной области: данные внешних систем недостаточно надёжны, чтобы писать их в модель напрямую. Но обеспечено оно не словами. При развёртывании создаются несколько ролей БД с разными правами, и у учётной записи импорта нет права записи в коммит‑граф. Плюс архитектурный тест в конвейере сборки, который падает, если фоновый обработчик вызывает фиксацию.
Коммит соответствует одному решению архитектора. Отсюда гранулярность истории и особый режим первичной загрузки, где одно решение охватывает целый класс однозначно сопоставленных объектов. Без него загрузка ландшафта дала бы столько же коммитов, сколько объектов, — каждый формально «решение архитектора», а фактически шум.
Чего это стоило
В первый же день я выбросил код, который агент написал без запроса, — он завёл репозиторий по архитектуре и заодно реализовал сервисы. Код, возможно, был рабочий и соответствовал архитектуре, но не выводился ни из одного функционального требования, потому что требований ещё не существовало. Принимать его было нечем.
Первый пакет требований появился через несколько дней и описывал то, что я до этого считал очевидным: каркас приложения, вход и администрирование. В «очевидный каркас», как выяснилось, входят обработка ошибок в стандартном формате, идентификатор запроса в журналах, лимиты объёма запроса, раздельные схемы ввода и вывода против массового присвоения полей, права ролей БД на новые таблицы и готовность к обновлению без простоя. Ни одно из этого не следует из фразы «сделать каркас», но каждое выводится из архитектуры.
И ограничение, о котором честнее сказать: архитектура утверждена, но не проверена реализацией. Ни один из измеримых показателей пока не замерен — замеры возможны со второй фазы.
Что я вынес
Постановка задачи заканчивается там, где перечислено исключённое — с основанием по каждой позиции. Основание превращает границу в проверяемое утверждение с известной ценой пересмотра.
ИИ‑агент — не человек, которому можно образно описать, что ты хочешь, а он сам всё додумает. Задачи ему должны ставиться максимально конкретно и с жёсткими рамками.
Когда пишешь документ или код сам, план составляют редко и ещё реже по нему идут. С ИИ без чёткого плана работа превращается в ходьбу по кругу.
Измеримый показатель — часть постановки, а не приложение к ней. И каждый из них потом что‑нибудь запретит: это и есть его работа.
«Модель задаётся в рантайме» — не функция, а ограничение, приходящее вместе со своим классом рисков. Соглашаться на него надо вместе с ними.
Принцип, не обеспеченный технически, принципом не является. Проверка простая: что сломается, если его нарушить? Если ответ «ничего, но мы договорились» — принципа нет.
Чистить документ от альтернатив можно, но отвергнутое должно переезжать в отдельный файл, а не исчезать: утвердительное решение и запись «что рассматривалось и почему отклонено» — разные артефакты, и второй нужен ровно тогда, когда решение начнут оспаривать.
Что дальше
В следующей статье — о том, как эта архитектура собиралась: что я подавал на вход, какие уточняющие вопросы изменили решения и где я почти взял графовую СУБД.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.