Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты
Иллюстрация архитектуры фронтенд-приложения под управлением LLM

Архитектура фронтенда под LLM-агентов: Feature-Sliced Design, жесткие границы и кодогенерация

Использование LLM-агентов во фронтенде быстро приводит к дублированию компонентов и росту файлов. Разбор практического подхода к удержанию порядка: урезанный трехслойный FSD, статический контроль импортов через dependency-cruiser, генерация типов из OpenAPI и изоляция UI-состояния от серверного кэша.

Архитектура фронтенда под LLM-агентов: Feature-Sliced Design, жесткие границы и кодогенерация

Активное использование автогенерирующих нейросетевых агентов во фронтенд-разработке кардинально меняет скорость появления пользовательских интерфейсов. На начальном этапе создания веб-приложения продуктивность выглядит впечатляюще: экраны собираются за считанные минуты, а готовый JSX-код выглядит опрятно. Однако при увеличении объема клиентской базы и усложнении логики проявляется фундаментальная проблема: структура клиентского кода начинает деградировать значительно быстрее бэкенда. Разработка упирается в гигантские файлы компонентов, неконтролируемое дублирование элементов интерфейса и бесконечный каскад повторных рендеров.

Для сохранения стабильности инженерного конвейера требуется архитектурная модель, специально спроектированная под ограниченный размер контекстного окна нейросети. Применение адаптированного трехслойного Feature-Sliced Design в сочетании со строгим статическим анализом зависимостей позволяет удерживать высокий темп разработки без превращения репозитория в неконтролируемый монолит.

Причины ускоренной деградации фронтенд-кода под управлением LLM

Быстрый рост технического долга на клиентской стороне под управлением LLM-агентов обусловлен двумя ключевыми факторами: особенностями обучающих выборок моделей и спецификой декларативного синтаксиса JSX.

Во-первых, обучающая выборка современных моделей содержит вперемешку три различных поколения библиотеки React: устаревшие классовые компоненты, подходы с ручным вызовом эффектов при маунтинге компонента и современные серверные компоненты. Не имея жестких рамок и локальных эталонов в рамках конкретного проекта, модель начинает смешивать паттерны разных лет в одном файле.

Во-вторых, сама природа JSX позволяет объединять в одном модуле разметку, бизнес-логику, стилистическое оформление и управление состоянием. На бэкенде для нарушения границ слоев агенту требуется добавить невалидный импорт, который легко фиксируется линтером. На фронтенде модель всегда выбирает наименее затратный путь: добавить еще одно состояние useState и тридцать строк JSX в существующий файл вместо создания выделенного модуля. В результате компоненты страниц разрастаются до сотен строк за считанные дни.

Проектирование трехслойной структуры Feature-Sliced Design

Классическая методология Feature-Sliced Design (FSD) предлагает до шести горизонтальных слоев, что часто приводит к преждевременной абстракции в продуктах среднего размера. Для оптимизации работы LLM-агентов используется упрощенная трехслойная структура, минимизирующая объемы контекста:

  1. Слой приложения (app/): содержит маршрутизацию Next.js и верхнеуровневую композицию. Выполняет роль тонких контроллеров, собирающих страницы из изолированных пользовательских сценариев.
  2. Слой пользовательских сценариев (src/features/): вертикальные слайсы, сгруппированные по продуктовому смыслу (upload-photo, buy-credits, cancel-task). Каждый слайс содержит собственную разметку, локальную логику и типы.
  3. Слой фундамента (src/shared/): глобальная инфраструктура, не обладающая знанием о продуктовом домене. Включает UI-кит базовых компонентов (shared/ui), API-клиент (shared/api), интернационализацию и вспомогательные утилиты.

Импорты разрешены строго сверху вниз: appfeaturesshared. Любые горизонтальные зависимости между слайсами внутри слоя features полностью запрещены. Если нескольким сценариям требуется общий компонент или логика, этот элемент спускается в слой shared. Каждая папка сценария предоставляет единую точку входа через index.ts, скрывая внутреннюю реализацию от внешнего мира.

