8 lines
17 KiB
JSON
8 lines
17 KiB
JSON
{
|
||
"index": 108,
|
||
"slug": "editorial-2025-01-practice-ai-coding-assistant",
|
||
"title": "AI-помощник в разработке: как принять только проверяемый diff",
|
||
"excerpt": "Сгенерированный код может выглядеть готовым и всё же менять не тот контракт. Разбираем ограниченный контекст, отрицательные условия, проверку diff и критерий готовности до merge.",
|
||
"contentHtml": "<p>Разработчик просит помощника исправить обработку ключа. В ответ приходит аккуратный diff: имена понятны, форматирование совпадает с проектом, основной тест проходит. Через день выясняется, что invalid input получает default, а соседний helper меняет право доступа. Симптом заметен не в ответе помощника, а на границе системы: функция вернула допустимую по типам, но неверную по смыслу строку. Цена ошибки — повторная проверка всех затронутых путей, задержка merge и риск выпустить изменение, за которое никто не взял явную ответственность.</p>\n<p>Проблема начинается до первого ответа. Запрос без контракта просит правдоподобный текст. Он не говорит, какие файлы разрешено менять, какой результат запрещён, кто принимает расширение области и каким наблюдением подтверждается решение. Поэтому полезный фрагмент легко получает лишние полномочия. Правильная граница выглядит так: помощник предлагает candidate diff, инженер задаёт контракт, reviewer проверяет scope, а тест наблюдает изменённую ветку. Ни один из этих шагов нельзя заменить красивым объяснением.</p>\n<h2>Механизм: от запроса к решению</h2>\n<p>У ограниченной задачи есть пять частей. Сначала формулируют один наблюдаемый результат. Затем называют допустимый контекст: сигнатуру функции, строки контракта и связанные тесты. После этого фиксируют отрицательные условия: не менять authorization, не добавлять default, не трогать публичный формат. Владелец принимает смысл изменения. Наконец, команда называет evidence: список путей, contract cases, focused test и человеческий review.</p>\n<p>Такой порядок разделяет разные вопросы. Scope отвечает на вопрос что изменилось. Контракт отвечает на вопрос какое поведение допустимо. Тест отвечает на вопрос что произошло на выбранном входе. Review отвечает на вопрос кто принимает остаточный риск. Если один зелёный тест используют как ответ на все четыре вопроса, появляется ложная уверенность.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<div class='table-scroll'><table><caption>Четыре типовых сбоя при работе с AI-assisted diff</caption><thead><tr><th scope='col'>Симптом</th><th scope='col'>Причина</th><th scope='col'>Проверка</th><th scope='col'>Действие</th></tr></thead><tbody><tr><td>Полезный hunk сопровождается изменением соседнего файла</td><td>В запросе нет allowed paths и запрета на расширение</td><td>Сверить каждый changed path с карточкой задачи</td><td>Удалить лишний hunk или открыть отдельное решение</td></tr><tr><td>Happy path проходит, invalid input получает новый default</td><td>До первого ответа не записан отрицательный результат</td><td>Сравнить input-output rows для normal, blank и invalid</td><td>Вернуть diff владельцу контракта</td></tr><tr><td>Тест зелёный, но side effect вызывается раньше ошибки</td><td>Тест наблюдает соседнюю ветку</td><td>Проверить вызовы helper на duplicate или forbidden input</td><td>Добавить focused negative case</td></tr><tr><td>Ответ выглядит убедительно, но область риска неизвестна</td><td>Explanation приняли за evidence</td><td>Перечислить неизвестные consumers, права и версии</td><td>Сузить задачу, привлечь owner или остановить merge</td></tr></tbody></table></div>\n<h2>Prompt-card до первого черновика</h2>\n<p>Карточка не улучшает модель сама по себе. Она делает решение читаемым для инженера и reviewer. Для учебного parser достаточно записать: нормализовать один synthetic invoice key; разрешить только функцию parser и таблицу входов и выходов; не менять authorization, public labels и dependencies; назначить владельца контракта; проверить bounded diff и focused cases. Это не настоящий prompt и не доказательство качества модели. Карточка содержит только фиксированные учебные значения.</p>\n<pre><code>const task = {\n result: 'normalize one invoice key',\n allowedContext: ['parseInvoiceKey signature', 'fixed input-output rows'],\n forbidden: ['authorization edits', 'new defaults', 'dependency changes'],\n owner: 'synthetic-parser-owner',\n evidence: ['changed paths', 'invalid-input case', 'human review']\n};\n\n// Candidate output is a proposal, not an approval.\n// Any access-policy change stops the review.</code></pre>\n<p>Важен не размер diff, а его связь с задачей. Правильная правка иногда меняет два файла: реализацию и тест. Такой diff остаётся bounded, если оба пути названы, а новое поведение следует из контракта. Небольшой diff может быть опасным, если одна строка меняет default, разрешение или смысл ошибки. При обнаружении новой области нельзя задним числом включить её в исходную просьбу. Нужно остановиться, назвать новый риск и получить отдельное решение.</p>\n<figure><img src='/assets/editorial/2025/ai-coding-assistant-2025-prompt-diff-review.svg' alt='Учебный цикл: prompt-card переходит в bounded diff, затем отдельно проверяются scope, human review и focused test; выход за scope ведёт к остановке.' loading='lazy' /><figcaption>Рисунок 1. Схема границы между карточкой задачи, candidate diff и проверкой. Подписи и значения synthetic; рисунок не описывает реальный pipeline и не показывает production-результаты.</figcaption></figure>\n<h2>Конкретный пример: правдоподобная ветка с неверным смыслом</h2>\n<p>Предположим, parser различает нормальный ключ, пустое значение и недопустимый маркер. Вход <code>invoice-42</code> даёт <code>invoice-42</code>. Пустой ввод означает отсутствие значения. Маркер <code>?</code> означает ошибку. Помощник предлагает вернуть пустую строку для любого значения, которое не удалось распознать. Happy path остаётся зелёным. Ошибка скрывается в том, что invalid и absent стали одним состоянием.</p>\n<pre><code>const cases = [\n { input: 'invoice-42', expected: 'invoice-42' },\n { input: '', expected: 'absent' },\n { input: '?', expected: 'invalid' }\n];\n\n// Учебный контракт. Он не вызывает реальный parser.\n// Candidate, который сводит '?' к 'absent', отклоняется.</code></pre>\n<p>Проверка должна увидеть не только значение. Если duplicate key запрещает запись, test обязан проверить ноль вызовов write helper. Если изменение касается authorization, нужно проверить deny-ветку и запрет на allow по умолчанию. Код может вернуть правильную ошибку после побочного эффекта. Поэтому expected result и forbidden side effect записывают рядом. Линтер проверяет форму. Unit test наблюдает сценарий. Reviewer связывает сценарий с задачей. Эти evidence не складываются в универсальную гарантию.</p>\n<h2>Порядок проверки до merge</h2>\n<ol><li><strong>Сформулируйте результат.</strong> Запишите один вход, ожидаемый выход и запрещённое побочное действие.</li><li><strong>Ограничьте контекст.</strong> Передайте только нужную сигнатуру, контрактные строки и тестовые случаи. Уберите секреты, персональные данные и ненужную историю.</li><li><strong>Зафиксируйте границу.</strong> Назовите allowed paths, запреты и owner. Новое поведение вне карточки остановите.</li><li><strong>Отделите candidate diff.</strong> Просмотрите список файлов и hunks до чтения объяснения. Ищите лишний путь, новый default, изменение зависимости или прав доступа.</li><li><strong>Сверьте контракт.</strong> Проверьте normal, blank, invalid и duplicate cases. Для каждой ветки назовите результат и отсутствие forbidden side effect.</li><li><strong>Запустите focused checks.</strong> Выполните тесты и статический анализ, которые отвечают именно на изменённый вопрос.</li><li><strong>Проведите человеческий review.</strong> Owner принимает смысл и расширение scope. Reviewer фиксирует comment, request changes или решение принять bounded diff.</li><li><strong>Запишите неизвестное.</strong> Перечислите других consumers, реальную нагрузку, совместимость версий и security context, если их не проверяли.</li></ol>\n<h2>Ограничения применения</h2>\n<p>Этот подход не превращает помощника в источник истины. Ограниченный prompt не знает скрытых consumers, если их не дали в контексте. Тест не доказывает поведение всех комбинаций. Review может пропустить доменную ошибку. Static analysis не заменяет threat model. Чем ближе изменение к authentication, платежам, персональным данным, миграции схемы или внешнему API, тем меньше допустимая область и тем сильнее нужны domain owner, security review и интеграционные проверки. Иногда разумное действие — не применять такой инструмент к чувствительному участку.</p>\n<p>Не следует переносить учебный пример в production как готовую библиотеку. Учебный пример не вызывает модель, не обращается к репозиторию, сети, CI или реальным пользователям и не содержит production-метрик. Имена, ключи и outcomes намеренно зафиксированы. Они показывают только форму проверки: сопоставить вход с контрактом, увидеть отрицательный путь и остановить предложение при выходе за границу.</p>\n<p>Официальная документация GitHub рекомендует проверять функциональность, контекст, зависимости и AI-specific pitfalls, включая выдуманные API, пропущенные ограничения и удалённые тесты. Это последовательность вопросов, но не сертификат корректности. Профиль NIST для secure software development с generative AI помогает встроить практики безопасности в жизненный цикл. Он не знает доменный контракт и не заменяет решение владельца риска.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Изменение готово к решению о merge, если reviewer может показать пять вещей: каждый path связан с задачей; каждый changed branch имеет допустимый и запрещённый результат; focused test наблюдает эту ветку; owner назван и принял остаточный риск; неизвестные перечислены отдельно. Если пункт отсутствует, действие должно быть конкретным: сузить diff, добавить evidence, привлечь владельца или остановить merge.</p>\n<p>Итог работы с помощником — не удачный ответ и не идеальный prompt. Итог — ограниченное изменение, для которого видно, что изменилось, почему это разрешено и как проверяется отказной путь. Так команда получает скорость черновика без передачи модели ответственности за контракт.</p>\n<h2>Проверяемые источники</h2><ul><li><a href='https://docs.github.com/en/copilot/tutorials/review-ai-generated-code' target='_blank' rel='noopener noreferrer'>GitHub Docs: Review AI-generated code</a> — официальная последовательность функциональных проверок, проверки контекста и intent, поиска AI-specific pitfalls и human review.</li><li><a href='https://www.nist.gov/publications/secure-software-development-practices-generative-ai-and-dual-use-foundation-models-ssdf' target='_blank' rel='noopener noreferrer'>NIST: Secure Software Development Practices for Generative AI and Dual-Use Foundation Models</a> — официальный SSDF Community Profile для встраивания практик безопасной разработки AI-систем и foundation-model сценариев в жизненный цикл.</li></ul>"
|
||
}
|