javascript

Два пути к идеальному DatePicker: классический промптинг или системный подход по работе с AI

  • вторник, 28 апреля 2026 г. в 00:00:07
https://habr.com/ru/articles/1028440/

Привет, коллеги! 

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

Исходный код доступен по ссылке: https://github.com/Codesrc-public-ru/ralf-datapicker

За основу мы возьмем два реальных кейса. Первый - «AI-драфтинг», отлично описанный нашей статье "Создаем WCAG-доступный DatePicker на React: как Claude пишет основу, а мы доводим до ума". Идея: получить 80% кода от нейросети, а остальное довести вручную. Это путь быстрых итераций и реактивного решения проблем.

Второй - «Системный инжиниринг», подход описан в этой документации к инструменту https://github.com/snarktank/ralph. Идея: сначала детальное проектирование, потом итеративная работа модели. Это путь проактивного управления сложностью.

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

Подход №1: AI-драфтинг. Реактивная разработка

Этот подход напоминает скоростную сборку прототипа. Берется детальный промпт, основанный на WAI-ARIA APG, и скармливается Claude. На выходе - практически готовый компонент. Кажется, победа, но, как показывает опыт коллег, именно здесь начинается самое интересное.

Проблемы, с которыми они столкнулись, - это классические интеграционные баги:

  • Конфликт спецификации и реальности: слепое следование APG (ячейки-<td> без button внутри) привело к менее надежной работе скринридеров, чем осознанное отступление от гайда.

  • Неявное управление состоянием: «моргающий» диалог при повторном клике - типичный баг, когда несколько обработчиков событий (onBlur, onClick) конфликтуют в непредсказуемой последовательности.

  • Тонкости a11y: проблема с aria-live="polite" против assertive - это не то, что может предсказать AI. Это нюанс UX, который выясняется только при реальном тестировании со скринридером.

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

Подход №2: Не кодер, а система. Как мы запрягли AI-агента.

Наш путь был другим. Мы сразу решили, что управлять разработкой будет не один большой промпт, а целая инженерная система, в которой AI - дисциплинированный исполнитель, а не свободный художник.

Рисунок 1.Схема работы Ralph (источник)
Рисунок 1.Схема работы Ralph (источник)

В роли исполнителя у нас был подход Ralph цикла - кастомный автономный агент на базе codex cli с моделью gpt 5.4-mini. Чтобы он не «фантазировал», мы написали ему жесткий системный промпт, который был буквально впечатан в его «личность» (AGENTS.md):

  • # Accessible DatePicker - Project Architecture & Coding Rules

You are a senior frontend engineer and software architect working on a production-grade, accessible DatePicker component for React + TypeScript.

Полный промпт находится вот тут.

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

  • PRD.md (Product Requirements Document): наш свод законов. Там было черным по белому: controlled-only API, полная навигация с клавиатуры, корректная aria-разметка. Это незыблемые принципы.

  • tasks.json: детальный таск-трекер для агента. Мы декомпозировали всю работу на атомарные шаги: «создай структуру каталогов», «опиши типы для дат», «напиши чистую функцию addMonths», «собери UI-компонент Button».

  • Использованные субагенты: 

Сам подход был похож на конвейер:

  1. На каждой итерации агент выбирает задачу из tasks.json

  2. Пишет или меняет код.

  3. Мы запускаем внешний контур проверки (The Verifier). Это не часть агента, а наш собственный скрипт, который прогонял unit- и a11y-тесты (Vitest + Playwright), сборку проекта (Vite build) и проверку типов (tsc --noEmit).

  4. Результаты работы агент записывает в progress.md

Если хоть одна проверка падала - агент получал лог ошибки и команду «переделывай». Он не мог перейти к следующему таску, пока текущий не был идеален. Именно такая система вынудила агента проактивно выстроить ту самую трехслойную архитектуру:

  • Ядро (lib/date/, lib/input/): чистая логика.

  • Представление (ui/): «глупые» stateless-компоненты.

  • Связующий слой (model/): логика, которая всем этим управляет.

Этот подход переносит фокус с «напиши мне DatePicker» на проектирование системы, которая этот DatePicker производит и проверяет, а это и есть проактивная разработка в действии.

Результаты работы агента:

 Рисунок 2. Результат разработки ии (компьютерная версия)
Рисунок 2. Результат разработки ии (компьютерная версия)
Рисунок 3. Результат разработки ии (мобильная версия)
Рисунок 3. Результат разработки ии (мобильная версия)
Рисунок 4. Пример зачитывания скринридером кнопки для открытия датапикера
Рисунок 4. Пример зачитывания скринридером кнопки для открытия датапикера
Рисунок 5. Пример зачитывания скринридером ячейки календаря
Рисунок 5. Пример зачитывания скринридером ячейки календаря
Рисунок 6. Пример отображения DatePicker  
Рисунок 6. Пример отображения DatePicker  