Автоматический контроль архитектурных границ с помощью dependency-cruiser

Правила архитектуры, записанные исключительно в текстовых инструкциях, регулярно нарушаются нейросетевыми агентами. Для жесткого контроля связей между модулями применяется статический анализатор граф-зависимостей dependency-cruiser. Конфигурация .dependency-cruiser.cjs описывает недопустимые связи:

module.exports = {
  forbidden: [
    {
      name: "feature-public-api",
      severity: "error",
      comment: "Внешние модули импортируют фичу строго через ее index.ts",
      from: { path: "^app/" },
      to: { path: "^src/features/", pathNot: "index\.ts$" }
    },
    {
      name: "no-shared-to-features-app",
      severity: "error",
      comment: "Слой shared не может зависеть от верхних слоев features и app",
      from: { path: "^src/shared/" },
      to: { path: "^(src/features/|app/)" }
    },
    {
      name: "no-cross-feature",
      severity: "error",
      comment: "Запрещены прямые зависимости между соседними фичами",
      from: { path: "(^src/features/)([^/]+)/" },
      to: { path: "^$1", pathNot: "$1$2/" }
    },
    {
      name: "no-circular",
      severity: "error",
      comment: "Циклические зависимости запрещены",
      from: {},
      to: { circular: true }
    }
  ]
};

Текстовое поле comment в конфигурации линтера формируется как лаконичное руководство для нейросети. При попытке совершить невалидный импорт агент получает точное текстовое сообщение об ошибке с пояснением, как именно следует исправить структуру кода.

Синхронизация бэкенд-контрактов через OpenAPI и TanStack Query

Для исключения ошибок при взаимодействии с бэкендом доменная модель на клиентской стороне полностью заменяется автогенерируемым API-клиентом. Бэкенд экспортирует спецификацию OpenAPI, из которой утилита orval автоматически генерирует TypeScript-типы и хуки для библиотеки TanStack Query.

Это гарантирует отсутствие ручного кода для отправки HTTP-запросов и исключает неверные имена полей в ответах. Любое изменение структуры данных на сервере приводит к ошибке компиляции TypeScript, давая агенту четкий сигнал для проведения рефакторинга.

Вторым критическим инвариантом является разделение управления состоянием. Все серверные данные и их кэширование находятся под управлением TanStack Query. В локальном состоянии useState или клиенте Zustand хранятся исключительно UI-флаги (активные вкладки, статус открытия модальных окон, введенные в формы значения). Копирование данных ответа сервера в локальный useState категорически запрещено для предотвращения рассинхронизации интерфейса.

Локализация стилей в Tailwind и визуальный контроль через Playwright

Использование традиционных CSS-файлов создает проблемы при работе LLM-агентов из-за необходимости удерживать в контексте два связанных файла (разметку и стили). Применение утилитарного фреймворка Tailwind CSS объединяет описание внешнего вида с разметкой в одной строке, устраняя возможность расхождения классов. Дискретный набор токенов Tailwind заставляет модель использовать строго определенную систему отступов и цветов.

Для решения проблемы "слепой верстки", когда формально валидный код создаёт визуальные дефекты на экране, в конвейер внедряются автоматизированные скриншотные тесты Playwright. Агент запускает безголовый браузер, выполняет скриншот ключевых сценариев и проверяет корректность отображения элементов перед отправкой изменений.

Практические правила для файла инструкций LLM-агента

Для повседневного применения сформирован наглядный список правил:

  • Карта проекта: app/ (маршруты, композиция) → src/features/ (продуктовые сценарии) → src/shared/ (UI-кит, API, инфраструктура).
  • Единый источник серверных данных: использование хуков из src/shared/api/generated. Ручное создание функций fetch запрещено.
  • Изоляция состояния: серверными данными управляет TanStack Query, в useState помещаются только интерактивные UI-флаги.
  • Единственность базовых компонентов: перед созданием новых элементов интерфейса выполняется обязательный поиск по src/shared/ui.
  • Запрет синхронизационных эффектов: вызовы useEffect применяются исключительно для интеграции с внешними браузерными API, но не для управления состоянием.