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

8 lines
21 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": 121,
"slug": "editorial-2024-08-field-feature-flags",
"title": "Feature flag после rollout: как отделить rollback от cleanup",
"excerpt": "Выключенный feature flag прекращает выдачу нового поведения, но не удаляет ветки, конфигурацию и несовместимые данные. Разбираем безопасный rollout, проверяемый rollback и отдельный cleanup gate.",
"contentHtml": "<p>Новый экран работает у сотрудников, но часть клиентов внезапно видит его после релиза. Команда ставит feature flag в <code>false</code>. Ошибка исчезает, график успокаивается, change закрывают. Через месяц в коде всё ещё живут две ветки, в конфигурации лежит старый ключ, а тесты проверяют два ответа. Любая случайная смена окружения может вернуть candidate-путь. Цена ошибки — повторный инцидент, долгий поиск владельца и риск для данных, которые новый путь уже успел записать.</p>\n<p>Обратный случай не безопаснее. Rollout достиг 100 процентов, и это объявили завершением. Но 100 процентов означает только текущий результат правила для объявленной аудитории. Это не доказывает совместимость данных, исправность recovery path и право удалить fallback. Feature flag — не одно переключение, а временный контракт: кто получает вариант, кто его вычисляет, что считается остановкой и когда старый путь перестаёт быть нужен.</p>\n<p><strong>Тезис.</strong> Rollout, rollback и cleanup нужно проводить разными изменениями. Rollout расширяет аудиторию. Rollback останавливает candidate и возвращает fallback. Cleanup удаляет лишнюю ветку после выбора итогового поведения. Если смешать эти операции, команда теряет причинную связь и принимает отключение за исправление.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<table><thead><tr><th>Симптом</th><th>Причина</th><th>Проверка</th><th>Действие</th></tr></thead><tbody><tr><td>Флаг выключен, но в коде остались две ветки</td><td>Rollback смешали с удалением реализации</td><td>Найти evaluator, branches, config references и тесты по ключу</td><td>Оставить fallback исполняемым, открыть отдельный cleanup change</td></tr><tr><td>100% rollout считают доказательством готовности</td><td>Процент подменяет проверку контракта и данных</td><td>Сверить cohort key, effective config version, stop condition и recovery path</td><td>Заморозить процент и провести решение о final variant</td></tr><tr><td>После возврата к fallback часть запросов получает ошибку</td><td>Candidate записал состояние, которое fallback не читает</td><td>Проверить read/write compatibility и миграционный контракт</td><td>Остановить расширение; исправить совместимость отдельно от flag value</td></tr><tr><td>После удаления ключ снова появляется в UI</td><td>Остался client check, stale cache или старый payload</td><td>Проверить server response, client bundle, cache key и config</td><td>Удалить presentation toggle и оставить только итоговый UI contract</td></tr><tr><td>Никто не может назвать дату удаления</td><td>У флага нет owner, final variant и cleanup gate</td><td>Запросить карточку решения с evidence и списком references</td><td>Не расширять rollout; назначить владельца и новый change</td></tr></tbody></table>\n<h2>Как работает решение</h2>\n<p>Сначала evaluator получает ключ флага и контекст. Контекст должен однозначно описывать subject, окружение и другие поля, которые участвуют в targeting. Для процентного rollout особенно важен стабильный cohort key. Если сегодня используется user id, а завтра случайный request id, один пользователь будет переходить между вариантами. Это уже не gradual rollout, а непредсказуемое распределение.</p>\n<p>Evaluator возвращает выбранный вариант и, если система это поддерживает, детали оценки: key, value, variant, reason и версию конфигурации. Приложение применяет результат в одной точке. Второй слой не должен пересчитывать то же правило с другим контекстом. Иначе сервер отдаст новый ответ, а клиент решит, что пользователь относится к fallback-группе.</p>\n<p>Процент не описывает весь жизненный цикл. Он не говорит, обновился ли client cache, дошёл ли запрос до нового обработчика и можно ли откатить данные. Для каждого этапа нужны вопрос наблюдения и заранее выбранное действие. При остановке меняют одну существенную переменную: аудиторию, правило или код. Одновременная смена процента, evaluator и схемы данных уничтожает полезность сравнения.</p>\n<pre><code>type FlagDecision = {\n key: string;\n variant: 'fallback' | 'candidate';\n cohortKey: string;\n configVersion: string;\n};\n\nfunction renderCheckout(decision: FlagDecision, order: Order) {\n if (decision.variant === 'candidate') {\n return renderNewCheckout(order);\n }\n\n return renderLegacyCheckout(order);\n}</code></pre>\n<p>Этот фрагмент — учебный. Он показывает границу между результатом оценки и применением варианта. В нём нет настоящего flag provider, хранилища, телеметрии, миграции или команды rollback. В рабочей системе нужно дополнительно определить формат контекста, источник версии конфигурации, правила кеширования и поведение при ошибке evaluator.</p>\n<h2>Команды для проверки ревизии</h2>\n<p>Если candidate доставляется отдельным Kubernetes Deployment, сначала проверьте историю и состояние rollout. Эти команды требуют настроенного кластера, namespace и права чтения. Значение <code>7</code> — пример ревизии: его нужно заменить результатом первой команды.</p>\n<pre><code>kubectl rollout history deployment/checkout -n shop\nkubectl rollout status deployment/checkout -n shop --timeout=60s\n\n# Изменение состояния: выполняйте только по утверждённому stop condition\nkubectl rollout undo deployment/checkout -n shop --to-revision=7\nkubectl rollout status deployment/checkout -n shop --timeout=60s</code></pre>\n<p><code>rollout undo</code> меняет состояние Deployment, поэтому это операционная команда, а не безопасная проверка «на всякий случай». Kubernetes откатывает Pod template к выбранной ревизии; он не возвращает записи в базе, события и внешние побочные эффекты. Если команда относится только к конфигурации feature flag, применяйте аналогичный runbook вашего провайдера и сохраняйте effective version.</p>\n<h2>Учебная лестница rollout</h2>\n<table><thead><tr><th>Этап</th><th>Вопрос</th><th>Стоп-условие</th><th>Действие</th></tr></thead><tbody><tr><td>0%</td><td>Fallback выполняет согласованный smoke scenario?</td><td>Старый путь уже не работает</td><td>Не включать candidate; исправить базовый контракт</td></tr><tr><td>5%</td><td>Одна стабильная cohort получает candidate в объявленной версии?</td><td>Сигнал нарушает заранее записанную границу</td><td>Вернуть аудиторию к 0%, сохранить конфигурацию и вопрос расследования</td></tr><tr><td>25%</td><td>Candidate можно сопоставить с fallback в одном окне?</td><td>Версия или источник сигнала неизвестны</td><td>Остановить расширение и не менять правило вместе с кодом</td></tr><tr><td>100%</td><td>Вся объявленная аудитория получает выбранный вариант?</td><td>Final variant не утверждён или fallback нужен для recovery</td><td>Не удалять флаг; открыть решение о завершении</td></tr><tr><td>Cleanup gate</td><td>Код и конфигурация больше не требуют alternate branch?</td><td>Осталась runtime reference или зависимость данных</td><td>Вернуть cleanup в доработку</td></tr></tbody></table>\n<p>Значения 0, 5, 25 и 100 процентов в таблице — фиксированный учебный пример. Они не являются рекомендацией, нормативом или результатом измерения. Реальный шаг выбирают по размеру риска, качеству сигнала, обратимости и размеру cohort. Если команда не может назвать population, control, окно и владельца решения, процент не даёт полезной информации.</p>\n<figure><img src=\"/assets/editorial/2024/feature-flags-2024-cleanup-gate.svg\" alt=\"Rollout переходит к rollback и cleanup gate только после отдельного решения о финальном варианте\"><figcaption>Rollout расширяет аудиторию, rollback возвращает fallback, cleanup удаляет alternate branch. Проценты на схеме относятся к учебной модели.</figcaption></figure>\n<h2>Rollback возвращает поведение, но не переписывает историю</h2>\n<p>При stop condition минимальное действие — прекратить дальнейшее расширение candidate. Для этого возвращают аудиторию к fallback или к заранее определённому safe value. Записывают effective config version и причину остановки. Не нужно в тот же момент удалять ветки, менять evaluator и переписывать миграцию. Иначе нельзя будет понять, что именно остановило симптом.</p>\n<p>Rollback значения флага не равен rollback данных. Если candidate уже создал записи, отправил события или изменил схему ответа, старый путь должен уметь прочитать это состояние. Фича-флаг не добавляет backward compatibility автоматически. Если fallback не понимает результат candidate, ответом должен стать отдельный migration contract, а не уверенность, что <code>false</code> решает всё.</p>\n<p>Также не стоит называть rollback мгновенным. Сервис, edge-кеш и браузер могут получить конфигурацию в разное время. В карточке изменения укажите, где вычисляется вариант, сколько живёт кеш и что происходит с активной сессией. Разница версий допустима только тогда, когда она входит в контракт и не ломает recovery path.</p>\n<h2>Cleanup — отдельное решение</h2>\n<p>Cleanup начинается после выбора единственного поведения. Сначала владелец фиксирует final variant. Затем команда перестаёт добавлять новые условия и открывает отдельное изменение удаления. В его области должны быть server branches, client checks, configuration, permissions, tests, документация и миграционные заметки.</p>\n<p>Проверка cleanup строится на отрицательных утверждениях. Runtime больше не вызывает evaluator для ключа. Клиент не переключает UI по старому payload. Конфигурация не содержит targeting rules и environment overrides, нужные только флагу. Тесты проверяют итоговый business contract, а не сохранение двух boolean-веток. Документ объясняет принятое поведение, но не предлагает включить исчезнувший путь.</p>\n<p>Глобальный поиск по имени ключа полезен, но недостаточен. Ключ может быть собран из частей, сохранён в типе, скрыт в конфиге или пришит к кешу. Ищите вызовы evaluator, schema fields, token scopes, generated client types и тестовые данные. Если reference нужна для обратимой миграции, cleanup ещё не завершён: у неё должен быть owner и срок следующей проверки.</p>\n<h2>Порядок действий</h2>\n<ol><li><strong>Опишите контракт.</strong> Назовите owner, evaluator, cohort key, fallback, config version и данные, которые меняет candidate.</li><li><strong>Проверьте fallback.</strong> Выполните smoke scenario и убедитесь, что старый путь доступен до включения нового.</li><li><strong>Запишите stop condition.</strong> Свяжите её с конкретным сигналом, окном, источником и действием на остановке.</li><li><strong>Запускайте один этап.</strong> Не меняйте одновременно аудиторию, правило, код и схему данных.</li><li><strong>При проблеме остановите расширение.</strong> Верните cohort к fallback, сохраните effective config version и отделите mitigation от поиска причины.</li><li><strong>Проверьте совместимость данных.</strong> Убедитесь, что fallback читает состояние, которое оставил candidate, либо оформите отдельную миграцию.</li><li><strong>После выбора final variant заморозьте flag policy.</strong> Не добавляйте новые условия и не продлевайте флаг без новой причины.</li><li><strong>Проведите cleanup search.</strong> Проверьте server, client, config, кеши, права, тесты и документацию.</li><li><strong>Удалите артефакты отдельным change.</strong> Сохраните тест итогового поведения и обычный rollback plan для самого cleanup-релиза.</li><li><strong>Закройте решение.</strong> Зафиксируйте final variant, owner, причину удаления и evidence отсутствия runtime-зависимости.</li></ol>\n<h2>Ограничения</h2>\n<p>Этот подход не выбирает конкретный SDK, размер cohort, TTL кеша, SLO или схему миграции. OpenFeature даёт общий API для evaluation context и результата, но не задаёт правила вашей платформы; при ошибке базовой оценки клиент возвращает переданное default value. Kubernetes rollback относится к версии Deployment и его Pod template; он не доказывает обратимость бизнес-данных. Система флагов также не доказывает качество эксперимента: для этого нужны отдельные метрики, контрольная группа и методика измерения.</p>\n<p>Не следует удалять fallback только потому, что candidate получил 100 процентов. Не следует сохранять flag навсегда только потому, что когда-то существовал инцидент. Если данные, кеш или долгоживущий consumer не описаны, это блокер cleanup. Если evaluator недоступен, нужен явный default и проверяемая политика ошибки. Если неизвестно, какой вариант является итоговым, сначала принимают это решение, а потом удаляют код.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Cleanup готов, если независимая проверка отвечает «да» на все вопросы: final variant записан владельцем; fallback больше не нужен для recovery или миграции; runtime не зависит от ключа; client и server не пересчитывают старое правило; configuration и permissions не содержат флаговые остатки; тесты сохраняют итоговое поведение; обычный релизный rollback не требует возвращать удалённый flag.</p>\n<p>До этого момента выключенный флаг нужно считать остановленным, но не удалённым. Такое различие сохраняет причину, границу действия и следующий шаг. Оно также не даёт спокойному графику скрыть технический долг, который снова станет пользовательской ошибкой.</p>\n<h2>Проверяемые источники</h2>\n<p><a href=\"https://openfeature.dev/docs/reference/concepts/evaluation-context/\" target=\"_blank\" rel=\"noopener\">OpenFeature: Evaluation Context</a> — официальный материал о контексте оценки, targeting key и рисках передачи персональных данных.</p>\n<p><a href=\"https://openfeature.dev/docs/reference/concepts/evaluation-api/\" target=\"_blank\" rel=\"noopener\">OpenFeature: Evaluation API</a> — официальное описание результата оценки и его деталей.</p>\n<p><a href=\"https://kubernetes.io/docs/tasks/run-application/update-deployment-rolling/\" target=\"_blank\" rel=\"noopener\">Kubernetes: Update a Deployment Without Downtime</a> — официальная документация о проверке rollout, возврате Deployment к ревизии и ограничении rollback только Pod template.</p>"
}