Files
progcode/editorial/agent-rewrites/001.json
T

8 lines
20 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 1,
"slug": "editorial-2027-12-field-author-manifesto",
"title": "Эксплуатационная инструкция: от симптома к проверенному откату",
"excerpt": "Как описать опасную операцию так, чтобы оператор видел область сбоя, условия запуска, одно обратимое действие, сигнал остановки и критерий восстановления.",
"contentHtml": "<p>Проблема эксплуатационной инструкции видна в первый сбой: сервис отвечает ошибками, а оператор не понимает, с какого шага начать и как ограничить воздействие. Цена неточного текста — несколько людей одновременно меняют состояние системы, исходные метрики теряются, а откат объявляют успешным только потому, что команда завершилась без ошибки.</p><p>Типичный симптом нужно описать наблюдаемыми признаками: например, «5xx выше 5% на POST /payments в одном регионе», а не «сервис нездоров». Если инструкция не задаёт область, условие запуска и проверку результата, читатель может отключить здоровый трафик, выполнить команду на другой версии или вернуть конфигурацию без восстановления данных.</p><h2>Тезис: инструкция описывает границы действия</h2><p>Хороший runbook не перечисляет команды ради полноты. Он связывает симптом с областью, предварительным условием, одним изменением, обратным действием и наблюдаемым результатом. Каждый шаг отвечает на четыре вопроса: что должно быть истинно до команды, что изменится, какой вывод ожидается и когда нужно остановиться.</p><p>Такой порядок отделяет факт от гипотезы. Сначала оператор сохраняет сигнал и ограничивает scope. Затем меняет один рычаг. После этого ждёт заданное окно и сравнивает метрику с критерием. Если критерий не выполнен или появился новый риск, оператор запускает rollback по заранее описанному условию. Окончание команды не равно восстановлению сервиса.</p><h2>Механизм проверяемой операции</h2><p><strong>Symptom</strong> фиксирует метрику, endpoint и время. <strong>Scope</strong> показывает, кого затронула проблема: регион, релиз, долю трафика или конкретный tenant. <strong>Precondition</strong> подтверждает доступ, версию, наличие backup или сохранённого dashboard. <strong>Action</strong> меняет одно состояние. <strong>Rollback</strong> возвращает его при указанном условии. <strong>Verification</strong> задаёт метрику и окно наблюдения.</p><p>Эти поля защищают от разных ошибок. Без scope оператор расширит локальный сбой до всей системы. Без precondition он применит команду к неправильной версии. Несвязанные команды усложнят причинность: после них нельзя понять, что помогло. Без verification текст заканчивается слишком рано — на синтаксически успешном вызове, а не на подтверждённом результате.</p><p>Модальность тоже должна быть явной. Слова «проверьте», «убедитесь» и «при необходимости» ничего не задают, пока рядом нет объекта и критерия. В терминах RFC 2119 обязательное условие можно пометить как MUST, допустимое исключение — как SHOULD, необязательную диагностику — как MAY. В русской инструкции достаточно написать «обязательно», «рекомендуется» или «можно», если правило остаётся однозначным.</p><p>Перед изменением состояния сохраните наблюдаемый факт. Нужен минимальный набор, по которому можно сравнить до и после: scope, версия, временное окно и исходная метрика. Для опасной команды укажите право доступа, namespace и способ увидеть diff. Не вставляйте секреты, настоящие hostname и локальные alias, которых читатель не сможет проверить.</p><h2>Как выбрать рычаг изменения</h2><p>Я выбираю самый узкий обратимый рычаг, который действительно покрывает scope. Это не универсальное правило: если ошибка уже записала несовместимые данные, одного feature flag недостаточно. Матрица ниже помогает назвать цену выбора до команды, а не после неудачного отката.</p><div class='table-scroll'><table><caption>Рычаг, радиус и цена отката</caption><thead><tr><th scope='col'>Рычаг</th><th scope='col'>Когда подходит</th><th scope='col'>Цена и риск</th><th scope='col'>Что проверить до запуска</th></tr></thead><tbody><tr><td>Feature flag</td><td>Проблема включается отдельным путём и данные совместимы</td><td>Малый blast radius; нужна рабочая ветка выключения и владелец флага</td><td>Scope флага, текущая версия и время распространения</td></tr><tr><td>Версионируемая конфигурация</td><td>Ошибка в параметре, а код менять не нужно</td><td>Откат обычно короткий, но конфигурация может приходить с задержкой</td><td>Diff, порядок публикации и фактическое значение на инстансе</td></tr><tr><td>Предыдущий артефакт</td><td>Причина в коде и быстрый локальный рычаг не покрывает сбой</td><td>Радиус больше и операция дольше; возможна несовместимость с миграцией данных</td><td>Совместимость схемы, артефакт и критерий остановки</td></tr></tbody></table></div><p>Сигнал остановки должен быть отдельной строкой: например, «при росте 5xx в незатронутом scope или при появлении ошибок записи прекратить rollout и вернуть flag». Он не заменяет критерий восстановления. Остановка говорит, когда нельзя продолжать, а verification — когда допустимо завершить операцию.</p><div class='table-scroll'><table><caption>Симптом, причина, проверка и действие</caption><thead><tr><th scope='col'>Симптом</th><th scope='col'>Вероятная причина</th><th scope='col'>Проверка</th><th scope='col'>Действие</th></tr></thead><tbody><tr><td>5xx выше порога на одном endpoint</td><td>Новая версия или флаг затрагивает ограниченный scope</td><td>Сравнить release, регион, долю трафика и error rate за одинаковое окно</td><td>Ограничить воздействие флагом; при ухудшении вернуть его</td></tr><tr><td>Timeout растёт, 5xx не меняется</td><td>Зависимость отвечает медленно или исчерпан ресурс</td><td>Сопоставить p95/p99, trace и лимиты пула с baseline</td><td>Не повторять запросы вслепую; остановить изменение и проверить зависимость</td></tr><tr><td>409 при повторной операции</td><td>Состояние уже изменено или нарушена идемпотентность</td><td>Проверить operation id, запись состояния и время первого вызова</td><td>Не выполнять повторно; выбрать безопасный путь чтения или ручного разбора</td></tr><tr><td>После отката ошибка остаётся</td><td>Откатил один рычаг, но причина вне его scope</td><td>Сравнить метрики до, после action и после rollback</td><td>Остановиться, зафиксировать результат и передать расследование владельцу</td></tr><tr><td>Команда завершилась успешно</td><td>Изменился только control plane, data plane ещё не восстановлен</td><td>Проверить пользовательский сигнал и заданное окно наблюдения</td><td>Не закрывать инцидент до прохождения verification</td></tr></tbody></table></div><figure><img src='/assets/editorial/2027/author-manifesto-2027-revision-handoff-loop.svg' alt='Петля эксплуатационной инструкции: симптом и предварительное условие ведут к одному действию, затем к проверке метрики и условному откату.' loading='lazy' /><figcaption>Операция считается завершённой после наблюдаемой проверки. Если сигнал не достиг критерия, инструкция возвращает оператора к безопасной остановке или откату.</figcaption></figure><h2>Минимальный рабочий пример</h2><p>Ниже — учебная проверка карточки runbook. Функция принимает шесть полей и отклоняет объект без содержательного rollback или наблюдаемой verification. Она не проверяет права, shell, облако и реальную исполнимость команды. Её задача — поймать структурный пробел до публикации инструкции.</p><pre><code>function validateRunbookCard(card) {\n const required = ['symptom', 'scope', 'precondition', 'action', 'rollback', 'verification'];\n if (!card || typeof card !== 'object') return { ok: false, reason: 'runbook-must-be-object' };\n const missing = required.filter((key) =&gt; typeof card[key] !== 'string' || card[key].trim().length &lt; 10);\n if (missing.length) return { ok: false, reason: 'runbook-fields-missing', missing };\n if (!/rollback|откат|вернуть/i.test(card.rollback)) return { ok: false, reason: 'rollback-must-be-explicit' };\n if (!/провер|verify|метрик|threshold/i.test(card.verification)) return { ok: false, reason: 'verification-must-be-observable' };\n return { ok: true, order: required };\n}\n\nconst card = validateRunbookCard({\n symptom: '5xx выше 5 процентов на POST /payments',\n scope: 'region eu-west, release 42, 10 percent traffic',\n precondition: 'есть доступ к flag и сохранён dashboard за 15 минут',\n action: 'отключить flag payments-v2 для 10 процентов трафика',\n rollback: 'вернуть flag payments-v2 после проверки результата',\n verification: 'проверить error rate и p95 в течение 10 минут',\n});\nconsole.log(card.ok, card.order.join(' → '));\n// true symptom → scope → precondition → action → rollback → verification\n\nconst incomplete = { ...card };\ndelete incomplete.rollback;\nconsole.log(validateRunbookCard(incomplete).reason);\n// runbook-fields-missing</code></pre><p>В примере первая строка показывает, что карточка прошла структурную проверку, а вторая — что пропущенное поле обнаруживается до публикации. Этот код не разрешает выполнять операцию: он не знает права, shell, облако, реальную команду и корректность порога. В настоящем runbook команда должна ссылаться на локальный механизм изменения и на конкретный сигнал. Если эти сведения нельзя проверить, их нужно оставить как явно обозначенные placeholders, а не заполнять вымышленными значениями.</p><h2>Порядок редакторской проверки</h2><ol><li>Запишите симптом в первых двух абзацах: метрика, объект, временное окно и цена ошибки. Не начинайте с инструмента или команды.</li><li>Сузьте scope до endpoint, региона, версии, доли трафика или другой проверяемой границы. Слово «все» требует отдельного доказательства.</li><li>Перед действием перечислите precondition: доступ, версия, backup, lock, сохранённые метрики и разрешённый объём воздействия.</li><li>Оставьте одно изменение на шаг. Рядом укажите ожидаемый output и способ увидеть diff, чтобы результат можно было отличить от совпадения.</li><li>Опишите rollback как действие и условие. Формулировка «откатить при проблеме» не говорит, что считать проблемой и когда начинать возврат.</li><li>Задайте verification: метрика, порог, сегмент и окно наблюдения. Сравните результат с тем же scope, который был зафиксирован до изменения.</li><li>Добавьте отдельный сигнал остановки и проверьте соседние ветки для timeout, 409 и ошибки после отката. Один симптом не должен автоматически вести к одному и тому же действию.</li><li>Проверьте учебный пример на принятом и неполном объекте. После этого удалите команды, которые нельзя безопасно воспроизвести.</li></ol><h2>Ограничения</h2><p>Структурная проверка не знает, кому разрешён доступ, как устроены shell и облако, есть ли backup, lock и реальные пороги. Она не выполняет команду и не доказывает, что rollback действительно возвращает исходное состояние. Поэтому runbook нельзя считать разрешением менять production: approval, окно операции и владельца изменения задаёт локальный процесс.</p><p>Архивная NIST SP 800-61 Revision 2 (2012) показывает цикл подготовки, обнаружения и анализа, containment, восстановления и действий после инцидента. NIST пометил Rev. 2 как withdrawn и заменил её Rev. 3 (2025), поэтому этот цикл здесь приведён как историческая модель, а не как текущая нормативная схема. Актуальная Rev. 3 связывает incident response с CSF 2.0, но по-прежнему не знает ваших сервисных зависимостей, команд и порогов остановки. RFC 2119 помогает различить обязательное и рекомендуемое действие, но не тестирует исполнимость конкретной команды.</p><p>Не добавляйте в текст вымышленные замеры или производственные результаты ради гладкого примера. Учебная карточка должна оставаться учебной и не содержать секретов, настоящих адресов и необратимых команд. Если значение неизвестно, лучше назвать, где его проверить, чем подменить неизвестное числом.</p><h2>Критерий готовности</h2><p>Инструкция готова, когда читатель может по первым абзацам определить симптом и scope, до команды проверить precondition, выполнить одно ограниченное действие, увидеть ожидаемый output, запустить rollback по явному условию и подтвердить восстановление метрикой за заданное окно. Если для любого шага нельзя назвать наблюдаемый результат, текст ещё не готов: вернитесь к условию, границе или проверке.</p><h2>Проверяемые источники</h2><ul><li><a href='https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-61r2.pdf' target='_blank' rel='noopener noreferrer'>NIST SP 800-61 Revision 2 — Computer Security Incident Handling Guide</a> — NIST, август 2012 года; архивирован и withdrawn 3 апреля 2025 года. Использован только для датированной исторической схемы этапов; NIST указывает Rev. 3 как superseding publication.</li><li><a href='https://csrc.nist.gov/pubs/sp/800/61/r3/final' target='_blank' rel='noopener noreferrer'>NIST SP 800-61 Rev. 3 — Incident Response Recommendations and Considerations for Cybersecurity Risk Management</a> — NIST, 3 апреля 2025 года. Подтверждает актуальную рамку incident response в CSF 2.0; не задаёт локальные команды, права и критерии rollback.</li><li><a href='https://www.rfc-editor.org/rfc/rfc2119.html' target='_blank' rel='noopener noreferrer'>RFC 2119 — Key words for use in RFCs to Indicate Requirement Levels</a> — IETF, март 1997 года, Best Current Practice. Определяет смысл MUST, SHOULD и MAY; не является эксплуатационным руководством и не проверяет команду.</li></ul>"
}