Введение
Я открыл демо-версию DatePicker’а, и она выглядела вполне обычно. Поле ввода, кнопка, всплывающий календарь, keyboard navigation, метки для screen reader’ов, макеты для настольных компьютеров и мобильных устройств.
С внешней стороны ничего особенного. Интересным был не сам компонент, а то, как мы его создали.
Привет, коллеги!
Меня зовут Илья, я технический директор компании «Исходный код». Наша frontend-команда последние шесть месяцев занималась улучшением доступности компонентов React (a11y). Мы хотели проверить простой вопрос на задаче, которая снаружи выглядит небольшой, но внутри очень быстро разрастается: сможет ли AI-агент создать доступный DatePicker производственного уровня для React и TypeScript?
Ответ — да.
Более точный ответ — да, если рассматривать AI как часть инженерной системы, а не как волшебную машину для печати кода.
Open-source проект доступен по ссылке на Github. Демо-версия DataPicker’а доступна по этой ссылке.
Почему DatePicker — это хороший тест?
DatePicker кажется простым, но это не так.
Сначала появляется сетка календаря и выбранное значение, затем следуют: keyboard navigation, управление фокусом, screen reader’ы, контролируемое состояние, локализация, мобильная верстка, проверки доступности (a11y) и все те мелкие состояния, которые возникают, когда реальные пользователи дважды нажимают или переключаются с помощью клавиши Tab, а затем вновь открывают всплывающее окно или очищают значение.
Мы сравнили два подхода:
-
80% работы доверяем AI, остальные 20% делаем руками (то, что мы делали в предыдущей статье). Вы даете модели подробную подсказку, получаете большую часть компонента, а остальное дорабатываете вручную.
-
Системное проектирование с помощью AI-агента. Вы готовите требования, архитектурные правила, задачи, тесты, проверки, и только потом позволяете агенту писать код. При этом, модель дорабатывает результат через итерации и prompt engineering. Подход хорошо описан в документации к инструменту по ссылке.
Оба подхода могут работать, разница только в том, где они дают сбой. Давайте посмотрим на них поближе.
Подход 1. 80% работы доверяем AI, остальные 20% делаем руками
Очевидный шаг — дать модели Claude четкий запрос на основе WAI-ARIA APG и попросить ее сгенерировать DatePicker.
Для прототипа этого может быть достаточно, ведь модель знает, как выглядит сетка календаря, она может писать компоненты React, обработчики, атрибуты ARIA и логику keyboard navigation.
Проблема начинается когда первая версия выглядит почти готовой.
Большинство ошибок возникает не внутри отдельного элемента, а на стыках между элементами:
-
Строгое следование APG может привести к созданию разметки, которая выглядит правильно, но работает с реальными экранными считывателями хуже, чем контролируемое отклонение от руководства.
-
Диалоговое окно может начать мигать из-за того, что обработчики
onBlurиonClickконфликтуют друг с другом. -
Разница между атрибутами
aria-live="polite"иassertiveвыглядит незначительной в коде, но становится реальной проблемой UX, когда screen reader начинает озвучивать изменения.
В этом заключается главный недостаток разработки с помощью AI. Модель может писать код, который выглядит правильно, но она не может достоверно оценить поведение системы в целом.
Архитектура также разрастается случайно. Вы исправляете ошибку с фокусом, затем — с состоянием, а потом — с доступностью. Компонент начинает работать, но его внутренняя структура определяется исправлениями. Для прототипа это приемлемо, но для компонента, который должен работать в реальных условиях, это рискованно.
Подход 2. системное проектирование с помощью AI-агента
Мы выбрали более медленный путь.
Я не хотел одного большого запроса типа «создай мне DatePicker». Мне нужен был контролируемый процесс, в котором у AI-агента было бы меньше возможностей для импровизации и больше давления, чтобы он следовал инженерным правилам.
Агент работал в конфигурации по типу цикла Ralph на основе codex cli и gpt 5.4-mini.
Мы предоставили ему файл AGENTS.md со строгими правилами проекта. В нем агент описывался как старший frontend-инженер, работающий над доступным DatePicker’ом производственного уровня для React и TypeScript.
Полный promt доступен по ссылке.
Затем мы добавили внешнюю память:
-
Файл PRD.md был основным источником достоверной информации. В нем были зафиксированы требования: API, поддерживающий только элементы с атрибутом
controlled, полная keyboard navigation, правильная разметка ARIA и предсказуемое поведение. -
Файл tasks.json служил для отслеживания задач. Мы разбили работу на небольшие этапы: создание папок, определение типов даты, написание чистых функций даты, создание компонентов UI, а затем их соединение.
-
Файл progress.md был рабочим журналом. Агент должен был фиксировать, что изменилось и в каком состоянии находился проект.
И задействовали субагентов:
Благодаря этому задача «напиши мне DatePicker» превратилась в процесс, в ходе которого создается и проверяется достойный DatePicker.
Результаты:
Верификатор был важнее promt’а
Ключевой частью был верификатор. Агент выбирал задачу, писал или изменял код, а затем наш внешний скрипт проверял результат.
Он запускал модульные тесты и тесты доступности с помощью Vitest и Playwright, собирал проект с помощью Vite и проверял типы с помощью tsc -noEmit.
Если что-то давало сбой, агент получал журнал ошибок и должен был исправить текущую задачу. Он не мог двигаться дальше, пока задача не была выполнена успешно. Благодаря этому ошибки оставались локальными.
Агент по-прежнему мог генерировать некорректный код, но не мог незаметно перенести неработающий шаг на следующий этап.
В результате проект естественным образом перешел к трехуровневой архитектуре:
-
Ядро содержало чистую логику в lib/date и lib/input.
-
UI содержал «глупые» компоненты без состояния.
-
Модель связывала состояние, поведение и представление.
Это разделение было сделано не ради красоты, оно снижало затраты на внесение изменений:
-
Ошибка в вычислении даты относится к чистой функции.
-
Ошибка фокусировки относится к модельному слою.
-
Визуальное состояние относится к UI.
Нам больше не приходилось отлаживать весь компонент как один большой блок.
Архитектура не решила всех проблем
Я не хочу преподносить это как волшебство, даже хорошая архитектура по-прежнему давала сбои в некоторых местах. Однажды небольшое изменение в расчете недели вызвало каскадную проблему в DatePicker’е, она возникла из слоя, откуда мы этого не ожидали. Это показало нам полезный предел: одних слоев недостаточно, большой системе также нужны контракты между слоями и проверка этих контрактов.
Теперь мы более ответственно подходим к границам. У слоя должно быть не только название, но и контракт, и этот контракт должен тестироваться.
Настоящая разница заключается в стоимости изменений.
Создание кода с помощью AI изначально обходится недорого. Достаточно составить один четкий promt, чтобы получить рабочую v1.0. Для минимально жизнеспособных продуктов (MVP), хакатонов, экспериментов и одноразовых прототипов это зачастую является правильным выбором.
Стоимость проявляется позже.
Без архитектурных правил модель может сгенерировать код, который сегодня работает правильно, а завтра станет источником проблем. Добавление выбора времени, выбора диапазона или настраиваемого форматирования может превратиться в рефакторинг вместо простого расширения.
Системная инженерия изначально обходится дорого.
Вы тратите время на PRD, декомпозицию задач, правила агентов, верификацию и тестирование. Вы также тратите больше токенов.
Окупаемость проявляется, когда компонент меняется.
Нужен выбор времени? Мы расширяем ядро, добавляем компонент UI и обновляем уровень модели. Остальные части остаются практически нетронутыми. Изменения не бесплатны, но они предсказуемы. Именно эта предсказуемость и является главной ценностью.
Это не борьба методов
Я бы не стал применять системное проектирование с помощью AI-агента повсеместно.
Подход «80% работы доверяем AI, остальные 20% делаем руками» хорошо подходит, когда цена ошибки низкая: прототипы, R&D, хакатоны, изолированные утилиты и эксперименты, которые можно выбросить. Кстати, познакомиться с нашим DataPicker’ом из прошлой статьи можно по ссылке.
Cистемное проектирование с помощью AI-агента лучше подходит, когда цена ошибки высокая: системы дизайна, общие компоненты UI, интерфейсы с высокими требованиями к доступности, основная логика приложения и долговечные продукты.
Выбор заключается в том, какой риск вы готовы взять на себя: подход 80/20 переносит риск в будущее, а системное проектирование окупается раньше, снижая этот будущий риск.
Что я понял?
-
AI не устранил инженерную работу из этой задачи, а поднял ее на уровень выше: мы написали меньше кода руками, и в то же время уделили больше внимания замыслу, архитектуре, верификации и контролю отклонений — компромисс кажется правильным.
-
AI может выполнять четкие задачи и по-прежнему не может решать: что следует создать, соответствует ли результат реальному замыслу и проверяют ли «зеленые» тесты то, что нужно — роль человека в этом процессе не исчезает, а становится в большей степени связанной с определением направления, установлением границ, заключением соглашений и верификацией.
Заключение
Создание компонента DatePicker’а с использованием AI — это сложно не потому, что React сложен, а потому, что у компонента есть множество мелких требований: состояние, фокус, поведение клавиатуры, вывод для screen reader’ов, логика даты, макет и контролируемое поведение API.
Для меня это главный урок:
Один promt может сгенерировать огромное количество кода, а система может предотвратить отклонение этого кода от заданного курса.
Будущее AI в разработке — это не только более совершенные модели, а еще и качественная инженерия вокруг моделей.
Я пока не рассматриваю агентов как автономных разработчиков, но вижу в них сильных исполнителей в рамках контролируемой среды.
ссылка на оригинал статьи https://habr.com/ru/articles/1052932/