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

Ниже - моя оснастка: память проекта, повторяемые процедуры и доступ к боевому серверу. Плюс то, о чём обычно молчат, - как проверять, что сгенерированное действительно работает.

Память проекта важнее промпта

Главная проблема не в том, что модель плохо пишет код. В том, что она не знает вашего проекта: почему здесь дублирование намеренное, почему эта библиотека лежит локально, а не подключается пакетом, почему поле считается при записи, а не при отображении.

Решается файлом с описанием проекта в корне репозитория, который подгружается в каждую сессию. Ключевой момент: туда пишут не структуру проекта, а неочевидные места. Структуру видно по коду. Не видно другого:

Логика деплоя намеренно продублирована в двух файлах:
один для панели администратора, второй для веб-хука.
Шаги обязаны совпадать. Меняешь один - меняй второй.

Такая заметка экономит не минуты, а часы: без неё правка уезжает в один файл из двух, и половина деплоя начинает работать по-старому. Модель об этом не догадается, но прочитает и учтёт.

Что туда писать: намеренные дубли, обходные пути и их причины, договорённости о стиле, места, где очевидное решение неверно. Что не писать: структуру папок, список зависимостей, пересказ кода.

Честный провал: документация тоже протухает

На днях я писал статью про устройство языковых версий и полез в этот самый файл за формулировкой. Там было описано автоопределение языка по заголовку браузера и хранение локали в сессии.

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

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

Скиллы: процедура вместо промпта

Второе, что реально меняет дело, - вынести повторяющиеся операции в именованные процедуры. Не «объясню каждый раз, как я делаю коммиты», а один файл с правилами, который срабатывает по фразе.

Устроен он просто: строка с триггерами, затем правила.

Запустить когда: закоммить, запушь, сделай коммит, отправь на гит.

Правила:
- Сообщение - одна короткая фраза, 2-6 слов, с маленькой буквы.
- Без Conventional Commits: никаких feat:, fix:, chore:.
- Файлы документации в коммит не включать.

Дальше достаточно сказать «запушь». Разница с обычным промптом в том, что правила не размываются к концу длинной сессии и одинаковы во всех проектах.

У меня таких процедур около двух десятков: планирование перед задачей, проверка изменений, отладка на боевом сервере, отчёты, работа с макетами. Отдельный скилл умеет предлагать новые - когда одна и та же операция повторяется третий раз, её пора описать.

Правило, из-за которого скиллы можно отдать

Есть неочевидное ограничение, которое я ввёл не сразу: в процедурах не должно быть ничего, по чему опознаётся проект, компания или заказчик. Ни названий, ни доменов, ни путей вида /var/www/клиент, ни ключей, ни идентификаторов задач в трекере.

Причина практическая. Хорошая процедура рано или поздно понадобится коллеге, и отдавать её должно быть не страшно. Если внутри зашит домен клиента и путь к его серверу - отдать нельзя, придётся вычищать.

Всё машинно-зависимое уезжает в отдельный локальный файл вне репозитория: ключи, адреса, исключения. Процедура читает его сама, а если файла нет - работает с настройками по умолчанию или спрашивает. В примерах - example.com, /var/www/project, user@host.

Доступ к боевому серверу

Самое спорное и самое полезное: доступ к проду по SSH. Не для правок - для того, чтобы не гадать.

Типичный диалог без доступа: «на сайте что-то не работает» - «покажите логи» - «какие логи, где их взять». С доступом это один запрос: прочитать хвост лога, посмотреть статус миграций, проверить, что задача в планировщике действительно запускается.

Здесь есть ловушка с правами, на которую напарываются все. Пользователь, под которым вы заходите по SSH, обычно не тот, от которого работает приложение. Чтение работает от любого:

ssh myserver "cd /var/www/project && php artisan migrate:status"

А всё, что пишет файлы или логи, нужно запускать от владельца - иначе команда упадёт на правах или, хуже, создаст файл, который потом не сможет перезаписать веб-сервер:

ssh myserver "sudo -u app_user php artisan cache:clear"

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

Отдельно полезно уметь проверять планировщик. У меня в кроне обнаружились две записи запуска расписания для одного приложения - одна от панели управления, вторая добавлена мимо неё. Обе срабатывали в одну секунду, то есть каждая задача выполнялась дважды. Нашлось это не в коде, а в системном журнале: два процесса с разными идентификаторами в одну и ту же секунду.

Проверка вместо доверия

Это то, что отличает работу от рулетки. Сгенерированный код не считается готовым, пока не проверен - и проверять должен не человек глазами, а что-то воспроизводимое.

Что работает у меня:

  • Тесты на поведение, а не на покрытие. За последнюю неделю они поймали три настоящих бага: метод валидации не возвращал необязательный ключ и ронял сохранение; подключённый обработчик заставил тесты ходить в реальные внешние сервисы, и прогон вырос с секунды до восемнадцати; ни один человек не заметил бы этого раньше прода.
  • Скриншоты через headless-браузер. Вёрстку нельзя проверить чтением кода. Открыть страницу в трёх ширинах и посмотреть - можно.
  • Измерения вместо впечатлений. «Кажется, блоки разъехались» превращается в замер координат: левый край логотипа, заголовка и подвала должен совпадать. Совпал - вопрос закрыт.

Один пример целиком. Жалоба звучала как «на мобильном горизонтальный скролл». Скрипт обошёл страницу в трёх ширинах, сравнил ширину документа с шириной окна и назвал виновника: декоративный элемент, вынесенный за край секции. Заодно выяснилось, что привычное overflow-x: hidden на body здесь не помогает - по спецификации значение уходит на область просмотра, а сам элемент остаётся видимым. Без замера это были бы полчаса догадок.

Где граница

Чего я не отдаю: решений об архитектуре, выбора между «быстро» и «правильно», всего, что касается денег и персональных данных. Не потому что модель не справится с кодом, а потому что цена ошибки несимметрична, и отвечать за неё мне.

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