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

8 lines
26 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": 40,
"slug": "editorial-2026-11-field-technology-evaluation",
"title": "Как сравнивать технологии, когда цена ошибки выше цены эксперимента",
"excerpt": "Сравнение технологий начинается не с рейтинга. Сначала нужно определить симптом, стоимость ошибки, критерии, измерение и границы вывода.",
"contentHtml": "<p>На встрече появляется таблица из двух технологий и итоговых баллов: 86 против 74. Через неделю никто не может ответить, откуда взялись числа. Не указаны версии, входные данные, число повторов и правило обработки разброса. Симптом понятен: вывод выглядит точным, но его нельзя воспроизвести.</p>\n<p>Цена ошибки зависит от того, куда попадёт решение. Неудачный выбор библиотеки для короткого скрипта отнимет время разработчика. Выбор очереди, базы данных или платформы затронет данные, задержки, безопасность, обучение и откат. Поэтому эксперимент должен быть дешевле ошибки, а его границы — уже, чем соблазнительный заголовок нового инструмента.</p>\n<h2>Сначала зафиксируйте границу выбора</h2>\n<p>Сравнивайте не названия технологий, а два способа решить одну задачу в одинаковых условиях. Формула вопроса проста: «Какой вариант лучше подходит для <em>конкретного сценария</em> при <em>заданных входах, нагрузке и ограничениях</em>?» В таком вопросе есть объект, условия и критерий. Вопрос «что быстрее и современнее?» не задаёт ни одного из них.</p>\n<p>Запишите границу до запуска теста. Укажите, что входит в систему: приложение, сеть, хранилище, очередь, операторские процедуры. Укажите, что не входит: например, аварийное восстановление или миграция исторических данных. Иначе в одном сравнении окажутся скорость компонента и стоимость всей замены.</p>\n<p>Полезно сразу сформулировать решение, которое должно последовать за наблюдением: выбрать вариант A для пилота, оставить вариант B, собрать ещё данные или остановить сравнение. Четвёртый исход важен. Отсутствие достаточных данных — это результат исследования, а не проигрыш команды.</p>\n<h2>Разделите симптом, риск и измерение</h2>\n<p>Симптом — наблюдаемая неприятность: растёт время ответа, сообщения обрабатываются повторно, релиз трудно откатить. Причина пока неизвестна. Если назвать причиной сам инструмент, эксперимент уже содержит желаемый ответ.</p>\n<p>Риск описывает последствие ошибочного выбора. Для очереди это может быть потеря сообщения, повторная доставка или рост операционной нагрузки. Для базы данных — долгий простой при миграции. Для сборочного инструмента — невозможность воспроизвести артефакт. Риск нельзя заменить одним числом производительности: быстрая система с неприемлемым сценарием восстановления не становится подходящей.</p>\n<p>Измерение отвечает на узкий вопрос и оставляет след: исходные входы, версию, окружение, команду запуска и результат каждого повтора. Оценка — это правило, по которому наблюдения превращаются в решение. Вес критерия выражает приоритет команды, но не доказывает свойство технологии.</p>\n<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>Симптом</td><td>Что именно болит сейчас?</td><td>Запрос, метрика, лог или воспроизводимое наблюдение</td><td>Если симптом описан только словами</td></tr><tr><td>Критерий</td><td>Что должно измениться?</td><td>Метрика, шкала и порог приемки</td><td>Если критерий нельзя проверить отдельно</td></tr><tr><td>Измерение</td><td>В каких условиях сравниваем?</td><td>Версии, входы, ресурсы, повторы и сырые значения</td><td>Если условия для вариантов различаются</td></tr><tr><td>Решение</td><td>Что делаем с результатом?</td><td>Правило выбора, стоп-условие и план отката</td><td>Если неизвестное выдали за нулевой балл</td></tr></tbody></table></div>\n<p>Таблица нужна не для декоративного рейтинга. Она показывает, какой пробел мешает перейти от жалобы к действию. Если команда не может заполнить одну строку доказательством, это повод сузить вопрос.</p>\n<h2>Соберите воспроизводимый протокол</h2>\n<p>Сравнение становится воспроизводимым, когда другой инженер может повторить его без устного объяснения. Зафиксируйте версию приложения и каждой альтернативы, операционную систему, лимиты CPU и памяти, размер и форму входных данных, число потребителей, сетевой режим, длительность прогрева и число повторов. Сохраните команду запуска и исходный вывод, а не только красивое среднее.</p>\n<p>Меняйте за один раз только то, что вы сравниваете. Если для одного варианта включён кэш, а для другого нет, результат отвечает на вопрос о двух конфигурациях, а не о технологиях. Если один тест прогрет, а другой стартует с холодного процесса, сначала измеряйте этот эффект отдельно.</p>\n<p>Для времени обработки смотрите распределение, а не только среднее. p95 означает границу, ниже которой оказалось 95 процентов наблюдений; он показывает хвост задержек лучше среднего, когда редкие длинные операции портят пользовательский сценарий. Но p95 не объясняет причину задержки. Его нужно сопоставить с ошибками, повторами, размером входа и ресурсами.</p>\n<figure><img src='/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg' alt='Схема сравнения технологий: от симптома через критерий и измерение к решению, которое останавливается при неизвестном условии' loading='lazy' /><figcaption>Сравнение движется к решению только после проверки условий; неизвестный вход возвращает задачу на уточнение протокола.</figcaption></figure>\n<p>Схема полезна как контрольная точка перед объявлением победителя. В ней нет отдельного шага «поверить презентации». Заявление поставщика, демонстрация на конференции и локальный тест — разные виды свидетельств. Они могут сформулировать гипотезу, но не заменяют измерение вашего сценария.</p>\n<h2>Учебный пример: две очереди в одном сценарии</h2>\n<p>Представим сервис, который принимает одинаковые сообщения, обрабатывает их и подтверждает результат. Команда сравнивает <code>queue-a</code> и <code>queue-b</code>. Симптом: после роста размера сообщения увеличился хвост задержек. Гипотеза: одна из очередей хуже ведёт себя при заданном размере сообщения. Альтернативная гипотеза: задержку создаёт запись результата, а очередь лишь получает вину вместе с ней.</p>\n<p>Чтобы отделить гипотезы, задайте один входной набор, одинаковый размер сообщения, одинаковое число потребителей и одинаковое правило подтверждения. Запишите три серии: холодный старт, прогретый процесс и повтор после нагрузки. Отдельно сохраните ошибки и повторные доставки. Если тест исключает ошибки, он отвечает только на вопрос о времени успешной обработки.</p>\n<div class='table-scroll'><table><caption>Рабочая матрица до подстановки измерений</caption><thead><tr><th scope='col'>Критерий</th><th scope='col'>Вес</th><th scope='col'>Шкала 0–3</th><th scope='col'>Наблюдение</th><th scope='col'>Ограничение</th></tr></thead><tbody><tr><td>p95 обработки</td><td>35</td><td>3 — ниже порога, 0 — выше критического</td><td>Сырые повторы и p95</td><td>Порог действует только для этой нагрузки</td></tr><tr><td>Повторная доставка</td><td>25</td><td>3 — в пределах лимита, 0 — за лимитом</td><td>Доля повторов и причины</td><td>Тест не описывает все виды отказа</td></tr><tr><td>Миграция</td><td>25</td><td>3 — обратимый малый объём, 0 — сложный откат</td><td>Карта изменений и пробный откат</td><td>Оценка зависит от команды и данных</td></tr><tr><td>Сопровождение</td><td>15</td><td>3 — минимум обязательных операций</td><td>Права, метрики и аварийные процедуры</td><td>Нужно подтвердить владельца каждого сигнала</td></tr></tbody></table></div>\n<p>Весы 35, 25, 25 и 15 складываются в 100. Это проверяет арифметическую полноту, но не делает приоритеты объективными. Команда должна объяснить, почему повторная доставка важнее или менее важна, чем время. Если нарушение лимита по повторам недопустимо, его нельзя компенсировать высокой скоростью. Такой критерий становится жёстким стоп-условием.</p>\n<p>Пока измерений нет, в колонке «Наблюдение» должно быть <code>unknown</code>. Ноль означает худшее значение по согласованной шкале. Неизвестность означает, что шкала ещё не применена. Подмена одного другим создаёт ложную точность и может выбрать вариант, который никто не проверял.</p>\n<h2>Код, который не рисует победителя</h2>\n<pre><code>const criteria = [\n { id: 'latency-p95', weight: 35, scoreA: 3, scoreB: 'unknown' },\n { id: 'redelivery', weight: 25, scoreA: 2, scoreB: 1 },\n { id: 'migration', weight: 25, scoreA: 'unknown', scoreB: 2 },\n { id: 'operations', weight: 15, scoreA: 2, scoreB: 3 }\n];\n\nfunction decide(items, side) {\n const weightTotal = items.reduce((sum, item) =&gt; sum + item.weight, 0);\n if (weightTotal !== 100) return { status: 'stop-invalid-weights' };\n if (items.some((item) =&gt; item[side] === 'unknown')) {\n return { status: 'stop-missing-observation' };\n }\n\n const score = items.reduce(\n (sum, item) =&gt; sum + item.weight * item[side] / 3,\n 0\n );\n return { status: 'score-available', score };\n}\n\nconsole.log(decide(criteria, 'scoreA'));\n// { status: 'stop-missing-observation' }</code></pre>\n<p>Пример намеренно останавливает расчёт для <code>scoreB</code> и для <code>scoreA</code>: у каждой стороны есть неизвестное значение. Это не проверка качества очереди и не benchmark. Это маленький предохранитель против незаполненной таблицы. В рабочем коде к нему добавляют проверку, что балл лежит в диапазоне от 0 до 3, что идентификаторы критериев уникальны и что решение не публикуется при стоп-статусе.</p>\n<p>Пример также показывает границу ответственности. Функция проверяет структуру оценки, но не знает, как измерялись миллисекунды и что означал повтор. Эти факты должны храниться рядом с результатом: в отчёте запуска, артефакте CI или журнале эксперимента. Арифметика не исправляет плохой протокол.</p>\n<h2>Как читать результаты без самообмана</h2>\n<p>Сначала сравните каждую альтернативу с порогом, а затем — альтернативы между собой. Если обе проходят порог, разница в баллах помогает выбрать следующий шаг. Если обе не проходят, ищите третью конфигурацию или меняйте границу задачи. Если одна быстрее, но нарушает обязательный лимит повторов, она не становится победителем из-за среднего балла.</p>\n<p>Проверяйте устойчивость вывода. Повторите измерение на нескольких размерах сообщения, при разных уровнях конкуренции и после восстановления процесса. Спросите, меняется ли порядок вариантов при разумном изменении веса. Если небольшое изменение веса переворачивает выбор, решение чувствительно к предпочтениям и требует явного согласования, а не уверенного заголовка.</p>\n<p>Стоимость миграции тоже должна иметь доказательство. Разложите её на изменение схемы, перенос данных, переключение трафика, наблюдаемость, обучение и откат. Запись «дёшево» не сравнима с «дорого». Даже диапазон с допущениями полезнее одного числа без происхождения: например, «два–четыре инженерных дня при готовом адаптере; неизвестно, если потребуется перенос исторических данных».</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<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>Есть итоговый победитель, но нет сырых чисел</td><td>Предпочтение выдали за наблюдение</td><td>Найти входы, версии, повторы и команду запуска</td><td>Снять вывод и вернуть <code>unknown</code></td></tr><tr><td>Среднее улучшилось, а ошибки выросли</td><td>Измеряли только один показатель</td><td>Сопоставить p95, ошибки, повторы и пропускную способность</td><td>Добавить критерий надёжности или применить стоп-условие</td></tr><tr><td>Результат меняется каждый запуск</td><td>Не зафиксированы прогрев, ресурсы или фон</td><td>Сверить окружение и повторить серии в одинаковом порядке</td><td>Назвать разброс и не объявлять устойчивый вывод</td></tr><tr><td>Неизвестное заменили нулём</td><td>Пропуск смешали с худшей оценкой</td><td>Проверить происхождение каждого балла</td><td>Разделить <code>unknown</code> и 0 в модели данных</td></tr><tr><td>Миграция выглядит дешёвой в одной строке</td><td>Не учли данные, откат и обучение</td><td>Составить карту зависимостей и обратного пути</td><td>Считать диапазон с явными допущениями</td></tr></tbody></table></div>\n<h2>Порядок проверки</h2>\n<ol><li>Опишите симптом наблюдаемым языком: что изменилось, где это видно и кому мешает.</li><li>Назовите цену ошибки: задержка, потеря данных, простой, трудоёмкость отката или будущая операционная нагрузка.</li><li>Сформулируйте один вопрос с двумя сравниваемыми альтернативами и границей сценария.</li><li>Отделите обязательные ограничения от критериев, которые можно взвешивать.</li><li>Задайте шкалы, веса и пороги; проверьте, что сумма весов равна 100.</li><li>Зафиксируйте версии, окружение, входы, прогрев, число повторов, команду запуска и формат сырых результатов.</li><li>Проведите одинаковые серии для каждой альтернативы и сохраните ошибки, повторы и хвост задержек.</li><li>Пометьте отсутствие наблюдения как <code>unknown</code>; не подставляйте ноль и не считайте итог.</li><li>Проверьте устойчивость при изменении нагрузки и разумном изменении весов.</li><li>Запишите решение, допущения, владельца следующей проверки и обратный путь.</li></ol>\n<h2>Ограничения применимости</h2>\n<p>Матрица не заменяет нагрузочное, отказоустойчивое и security-тестирование. Она не доказывает поведение другой версии, другого размера входа, другого региона или другой политики подтверждения. Хороший результат на стенде не переносится на продакшен автоматически. Сначала докажите совпадение условий, затем расширяйте вывод.</p>\n<p>p95 полезен для хвоста задержек, но не показывает потерю данных, корректность результата или стоимость сопровождения. Микробенчмарк полезен для маленького фрагмента, но не описывает сеть, блокировки и работу всей системы. Взвешенный балл делает компромисс видимым, но не превращает мнение о весах в факт.</p>\n<p>У сравнения есть отрицательный путь. Если входы не описаны, версии различаются, повторов недостаточно, часть ошибок исключена или откат не проверен, корректное решение — остановиться. Сузьте утверждение до того, что действительно измерено, или сначала доберите недостающие наблюдения. Иногда лучший результат эксперимента — доказать, что менять технологию пока рано.</p>\n<h2>Критерий готовности решения</h2>\n<p>Решение готово к инженерному обсуждению, когда другой специалист без устных пояснений находит симптом, цену ошибки, входы, версии, протокол, сырые результаты, веса, ограничения и правило остановки. Для каждого числа известен источник. Для каждого неизвестного поля стоит <code>unknown</code>. Вывод не выходит за границы проверенного сценария.</p>\n<p>Перед выбором задайте пять вопросов: что измеряли; на каких входах; сколько было повторов; какие показатели могли ухудшиться; что произойдёт при ошибочном выборе. Если ответа нет, не прячьте пробел в итоговом рейтинге. Назовите следующую безопасную проверку и владельца действия.</p>\n<h2>Проверяемые источники</h2><ul><li><a href='https://csrc.nist.gov/pubs/sp/800/30/r1/final' target='_blank' rel='noopener noreferrer'>NIST SP 800-30 Rev. 1: Guide for Conducting Risk Assessments</a> — официальный материал NIST о выявлении и оценке рисков, включая связь угроз, уязвимостей, вероятности и последствий. Здесь он служит каркасом для разговора о цене ошибки, а не доказательством превосходства конкретной технологии.</li><li><a href='https://docs.python.org/3/library/timeit.html' target='_blank' rel='noopener noreferrer'>Python documentation: timeit</a> — официальная документация инструмента для измерения небольших фрагментов кода; описывает повторные запуски и отделяет подготовку от измеряемого участка. Локальный замер по этой документации всё равно нельзя выдавать за характеристику всей системы.</li></ul>"
}