Сравниваем в лоб: цена, качество, риски

Давайте сведем все в таблицу и разберем детально.

1. Экономика и стоимость изменения (Cost of Change)

Рисунок 7. Мем про экономику токенов (источник)
Рисунок 7. Мем про экономику токенов (источник)
  • AI-драфтинг: низкий порог входа, но стоимость изменений - лотерея. С хорошим промптом AI может выдать отличное, структурированное решение. Но без жестких архитектурных рамок он часто генерирует монолитный код, где любая фича превращается в болезненный рефакторинг. Ключевой риск - непредсказуемость. Техдолг начинает копиться сразу.

  • Системный инжиниринг: высокая стоимость входа (время на проектирование и большой расход токенов). Нужно добавить поддержку выбора времени? Мы просто расширяем ядро, добавляем новый UI-компонент и обновляем связующий слой. Остальные части системы остаются нетронутыми. Это инвестиция, которая окупается на дистанции.

2. Архитектура и управление сложностью

  • AI-драфтинг: архитектура сгенерированного кода - это по сути черный ящик. Она неявная и хрупкая. Разделение ответственности отсутствует, что делает код трудным для понимания и отладки. Чтобы починить «моргающий диалог», нужно держать в голове всю картину целиком.

  • Системный инжиниринг: думаете, хорошая архитектура в большом проекте спасет от всех проблем? Как бы не так. Мы тоже так думали. Разложили все на три слоя, чтобы спокойно вносить правки. А потом небольшое изменение в логике расчета недель привело к каскадному сбою в датапикере. Прилетело оттуда, откуда не ждали. Это не значит, что подход плохой. Это значит, что для больших систем его недостаточно. Теперь мы всегда добавляем четкие «контракты» между слоями и автоматическую валидацию. Это как отбойники на трассе - не дают случиться аварии.

3. Типология и локализация ошибок

Это самый показательный пункт.

  • Самая частая ошибка при работе с AI-драфтингом - верить, что он сам все сделает правильно. Не сделает. Его главная слабость - интеграция. Он может идеально написать два модуля по отдельности, но на их стыке начнется хаос: ошибки состояния, рендера, событий. А все потому, что у AI нет понимания «правильно/неправильно», он просто выполняет задачу. Наш подход - не исправлять баги, а не давать им появиться. Мы внедряем «гардрейлы» (guard rails) - это набор жестких требований и инструкций, которые AI обязан соблюдать. По сути, мы заставляем его думать об архитектуре. В этом датапикере https://github.com/Codesrc-public-ru/datepicker  модули как раз явные. И такой подход снижает риск скрытых дефектов в разы. 

  • Системный инжиниринг смещает ошибки на уровень юнитов. Если календарь неправильно показывает февраль в високосный год, мы знаем, что проблема находится в одной конкретной чистой функции в слое Core. Эта ошибка легко покрывается юнит-тестом и исправляется изолированно. Мы не чиним «компонент», мы чиним «алгоритм».

Итог: это не битва, а выбор стратегии управления рисками

Становится очевидно, что это не спор о том, «что лучше». Это стратегический выбор, основанный на контексте.

AI-драфтинг - это стратегия для задач с низкой ценой ошибки:

  • Прототипы и MVP: когда нужно быстро проверить гипотезу и выкинуть.

  • R&D и хакатоны: когда скорость исследования важнее долгосрочной поддержки.

  • Изолированные, некритичные утилиты.

Системный инжиниринг - это стратегия для задач с высокой ценой ошибки:

  • Разработка дизайн-систем: где каждый компонент - это продукт для десятков других команд.

  • Создание ядра приложения: где надежность и предсказуемость - не пустые слова.

  • Долгосрочные проекты: где вы знаете, что компонент будет жить, развиваться и рефакториться годами.

Познакомиться с нашим интерактивным проектом можно по ссылке: https://ralf-datapicker.netlify.app/

Наш следующий шаг - гибридный подход. Мы продолжим использовать системный инжиниринг для проектирования, но будем делегировать AI рутинные, хорошо формализованные задачи. Например: «Вот спецификация нашего математического ядра и тесты. Напиши Storybook-истории для вот этого ‘глупого’ UI-компонента сетки календаря».

AI - это мощнейший ускоритель, но он не заменяет инженера. Он заменяет рутину. А задача инженера - как раз и состоит в том, чтобы спроектировать систему, в которой рутинных, предсказуемых задач будет как можно больше.

Автор статьи: Ищенко Тимофей @Is_Tim, senior frontend-разработчик в “Исходном Коде”.

А какой стратегии придерживаетесь вы? Где для вас проходит граница, за которой скорость перестает оправдывать риски?