{ "index": 94, "slug": "editorial-2025-05-field-engineering-automation", "title": "Как ограничить batch-автоматизацию до безопасного изменения", "excerpt": "Надёжный маршрут для автоматической операции: сначала получить точный preview, затем проверить scope и согласование, выполнить change, независимо проверить результат и остановиться перед опасным rollback.", "contentHtml": "
Скрипт выбирает записи по фильтру и обновляет их за секунды. Затем владелец видит лишние изменения: шаблон совпал с архивными объектами, список targets устарел, а часть полей уже исправил другой процесс. Ошибка не заканчивается неудачным exit code. Она оставляет частичный change, теряет исходные значения и заставляет команду запускать ещё одну операцию для исправления первой. Цена ошибки — простой, ручная сверка и риск испортить данные при поспешном rollback.
\nБезопасная batch-автоматизация строится как цепочка независимых границ: preview, ограниченный scope, проверка полномочий, approval, execute, audit trail и независимая verification. Ни один этап не должен выдавать результат следующего этапа. Preview не равен разрешению. Успешное завершение процесса не доказывает состояние данных. Rollback не должен автоматически наследовать полномочия исходной операции.
\nДо запуска опишите operation card — короткую карточку изменения. Она отвечает на вопрос: что именно процесс собирается изменить и как владелец узнает, что изменение завершилось правильно. В карточке нужны стабильный operation id, selector, полный список targets, исключения, digest списка, ожидаемое состояние до и после, версия логики и критерий проверки.
\nСписок targets должен быть плотным: без пропущенных элементов, неявного «всё найденное» и повторного поиска между preview и execute. Digest не заменяет список для чтения. Он связывает карточку, согласование и запуск. Если selector, список и digest невозможно показать вместе, reviewer не видит границу операции.
\n| Поле | Пример учебного значения | Проверка | Остановиться, если |
|---|---|---|---|
| operationId | normalize-labels-2025-05-01 | одно значение проходит через preview, approval и audit | идентификатор переиспользован или отсутствует |
| targets | doc-a, doc-b | список плотный, видны selector и exclusions | список пуст, разрежен или не соответствует фильтру |
| targetDigest | sha256:7b… | digest вычислен по каноническому списку | digest относится к другому набору |
| authority | label-editor, максимум 2 записи | лимит покрывает точный scope | операция шире разрешённого лимита |
| expectation | label=normalized после запуска | есть источник, который это прочитает | успехом считается только exit code |
Preview показывает proposed change без внешней записи. Это полезная граница для чтения, но не гарантия будущего результата. Между расчётом и запуском другой процесс может изменить target. Запись может исчезнуть. Политика может истечь. Поэтому preview получает время расчёта, версию входных данных и максимальный срок действия.
\nКороткий пример ниже учебный. Он не обращается к файлам, сети или реальным targets. Его задача — показать отрицательный путь: authority разрешает одну запись, а preview содержит две. В таком случае код не уменьшает список молча и не запускает разрешённую часть.
\nconst preview = {\n operationId: 'normalize-labels-2025-05-01',\n targets: ['doc-a', 'doc-b'],\n targetDigest: 'sha256:7b-demo',\n expiresAt: '2025-05-01T12:00:00Z'\n};\n\nconst authority = {\n selector: 'document-label',\n maxTargets: 1\n};\n\nfunction approve(preview, authority, now) {\n if (new Date(preview.expiresAt) <= now) {\n return { approved: false, reason: 'preview-expired' };\n }\n if (preview.targets.length > authority.maxTargets) {\n return { approved: false, reason: 'scope-exceeds-authority-limit' };\n }\n return { approved: true };\n}\n\nconsole.log(approve(preview, authority, new Date('2025-05-01T11:00:00Z')));\n// Учебный результат: approved=false, scope-exceeds-authority-limit.\nПроверка должна происходить до approval и тем более до write. Нельзя превращать ограничение в предупреждение. Предупреждение оставляет решение в голове оператора и делает два одинаковых запуска разными по поведению. Явный stop code даёт владельцу причину, которую можно проверить и обработать.
\nApproval должен относиться к конкретной карточке, а не к названию задачи. Минимальная связка содержит operation id, preview digest, target digest, authority id, решение, identity reviewer и срок действия. Executor сверяет значения буквально. Если reviewer согласовал список doc-a и doc-b, а перед запуском список стал doc-a и doc-c, старое согласование недействительно.
\nЭто правило закрывает распространённый отрицательный путь. Сервис обновил inventory, получил новый список и продолжил старым approval. В логах есть «approved», но approval относился к другому scope. Правильное действие — остановиться, построить новый preview и запросить новое решение. Автоматически угадывать намерение reviewer нельзя.
\nExecute сообщает, что процесс попытался применить change. Audit trail сохраняет связанный контекст: operation id, digest входа, версию исполнителя, время, результат по каждому target и correlation id. Audit не доказывает, что данные приняли новое состояние. Для этого нужен отдельный reader, который обращается к authoritative source после execute.
\nVerification сравнивает наблюдаемое состояние с явным expectation. У неё должно быть не два, а три результата: matched, mismatched и unknown. Недоступный reader даёт unknown. Таймаут не превращает unknown в успех. Если один target совпал, а второй нет, операция остаётся остановленной с указанием subset и владельца восстановления.
\n| Симптом | Причина | Проверка | Действие |
|---|---|---|---|
| В preview слишком много записей | Selector шире ожидаемого | Сравнить selector, exclusions и лимит authority | Остановиться и сузить scope; не обрезать список автоматически |
| Approval есть, но digest не совпал | Inventory изменился после review | Сверить operation id и два target digest | Построить новый preview и получить новое approval |
| Executor завершился успешно, данных нет | Exit code приняли за наблюдаемое состояние | Прочитать authoritative source отдельным reader | Отметить unknown и назначить владельца verification |
| Изменена только часть targets | Partial execution или внешний конфликт | Сохранить точный subset и результаты по объектам | Остановиться; не запускать широкий inverse |
| Preview старше допустимого окна | Stale input или истёкшая политика | Сравнить retrieval time с max age | Пересчитать preview перед новым approval |
После mismatched verification естественно вызвать обратную команду в блоке finally. Это опасно. Часть объектов могла измениться вручную. Старое значение могло стать неверным. Новый selector может выбрать больше записей. Поэтому rollback сначала создаёт план восстановления: наблюдаемый subset, известные и неизвестные состояния, владельца recovery и данные для следующего решения.
\nДальше rollback проходит тот же маршрут: новый scope, новый authority check, новый preview, новое approval и новая verification. Исходное согласование не даёт права на обратную запись. Если команда пока не может перечислить targets для rollback, результатом должен быть stop и расследование, а не команда «вернуть всё назад».
\nЭтот маршрут не делает опасную операцию безопасной сам по себе. Он не доказывает идемпотентность кода, неизменяемость журнала, корректность identity provider, отсутствие гонок и возможность восстановления данных. Внешний сервис может вернуть неполный список, а reader — устаревшее состояние. Эти свойства нужно проверять в архитектуре конкретной системы.
\nУчебный код использует фиксированные строки и память процесса. Он не является реализацией permission system, durable audit storage или реального batch runner. В production нужно определить источник targets, каноническое вычисление digest, правила истечения preview, владельца остановки и допустимость partial execution. Если хотя бы один из этих пунктов неизвестен, scope автоматизации следует уменьшить.
\nОперация готова к ограниченному запуску, если команда может воспроизвести три случая на тестовых данных: корректный bounded scope проходит весь маршрут; scope больше authority останавливается до approval; изменившийся target digest останавливает запуск после повторной проверки. Для каждого случая видны operation id, причина остановки или matched verification, запись audit и отсутствие запрещённой записи. Критерий не обещает результат для production. Он показывает, что границы процесса работают.
\n