Чем лучше AI становится в программировании, тем страннее выглядит одна старая привычка. Мы всё ещё очень часто оцениваем его по качеству кода. Красивый ли diff. Хорошая ли архитектура. Есть ли тесты. Проходит ли static analysis. Не появилось ли лишнего дублирования.
Всё это важно. Но постепенно я начал замечать, что самые неприятные ошибки всё чаще находятся уровнем выше. Код может быть хорошим. Тесты зелёными. Review пройден. А feature всё равно неправильная.
Потому что существует две очень разные вещи:
Code correctness
и
Task correctness
И AI может прекрасно справиться с первой, полностью провалив вторую.
Написать правильный код для неправильной задачи довольно легко
Представим обычное требование: после неуспешной оплаты нужно повторить попытку автоматически. AI исследует проект. Находит payment service. Находит очередь. Создаёт retry mechanism. Добавляет backoff. Пишет тесты. Проверяет idempotency. Запускает static analysis. Всё выглядит очень хорошо. На review тоже ничего критичного.
Только потом кто-то из продукта говорит: подождите. Мы вообще не должны автоматически повторять эту категорию платежей. И внезапно весь хороший код становится неправильным. Не потому, что реализация плохая. Она может быть отличной. Просто мы реализовали не то поведение.
Это неприятный класс ошибок
Когда код технически сломан, обычно есть сигналы. Тест падает. Exception. Ошибка типов. Приложение не запускается. Что-то видно. А здесь всё выглядит убедительно:
✓ clean implementation
✓ tests passed
✓ static analysis passed
✓ review passed
✓ browser scenario works
Но исходная задача понята неверно. И все эти проверки только подтверждают, что неправильная идея реализована последовательно.
Я начал разделять два вопроса
Первый: работает ли решение так, как мы его реализовали? Это code correctness. Второй: должна ли система вообще работать именно так? Это task correctness.
Разница кажется очевидной. Но в реальной разработке они очень легко смешиваются. Особенно когда один и тот же агент читает задачу, интерпретирует её, строит plan, пишет code, пишет tests и делает self-review. Если ошибка произошла на первом или втором шаге, дальше можно получить идеальное подтверждение неправильного решения.
Чем сильнее implementation, тем опаснее неправильная постановка
Это немного парадоксально. Слабый AI плохо понял задачу и написал плохой код. Это видно. Ты почти сразу вмешиваешься. Сильный AI может плохо понять задачу и написать очень хороший код. Вот это гораздо интереснее.
Потому что плохое исходное предположение оказывается скрыто качеством реализации. Код выглядит настолько правдоподобно, что перестаёшь задавать вопрос: а почему вообще мы это делаем?
Раньше часть проверки происходила естественно во время написания
Когда я сам реализую feature, между requirement и готовым кодом проходит довольно много времени. Я читаю. Думаю. Пишу. Возвращаюсь к задаче. Нахожу странность. Задаю вопрос. Иногда прямо посередине реализации понимаю: стоп. Требование, похоже, означает не это.
Сам процесс программирования создаёт трение. А трение иногда полезно. AI способен убрать его почти полностью. Пять минут назад была формулировка. Теперь уже есть половина feature. И можно очень быстро проскочить момент, где стоило остановиться и уточнить смысл.
Поэтому скорость реализации меня всё меньше впечатляет сама по себе
Если агент пишет feature за двадцать минут вместо моих трёх часов — прекрасно. Но только если эти двадцать минут направлены в правильную сторону. Иначе мы просто быстрее приехали не туда.
быстро
+
правильно
=
полезно
быстро
+
не туда
=
дорогой откат
AI очень сильно улучшает первый множитель. А значит, второй становится ещё важнее.
Хорошая формулировка задачи теперь имеет гораздо больший вес
Раньше плохое требование тоже было проблемой. Конечно. Но разработчик часто компенсировал его по ходу работы. Приходил к аналитику. Спрашивал коллегу. Смотрел существующее поведение. Замечал противоречие. С AI возникает соблазн отдать текст буквально: вот ticket. Реализуй.
И если ticket написан не очень хорошо, агент начинает достраивать недостающее самостоятельно. А модели очень хорошо умеют делать разумные предположения. В этом и проблема. Разумное предположение не обязательно совпадает с бизнесом.
Самая опасная фраза — «логично предположить»
Например: логично предположить, что администратор может выполнить операцию независимо от состояния. Нет. Может быть, не может. Логично предположить, что cancelled заказ освобождает reservation. Тоже не обязательно. Логично предположить, что после успешной оплаты заказ считается завершённым. В конкретной системе это может быть вообще не так.
AI довольно хорошо заполняет пустоты. Но пустота в requirement — это не приглашение придумать бизнес-правило.
Я стал внимательнее относиться к словам, которые раньше считал очевидными
Например: пользователь может повторить операцию. Какой пользователь? Когда? Сколько раз? После какого состояния? А если первая попытка завершилась частично?
Система должна уведомить клиента. Как? Когда? Один раз? Что делать, если канал недоступен? Заказ считается завершённым. Что означает «завершённым» в этой системе?
Технически можно реализовать всё это очень быстро. Но сначала нужно сделать значение слов явным.
Acceptance criteria стали важнее красивого prompt
Мне всё меньше хочется писать длинные роли и инструкции вроде «ты senior software engineer...». И всё больше хочется иметь несколько точных утверждений.
Ручной retry разрешён только для failed операций.
Retry запрещён после перехода связанной сущности в final state.
Повторный успешный вызов должен быть idempotent.
Existing automatic retry не меняется.
Все ручные попытки пишутся в audit log.
Вот это уже хорошая основа. Не потому, что prompt красивый. А потому, что границы правильного результата становятся проверяемыми. Это хорошо стыкуется с тем, почему я всё меньше держу инженерию в промпте.
Самый полезный вопрос теперь звучит: «как мы поймём, что сделали именно нужную вещь?»
Первый вопрос — как проверить, что код работает. Но важнее другой: как проверить, что поведение соответствует задаче?
Это может означать acceptance criteria, examples, user scenario, бизнес-правила, существующее поведение, product decision, contract с другой системой. Без этого проверка легко замыкается на реализации:
code
↓
test code
↓
review code
А источник истины должен находиться выше. Про тесты, которые подтверждают предположение, а не требование, я писал отдельно.
Я стал чаще просить AI пересказать задачу своими словами
Это очень простой checkpoint. До plan. До implementation. Примерно: объясни, какое поведение нужно получить и какие ограничения ты видишь. Пока ничего не меняй.
Если ответ звучит не так, как я понимаю задачу, это отличный момент остановиться. Потому что исправить один абзац понимания дешевле, чем потом исправлять тысячу строк. Это та же логика, что и в остановке агента, когда растёт только diff.
Иногда полезно спросить, чего агент не знает
Например: какие предположения ты вынужден сделать, потому что их нельзя подтвердить из задачи или проекта? Мне нравится этот вопрос. Потому что он вытаскивает скрытую неопределённость наружу.
Вместо уверенного «я реализую X» может появиться: неясно, разрешена ли операция после перехода в состояние Y. Вот это уже хороший повод подключить человека.
Хороший агент должен не только решать, но и замечать недоопределённость
Мне кажется, это вообще одна из важнейших характеристик зрелого workflow. Не максимальная автономность. А способность сказать: здесь недостаточно информации для безопасного решения.
ambiguous requirement
↓
stop
↓
question
↓
clarification
↓
implementation
Вместо:
ambiguous requirement
↓
reasonable assumption
↓
implementation
↓
tests
↓
✓
Второй путь быстрее. Первый надёжнее.
Это немного меняет роль разработчика
Если раньше мой главный вклад был в том, чтобы превратить requirement в код, теперь всё чаще он находится раньше. Нужно понять: что именно требуется? Какой компромисс допустим? Какое поведение является правильным? Какие ограничения нельзя вывести из repository?
После того как эта часть решена хорошо, implementation может оказаться сравнительно простой. И её уже можно делегировать.
Самое ценное знание всё чаще находится не в syntax
AI знает PHP. Знает Symfony. Знает Laravel. Знает patterns. Может разобраться в API. Может написать migration. Может построить хороший test suite. Но он не знает автоматически, почему этому клиенту нельзя видеть этот статус. Почему бизнес согласился на технический долг именно здесь. Почему этот процесс нельзя автоматизировать полностью. Почему старая интеграция должна продолжить работать ещё шесть месяцев.
Вот эта информация начинает становиться главным ограничителем качества.
Можно идеально выполнить specification и всё равно сделать не то
Это ещё один неприятный уровень. Допустим, requirement формально написан правильно. AI реализовал его идеально. Но сам requirement плохой. Например: добавить возможность менеджеру вручную менять любой статус заказа.
Реализация может быть безупречной. Но продуктово это может быть ужасная идея. Здесь уже нельзя обвинить AI в неправильном понимании. Он сделал ровно то, что попросили. И именно поэтому финальная ответственность всё равно остаётся выше implementation.
«Что делать» важнее «как сделать»
Чем сильнее coding agents становятся в вопросе «как», тем сильнее ценность смещается к вопросу «что». Не как добавить новый статус, а нужен ли он вообще. Не как реализовать retry, а в каких сценариях он имеет смысл. Не как сделать новый dashboard, а какое решение пользователь должен принимать с его помощью.
AI может помогать отвечать и на эти вопросы. Но они требуют другого качества контекста. И другого уровня ответственности.
Здесь сходятся почти все предыдущие эксперименты
Контекст важен, потому что без него AI неправильно понимает систему. Project instructions важны, потому что фиксируют правила. Research важен, потому что позволяет не начинать с ложного предположения. Independent review важен, потому что второй агент может поставить под сомнение решение первого. Тесты важны, но только если они проверяют правильное поведение. Human checkpoints важны там, где нужно принять решение, а не просто реализовать его.
То есть всё это в итоге упирается в один вопрос: насколько хорошо мы определили, что вообще считается правильным результатом?
Я начал различать несколько уровней «готово»
Раньше было довольно просто: код написан + тесты зелёные = готово. Теперь у меня скорее так:
код работает?
↓
да
тесты проходят?
↓
да
архитектура допустима?
↓
да
исходное поведение сохранено?
↓
да
требование реализовано?
↓
да
а требование вообще правильное?
↓
вот здесь иногда начинается настоящий разговор
И последний вопрос нельзя автоматически закрыть CI.
Возможно, AI делает product thinking важнее, а не слабее
Есть популярная идея: AI заберёт реализацию, значит разработчику останется меньше работы. Я пока вижу другое. Если implementation становится дешевле, цена ошибки в выборе того, что реализовывать, становится относительно выше.
Раньше плохая идея могла умереть по дороге, потому что её дорого было реализовать. Теперь можно очень быстро построить убедительный prototype неправильной feature. То есть скорость разработки требует лучшего product judgment, а не меньшего.
Это особенно заметно в больших legacy-системах
Потому что там задача почти никогда не является просто текстом ticket. Есть ticket плюс история системы, неявные ограничения, текущие клиенты, внешние интеграции, старые компромиссы, планы на будущее.
AI видит часть этого. Иногда большую. Но редко всю. Поэтому фраза «реализуй задачу» на самом деле может означать: восстанови весь этот контекст и только потом выбери правильное поведение. Это уже совсем не локальная задача по написанию кода.
Я всё больше хочу отделять техническое acceptance от смыслового
Technical acceptance:
- tests green
- static analysis green
- no API regression
- migration reversible
Task acceptance:
- пользователь может сделать X
- только в состоянии Y
- результат Z
- сценарий W не должен измениться
Первое хорошо автоматизируется. Второе требует хорошего описания поведения. А иногда ещё и человека, который реально знает продукт. Мне кажется, это разделение будет становиться всё важнее.
AI может написать идеальную реализацию неправильного компромисса
Допустим, нужно выбрать быстрое локальное решение или более дорогую архитектурную переработку. AI может подробно сравнить оба. И даже объективно сказать: второе чище архитектурно. Но сейчас может быть важнее первое. Потому что feature нужна через два дня. Или наоборот. Быстрое решение создаст риск, который бизнес не готов принимать.
В коде этого не видно. Task correctness включает не только соответствие literal requirement. Она включает соответствие реальной цели. Это та граница, которую я пока не готов полностью отдавать.
Поэтому моя постановка задачи постепенно меняется
Раньше: добавь X. Потом: добавь X вот таким способом. Теперь мне больше нравится:
Проблема:
...
Желаемое поведение:
...
Ограничения:
...
Что не должно измениться:
...
Как поймём, что задача решена:
...
И только потом: сначала исследуй проект и скажи, правильно ли ты понимаешь задачу. Это уже не prompt engineering в старом смысле. Это скорее работа над контрактом результата.
Я всё меньше хочу объяснять AI реализацию
Если я пишу: создай service A, добавь method B, вызови его из controller C... — я беру на себя большую часть архитектурного решения. Иногда это правильно. Но если хочу использовать AI как реального участника разработки, полезнее объяснить, что система должна делать. И дать агенту пространство исследовать, как это лучше встроить.
Но тогда ответственность за качество описания «что» становится выше. Это своеобразный обмен. Меньше указаний по implementation. Больше точности в outcome.
Чем автономнее агент, тем более опасна плохая цель
Если агент меняет один метод, последствия ограничены. Если он способен самостоятельно пройти research, plan, implementation, tests, review и verification — то одна неправильная постановка может пройти через весь pipeline. Причём каждый следующий этап будет усиливать уверенность.
Поэтому human checkpoint всё чаще нужен не в конце — посмотреть код. А в начале: убедиться, что решаем правильную задачу. Это, наверное, один из самых сильных сдвигов для меня.
Может быть, будущий workflow начнётся не с coding agent
А с отдельной стадии:
TASK
↓
task clarification
↓
acceptance criteria
↓
assumption check
↓
research
↓
plan
↓
implementation
Причём первые три этапа могут вообще не касаться кода. Это немного забавно. Чем лучше AI пишет код, тем больше времени перед кодом становится ценным.
Я всё ещё чаще ловлю неправильный код, чем неправильную задачу
Наверное, потому что код видно. Diff перед глазами. Условие можно проверить. SQL можно запустить. Неправильное понимание задачи гораздо менее заметно. Оно выглядит разумно. Особенно если сформулировано хорошей моделью.
Поэтому мне самому ещё нужно учиться review не только implementation, но и intent.
Иногда лучший code review — это вопрос к ticket
Например, вместо «здесь потенциальная race condition» самым важным замечанием может оказаться: а почему мы считаем, что это действие вообще должно быть доступно пользователю?
И тогда разговор уходит из PHP. В продукт. В бизнес. В workflow пользователя. И это не отвлечение от разработки. Это, возможно, самая важная её часть.
Поэтому фраза «AI хорошо пишет код» всё меньше кажется мне достаточной
Да. Пишет. Иногда очень хорошо. И будет писать ещё лучше. Но если смотреть только на code quality, можно пропустить более важное изменение.
Bottleneck постепенно перемещается: из «как реализовать?» в «что именно реализовать?», из «код корректен?» в «задача понята правильно?», из «все тесты зелёные?» в «они проверяют правильное поведение?»
И вот здесь роль человека пока становится не менее важной. Просто немного другой.
Возможно, самая дорогая ошибка будущего — идеально реализованная ненужная feature
Не syntax error. Не missing import. Не failing test. А система, которая отлично делает то, чего пользователь вообще не хотел. И чем дешевле становится implementation, тем чаще мы можем позволить себе такую ошибку.
Это хороший повод немного замедлиться именно там, где AI сильнее всего ускоряет.
Сейчас мой процесс всё чаще начинается с двух вопросов
Первый: что именно должна сделать система? Второй: как мы узнаем, что это действительно правильный результат? И только потом: как это реализовать?
Потому что третий вопрос AI решает всё лучше. А первые два от этого не становятся менее важными. Скорее наоборот.
Code correctness и task correctness — не конкуренты
Нужны оба. Плохой код правильной feature — проблема. Хороший код неправильной feature — тоже. Просто второй случай раньше встречался реже в таком убедительном виде.
Теперь AI способен очень быстро произвести технически качественный результат. И поэтому мне приходится внимательнее смотреть на то, какой именно результат я попросил произвести.
Code correctness
=
мы правильно реализовали решение?
Task correctness
=
мы вообще решаем правильную задачу?
И чем больше первую часть я могу делегировать агентам, тем больше моей ответственности остаётся во второй. Наверное, это и есть одна из самых важных вещей, которые я понял за последние месяцы.
AI может очень хорошо написать код. Но он всё равно может сделать не ту вещь. А значит, следующая большая задача для меня — не научиться ещё лучше просить его программировать. А научиться гораздо точнее отвечать на вопрос: что именно мы хотим построить и почему?