Когда ИИ пишет ваше приложение на React, но игнорирует принципы чистого кода
Узнайте семь принципов чистого кода — DRY, единая ответственность, защитные клозы и другие — которые часто нарушаются в React-коде, сгенерированном ИИ, и как их исправить.
Недавно один клиент передал нам проект по созданию веб-сайта для заказа пиццы.
Сначала объем работ казался управляемым.
Лендинг-страница.
Раздел меню.
Разные категории пиццы.
Отдельные страницы товаров.
Корзина покупок.
Процесс оформления заказа.
Плюс панель администратора для управления всем на фоне.
Естественным предположением было:
«Это должно занять недолго».
Затем было принято решение воспользоваться ИИ-ассистентом для программирования.
Именно тогда все стало интереснее.
Предоставление ИИ большей части работы по программированию
Проект был создан с использованием React.
Вместо того чтобы вручную писать каждую строку кода, мы отправляли запросы ИИ для генерации отдельных частей.
Например, такой запрос:
«Создай компонент карточки пиццы».
мгновенно приводил к созданию этого компонента.
Далее:
«Создать корзину покупок».
Выполнено.
Затем:
«Создать экран оформления заказа».
Также выполнено.
Затем:
«Создать панель управления для администратора с отображением статистики, заказов и информации о клиентах».
Готово.
Скорость работы была действительно впечатляющей.
Работа, которая обычно занимала часы, выполнялась за несколько минут.
Результат включал:
- Компоненты React
- Формы
- Таблицы данных
- Виджеты панели управления
- Интеграция с API
- Индикаторы загрузки
- Обработка ошибок
- Макеты, адаптирующиеся под размер экрана
И приложение действительно работало.
Всё выглядело многообещающе.
Казалось, что был найден идеальный способ работы:
Идея → Запрос → Код → Релиз
Но отсутствовал один критически важный шаг.
Проверка того, что было создано.
Клиент так и не увидел сам код
С точки зрения клиента всё выглядело отлично.
Сайт работал без проблем.
Панель управления функционировала нормально.
Меню отображалось правильно.
Заказы появлялись так, как и ожидалось.
Визуально всё было безупречно.
Так в чём же была проблема?
Проблему нельзя было заметить снаружи.
Под поверхностью кодовая база постепенно превращалась в источник проблем с техническим обслуживанием.
Сначала это не было очевидно.
Но по мере добавления новых функций начал проявляться определённый паттерн.
Постоянно возникала одна и та же мысль:
"Подождите... разве не было уже создано что-то почти идентичное этому?"
Именно тогда возникло важное осознание:
Искусственный интеллект действительно хорошо справляется с созданием рабочего кода.
Но чистый код — это не то, что он генерирует автоматически.
Проблема №1: Повторяющаяся логика повсюду
Оказалось, что практически идентичная логика была распространена по всему приложению.
Например, несколько отдельных функций самостоятельно вычисляли суммы.
Одна из них выглядела так:
const total = price * quantity;
В другом месте появилась практически идентичная версия:
const orderTotal = price * quantity;
А в ещё одном файле:
const cartAmount = price * quantity;
Все три функции по сути выполняли одни и те же вычисления.
Технически ничего не было сломано.
Но как только требовалось изменить правила ценообразования, приходилось находить каждую копию кода и обновлять её.
Именно тогда стало ясно первое принцип чистого кода.
1. DRY — Не повторяйтесь
Каждый раз, когда одна и та же логика встречается несколько раз, стоит задать себе следующий вопрос:
Можно ли объединить её в один источник?
Например:
const calculateItemTotal = (price, quantity) => {
return price * quantity;
};
С этого момента любая часть приложения может вызывать ту же общую функцию.
Цель не в том, чтобы принудительно использовать повторное использование везде.
Это просто способ устранить бесполезное дублирование.
Проблема №2: Один компонент делал всё
В какой-то момент один из сгенерированных файлов React был открыт для более детального рассмотрения.
Он продолжал увеличиваться в размере.
Строка за строкой.
Он просто продолжал расти.
Этот единственный компонент отвечал за:
- Запросы к API
- Управление состоянием формы
- Логику проверки
- Видимость модального окна
- Отображение таблицы
Короче говоря, один файл фактически сам по себе обрабатывал половину работы приложения.
Очевидным выводом было:
«Поддержка этого будет сложной.»
Поэтому компонент был разделен на более мелкие части.
Вместо сохранения текущей структуры:
Orders.jsx
↓
800+ lines
она была реструктурирована примерно так:
Orders
├── OrderFilters
├── OrderTable
├── OrderRow
├── OrderModal
└── useOrders
Это изменение напрямую привело к следующему принципу.
2. Одна ответственность
Компонент или функция должны иметь одну четко определенную задачу.
Если вы не можете описать, что делает компонент, одним предложением, скорее всего, у него слишком большой объем функций.
Например:
OrderTableотображает список заказов.
Это четкое и сфокусированное описание.
Сравните его с этим:
OrderTableзагружает заказы, проверяет права пользователя, рассчитывает итоговую сумму, отправляет уведомления и отображает всё в виде таблицы.
Такое описание — это тревожный сигнал.
Проблема №3: вложенные операторы if захватили код
В какой-то момент часть логики проекта выглядела так:
if (user) {
if (user.isActive) {
if (user.isVerified) {
// create order
}
}
}
Она работала корректно.
Но просмотр её был утомительным.
Поэтому она была переписана так:
if (!user) return;
if (!user.isActive) return;
if (!user.isVerified) return;
// create order
Эта версия была гораздо проще для понимания с первого взгляда.
Здесь и приходит в игру следующая привычка:
3. Ранние возвраты / защитные конструкции
Вместо того чтобы прятать основную логику под несколькими уровнями вложенных условий, лучше сразу обрабатывать недопустимые случаи и выходить из функции раньше времени.
Invalid?
↓
Return
Invalid?
↓
ReturnEverything okay?
↓
Do the actual work
В результате основная логика остается простой, читаемой и не содержит ненужного внедрения отступов.
Проблема №4: ИИ предложил новые компоненты для уже существующих элементов
Эта ошибка была довольно неловкой.
Запрос, сделанный ИИ, звучал так:
"Создайте модальное окно подтверждения для удаления заказа."
ИИ ответил, сгенерировав совершенно новый компонент.
Его почти без вопросов приняли.
Затем быстрый поиск в проекте выявил что-то важное.
Компонент модального окна уже существовал.
Его можно было просто переиспользовать.
В тот момент стало ясно одно:
ИИ автоматически не знает, что уже существует в данной базе кода.
И даже когда система это знает, она всё равно может решить создать что-то новое вместо того, чтобы использовать уже существующее.
Лучший подход — дать указание вроде следующего:
«Прежде чем создавать новый компонент, проверьте существующую базу кода на наличие чего-либо, что можно использовать заново».
Этот опыт указывает на ещё одно правило:
4. Используйте существующее перед созданием нового
Прежде чем добавить:
- новый компонент
- новую вспомогательную функцию
- новый пользовательский хук
- нового вспомогателя API
спросите себя:
«Это уже существует в проекте?»
Чистая база кода определяется не наличием десятков элементов, которые можно использовать заново.
Она определяется разработчиками, которые используют то, что уже имеется, вместо того чтобы тайно копировать его.
Проблема №5: Имена переменных иногда были ужасными
Код, сгенерированный ИИ, появляется быстро.
И легко принять имена переменных вроде:
const data = ...
const result = ...
const x = ...
const temp = ...
Такие имена с компиляцией не имеют проблем и не вызывают ошибок.
Но спустя месяцы к чему на самом деле относится data?
Это данные о пицце?
Информация о заказах?
Записи клиентов?
Что-то, связанное с панелью управления?
Поэтому по мере роста проекта именование должно становиться более осмысленным.
Вместо того чтобы писать:
const data = await getOrders();
лучше использовать более понятную версию:
const orders = await getOrders();
А вместо того чтобы:
const x = users.filter(...);
так гораздо легче читать:
const activeUsers = users.filter(...);
Это приводит к ещё одному простому правилу:
5. Используйте имена, которые объясняют код
Описательное именование часто позволяет полностью обойтись без комментариев.
Читая что-то вроде:
const activeUsers = users.filter(
(user) => user.isActive
);
Это уже точно объясняет, что происходит.
Нет необходимости добавлять:
// Filter users who are currently active
Код говорит сам за себя.
Проблема №6: Оптимизация того, что не требует оптимизации
На каком-то этапе в подобном проекте легко начать думать:
«Этот код нуждается в дополнительной оптимизации».
Это часто приводит к изучению таких вещей, как:
useMemo()
useCallback()
и ещё нескольких приемов улучшения производительности.
Но стоит здесь остановиться.
Оптимизация не всегда является хорошей вещью.
Возьмем простой расчёт, например:
const total = price * quantity;
Нет причин оборачивать его в какой-то сложный шаблон оптимизации только потому, что существуют соответствующие инструменты.
Это лишь сделает код более сложным для понимания.
Это указывает на ещё один урок:
6. Оптимизируйте только тогда, когда есть реальная проблема
Не начинайте оптимизацию из-за:
«Кто-то в интернете сказал, что этот метод быстрее».
Сначала выявите настоящий узкий место.
Чрезмерно ли часто перерисовывается какой-либо компонент?
Медленный ли запрос к API?
Действительно ли сложен какой-либо расчёт?
Слишком большой ли размер пакета данных?
Неэффективен ли запрос к базе данных?
Определите реальную проблему, прежде чем что-либо менять.
Только тогда следует приступать к оптимизации.
Чистый код плюс ненужная оптимизация приводят к избыточной сложности.
Проблема №7: Больше не просить ИИ «исправить всё»
Возможно, это самый важный урок из подобного проекта.
Иногда возникает соблазн просто сказать:
«Просто приведите в порядок весь проект для меня».
Но стоит воздержаться от этого.
Что вообще означает «привести в порядок» в этом контексте?
Искусственный интеллект может ответить следующим образом:
- переименование переменных и файлов
- переорганизация структуры папок
- введение новых абстракций
- объединение функций
- удаление кода, который он считает ненужным
- перестройка архитектуры
Некоторые из этих изменений действительно могут помочь.
Другие могут оказаться бесполезными.
А некоторые могут тихо привести к появлению багов.
Лучше использовать более медленный, поэтапный подход.
Начните с вопроса:
«Посмотрите на этот проект, но пока ничего не меняйте».
Затем:
«Укажите на любую дублирующуюся логику».
Затем:
«Скажите мне, какие из этих дубликатов действительно стоит исправлять».
Только после этого следует рассмотреть предложения.
Затем вносится одна корректировка.
После этого производится тестирование.
Это становится седьмым правилом:
7. Поймите прежде, чем рефакторить
Никогда не позволяйте ИИ вмешиваться в код, который сначала не был полностью понят.
Начните с анализа.
Затем убедитесь, что логика понята.
После этого решите, что делать.
Затем внесите изменения.
После этого проверьте их с помощью теста.
Что иллюстрирует сайт с информацией о пицце
Интересно, что клиенты редко спрашивают:
«Ваш код чистый?»
Вопрос обычно проще:
«Сайт работает?»
И часто он действительно работает.
Тем не менее у разработчиков есть ответственность, выходящая за рамки первой рабочей версии.
Программное обеспечение редко остается неизменным после выпуска.
В конце концов клиент возвращается с просьбами:
"Можем ли мы добавить отслеживание доставки?"
Затем:
"Давайте добавим коды скидок."
Затем:
">Нам нужна поддержка нескольких филиалов."
Затем:
"Добавьте учетные записи для сотрудников ресторана."
Затем:
">Нам нужны отчеты."
Вскоре этот небольшой сайт с информацией о пицце превратился в полноценную систему.
Именно в этот момент чистый код начинает оправдывать приложенные усилия.
Вина не в ИИ
Здесь важно быть справедливыми.
Вина не в ИИ.
Это действительно ценно во время подобной разработки.
Это помогает с:
- ускорением работы
- испытанием различных подходов
- выполнением повторяющихся задач по программированию
- исправлением багов
- созданием структурных компонентов
- поиску решений
Настоящая проблема никогда не заключается в том, что ИИ генерирует код.
Проблема в том, что генерируемый код иногда принимается без достаточно тщательной проверки.
Это две совершенно разные проблемы.
Изменённый рабочий процесс при программировании с ИИ
После подобного проекта имеет смысл изменить подход.
Процесс может выглядеть примерно так:
Understand the requirement
↓
Explore existing code
↓
Plan the solution
↓
Ask AI for implementation
↓
Review the generated code
↓
Simplify
↓
Test
↓
Refactor if necessary
ИИ по-прежнему выполняет значительную часть фактического ввода кода.
Но большая часть мыслительной работы возвращается к разработчику.
Похоже, именно к такому балансу движется разработка программного обеспечения в целом.
Искусственный интеллект пишет код, но база кода по-прежнему принадлежит вам
В этом и заключается главный вывод из всего этого опыта.
Как только искусственный интеллект начинает создавать ваш код, возникает соблазн предположить:
"Искусственный интеллект написал это, значит он должен понимать, что делает."
Но такое предположение неверно.
Владение базой кода остается у вас.
Именно вы будете её поддерживать в дальнейшем.
Именно вы будете находить баги, когда они появляются.
Именно вам придётся объяснять, как она работает, другим людям.
Именно вы вернётесь к ней через несколько месяцев и снова попытаетесь её понять.
В какой-то момент другой разработчик может открыть проект и задаться вопросом:
">В чём заключалась логика этого подхода?"
В идеале сам код должен ясно отражать эту логику.
Семь принципов качественного кода, которых стоит придерживаться
Если свести всё это к сути, вот что запомнилось мне:
1. DRY
Избегайте дублирования одной и той же логики в нескольких местах без веской причины.
2. Одна ответственность
Убедитесь, что у каждой функции или компонента есть четко определенная цель.
3. Ранние возвраты
Сокращайте уровень вложенности, чтобы основной путь выполнения логики оставался понятным.
4. Используйте существующее перед созданием нового
Прежде чем создавать что-то новое, ищите готовые решения в кодбазе.
5. Хорошее название
Выбирайте имена, которые четко указывают на то, что представляет собой переменная или функция.
6>Оптимизация с доказательствами
Откладывайте добавление сложности, если нет измеримой проблемы с производительностью, которая бы это оправдывала.
7. Поймите, прежде чем рефакторить
Искусственный интеллект может предложить изменения, но решение о том, какие из них стоит внедрить, принадлежит вам.
Перед началом проекта сайта с пиццей я предполагал, что основной преимуществом помощи ИИ будет:
быстрая скорость работы.
Оглядываясь назад, я пришел к выводу, что существует еще более значительное преимущество.
Искусственный интеллект освобождает умственные ресурсы для тех аспектов разработки, которые действительно требуют суждения.
Вместо того чтобы тратить усилия на рутинные задачи, мы можем направить эту энергию на более важные вопросы:
Действительно ли это правильный подход?
Можно ли сделать это проще?
Уже существует что-то подобное в проекте?
Выдержит ли такой дизайн появление новых функций?
Сможет ли другой разработчик следовать этому подходу?
В этом и заключается настоящая сложность работы с разработкой с помощью ИИ.
ИИ способен почти мгновенно генерировать сотни строк кода.
Подтверждение того, что эти строки действительно заслуживают своего места в кодовой базе, по-прежнему ложится на нас.
Возможно, это и есть обновленное определение качественного кода с учётом использования ИИ в процессе:
То, что код запускается, — это не финишная черта. Главное — возможность его последующего обслуживания.
Связанные статьи
- Почему доступ к ИИ, а не его возможности, представляет реальный риск зависимости — В этой статье рассматриваются недавние инциденты с контролем за экспортом, связанные с Claude и GPT-5.6, чтобы показать, что доступ к моделям ИИ является нестабильным фактором, независимым от их базовых возможностей.