
Сейчас во многих инженерных командах наблюдается одна и та же картина.
Разработчик открывает ИИ-помощника по созданию кода, вставляет пользовательскую историю (user story) и пишет:«Build this» («Сделай это»). ИИ выполняет задание. Код компилируется. Тесты проходят успешно. А где-то глубоко в коде находится решение, которое разработчик никогда не принимал — значение по умолчанию, выбранное ИИ, поведение, которое никто не задавал, упрощение, которое имело смысл в изоляции, но через шесть недель привело к инциденту в продакшен-среде.
Разработчик винит ИИ. Но ИИ сделал именно то, о чем его попросили.
Вот в чем настоящая проблема.
Вы пишете не промпты. Вы пишете контракты.
Когда вы просите инженера-человека реализовать функциональность, он берется за дело, опираясь на свой многолетний опыт. Ему уже не раз приходилось выпускать неработающие системы. Он поймет, что команда “delete the record” (“удалить запись”), скорее всего, ошибочна. Он будет задавать уточняющие вопросы, отмечать крайние случаи на ежедневных собраниях и сопротивляться, когда что-то вызывает у него подозрения.
У ИИ нет всей этой организационной памяти. У него есть только тот текст, который вы ему дали.
Поэтому, написав промпт: «allow users to archive their account» («позволь пользователям архивировать свои аккаунты«, вы предоставляете ИИ право решать, что означает «архивировать». Удалить с возможностью восстановления (soft-delete)? Удалить без возможности восстановления (hard-delete)? Приостановить биллинг? Отозвать доступ? Сохранить данные? В данной ситуации допустима каждая из этих интерпретаций. ИИ выбирает статистически наиболее правдоподобную на основе своих обучающих данных — и продолжает работу.
Вы не узнаете, какую интерпретацию он выбрал, пока не прочитаете код. Или пока данные пользователя не исчезнут.
Проблема не в интеллекте. Проблема в интерфейсе. Вы высказываете ИИ пожелание, тогда как ему нужен контракт.
Налог на неоднозначность
Каждый недостаточно конкретизированный входной сигнал для ИИ несет в себе то, что я называю «налогом на неоднозначность» (“плата за двусмысленность”).
ИИ решает эту неоднозначность без объяснений, выбирая значения по умолчанию. И каждое значение по умолчанию — это решение, которое вы не принимали, — поведение, которое вы не проверяли, не тестировали и сознательно не одобряли.
В небольшом скрипте с этим можно справиться. В продакшн-системе неоднозначность усугубляется.
Рассмотрим такой промпт:«Build a subscription cancellation flow» (“Создай процесс отмены подписки»).
ИИ, ориентированный на повествование, интерпретирует это так: установить статус подписки cancelled (отменена), перенаправить пользователя на страницу подтверждения. Готово. Разумно. Скорей всего, неверно.
А как же пропорциональный возврат средств? А платежный цикл — отмена вступает в силу немедленно или в конце периода? А пользователи с годовым тарифом, отменяющие подписку на втором месяце? А журнал аудита, требуемый вашей платежной системой? А повторная активация — сохраняется ли тот же ID подписки?
Ничего из этого не было в промпте. Поэтому ИИ пришлось догадываться. И он догадывался последовательно, уверенно и совершенно незаметно для вас.
Вы наследуете каждое из этих предположений в тот момент, когда вводите команду git commit.
Проблема в формате
Формат пользовательской истории — “As a [user], I want [action] so that [outcome]” (“Как [пользователь], я хочу [действие], чтобы [результат]” — не был разработан для машин. Он был разработан для формирования чувства эмпатии в команде разработчиков. Чтобы придать процессу разработки ПО “человеческий характер”.
Именно этот уровень эмпатии и делает его интерфейсом, неподходящим для ИИ.
Пользовательские истории — сжатая информация. Они опираются на общий контекст, профессиональную интуицию и возможность задавать уточняющие вопросы на совещании по планированию спринта. Ввести пользовательскую историю в ИИ — все равно что дать ему заголовок и попросить воссоздать статью.
На самом деле ИИ нужна не сжатая информация, а наоборот — ее расширение: четкие границы системы, заявленные ограничения, объявленные взаимосвязи и определенные режимы сбоев — и все это должно быть изложено простым языком до начала реализации.
Не псевдокод. Не схема. Просто: вот условия, в которых существует эта функция, вот правила, которые ею управляют, и вот как выглядит правильный результат.
От нарратива к детерминизму
Этот переход проще, чем кажется. Вам не нужно учить новый язык или осваивать новый набор инструментов. Вы меняете то, что описываете.
Вместо того чтобы описывать, чего хочет пользователь, описывайте систему, с которой он взаимодействует.
Это описание охватывает четыре основных аспекта:
- Что — сущности, их состояния и то, как они связаны между собой: “A subscription can be active, paused, or cancelled. A cancelled subscription retains its data for 90 days before permanent deletion» («Подписка может быть активной, приостановленной или отмененной. Отмененная подписка сохраняет свои данные в течение 90 дней перед окончательным удалением»).
- Кто — участники и их права доступа: “Account owners can cancel their subscription. Team members cannot. Billing admins can view but not modify subscription status” (“Владельцы учетных записей могут отменить подписку. Члены команды не могут этого сделать. Администраторы по биллингу могут просматривать статус подписки, но не могут его изменять”).
- Почему — бизнес-правила, определяющие поведение: «A subscription cannot be cancelled if there is an outstanding invoice. Users must clear their balance first.» («Подписку нельзя отменить, если есть неоплаченный счет. Пользователи должны сначала погасить задолженность»).
- Как — технические ограничения, определяющие реализацию:“This runs on a Node.js backend with Stripe for billing. All subscription state changes must go through Stripe webhooks, not direct database writes» («Система работает на бэкенде Node.js с использованием Stripe для биллинга. Все изменения статуса подписки должны проходить через веб-хуки Stripe, а не посредством прямой записи в базу данных”).
Теперь передайте это ИИ.
Результат полностью меняется — не потому, что ИИ стал умнее, а потому, что вы устранили неоднозначность, которую он самостоятельно разрешал. Ему не нужно гадать, что означает «архивировать». Вы ему это сказали. Ему не нужно делать выводы о том, следует ли сохранять данные. Вы это заявили. Ему не нужно решать, как биллинг взаимодействует с отменой. Вы это указали.
Это уже не автодополнение ввода. Это мощный инструмент управления
Побочный эффект: вы сами начинаете лучше мыслить
Вот о чем никто не говорит, когда обсуждают разработку с помощью ИИ: процесс написания спецификации, ориентированной на систему, заставляет вас принимать решения, которые вы ранее откладывали.
Большинство недостаточно конкретизированных промптов — результат не лени, а поспешности. Разработчик еще сам не определил, каким должно быть правильное поведение, а уже передает эту неоднозначность ИИ, надеясь на лучшее.
Составление промпта, охватывающего разные аспекта «что/кто/почему/как», заставляет вас четко сформулировать эти решения простым языком еще до того, как появится хоть строчка кода. Вы не сможете написать «a subscription cannot be cancelled if there is an outstanding invoice” (“подписку нельзя отменить, если есть неоплаченный счет”», не определив сначала: действительно ли это правильное правило? что произойдет, если счет будет оспорен? применимо ли это к бесплатным пробным версиям?
Процесс написания спецификации выявляет решения, которые в противном случае были бы приняты по умолчанию — либо вами, намеренно, либо ИИ, незаметно для вас.
Намеренное решение почти всегда лучше.
Как это выглядит на практике
Вам не нужен 20-страничный документ. Для большинства функций достаточно одного структурированного блока текста, написанного простым языком.
Вот простой формат, который работает:
Функция: Отмена подписки
Что:
Подписка имеет три возможных состояния: активная, отмененная и просроченная. Отмена является окончательной — отмененную подписку нельзя восстановить; вместо этого необходимо создать новую подписку. Данные подписки хранятся в течение 90 дней после отмены, затем удаляются.
Кто:
Владельцы учетных записей могут инициировать отмену. Члены команды не могут. Администраторы биллинга могут просматривать статус отмены, но не могут ее отменить.
Почему:
Подписку нельзя отменить, пока есть неоплаченный счет. Отмена должна немедленно отправить веб-хук платежному провайдеру. Все изменения статуса должны записываться в журнал аудита с отметкой времени и идентификатором пользователя, инициировавшего действие.
Как:
Бэкенд на Node.js, Stripe для биллинга. Состояние подписки хранится в Stripe — база данных его отражает, а не наоборот.
Вот и все. Четыре абзаца. Каждое важное решение изложено явно. Каждое ограничение сформулировано. У ИИ не осталось никаких неоднозначностей, которые ему пришлось бы разрешать за вас.
Передайте это вашему умному помощнику по программированию вместе с задачей на реализацию и сравните результат с тем, что вы получите, опираясь только на пользовательскую историю. Разница будет очевидной.
Глубинный сдвиг
Мы находимся в середине переходного периода, который большинство инженерных команд еще не до конца осознали.
ИИ превращается из инструмента автодополнения в соавтора. А соавтору нужен контекст, а не просто команды. Ему нужно понимать систему, а не только задачу. Он должен знать правила, прежде чем писать код.
Формат пользовательской истории хорошо служил нам десять лет для общения человека с человеком. Но это плохой интерфейс для разработки типа «человек-ИИ». Не потому, что ИИ хуже человека, а потому, что ИИ другой. Он не знает вашего контекста. Он не участвует в ваших ежедневных собраниях. Он не знает, что вы имели в виду. Он знает только то, что вы написали.
Пишите четче — получите качественней код.
Навык, который будет отличать продвинутых инженеров эпохи ИИ от остальных, — это не промпт-инжиниринг в смысле умения хитро формулировать промпты, чтобы обмануть модель. Это умение мыслить спецификациями — способность четко описать все аспекты системы, явно сформулировать ее правила и устранить двусмысленности, прежде чем они превратятся в ошибки.
Этот навык не нов. Просто сейчас он важнее, чем когда-либо.
Читайте также:
- 4 метода оптимизации LLM-промптов по стоимости, времени отклика и производительности
- Имеет ли код значение?
- Неужели комментировать код — это плохо?
Читайте нас в Telegram, VK и Дзен
Перевод статьи Vishal Mysore: Spec-Driven Development: Why Your AI Writes Bad Code Without It





