8 lines
18 KiB
JSON
8 lines
18 KiB
JSON
{
|
||
"index": 40,
|
||
"slug": "editorial-2026-11-field-technology-evaluation",
|
||
"title": "Как сравнивать технологии, когда цена ошибки выше цены эксперимента",
|
||
"excerpt": "Сравнение технологий начинается не с рейтинга. Сначала нужно определить симптом, стоимость ошибки, критерии, измерение и границы вывода.",
|
||
"contentHtml": "<p>На встрече появляется таблица из двух технологий и итоговых баллов: 86 против 74. Через неделю никто не может ответить, откуда взялись числа. Не указаны версии, входные данные, число повторов и правило обработки разброса. Симптом простой: вывод выглядит точным, но его нельзя воспроизвести.</p>\n<p>Цена ошибки зависит от решения. Если команда выбирает библиотеку для короткого скрипта, потеря обычно ограничивается временем разработчика. Если выбор затрагивает данные, задержки, безопасность и обслуживание, ошибка переезжает в архитектуру. Она увеличивает стоимость миграции, усложняет диагностику и заставляет защищать решение, основанное на неизвестных предпосылках.</p>\n<h2>Тезис: сравнивать нужно не названия, а условия</h2>\n<p>Технология не бывает лучшей сама по себе. Сравнение имеет смысл только внутри задачи: с известными входами, версией, окружением и ограничением по стоимости ошибки. Рейтинг без этих условий смешивает измеряемый сигнал с предпочтением.</p>\n<p>Сначала отделите четыре вещи. Симптом показывает, что в текущем решении болит. Критерий описывает, что важно для новой альтернативы. Измерение даёт наблюдаемый сигнал. Риск показывает, чем обернётся неверный вывод. Вес критерия выражает приоритет. Он не доказывает свойство инструмента.</p>\n<h2>Механизм: четыре слоя решения</h2>\n<p>Предположим, сервис обрабатывает одинаковые сообщения и выбирает между двумя библиотеками очередей. Первая жалоба — время обработки скачет. Но этот симптом ещё не говорит, что другая библиотека быстрее. Причиной может быть размер сообщения, холодный процесс, блокировка записи, повторная доставка или неверный таймер.</p>\n<p>Поэтому вопрос формулируют уже: «Как меняется время обработки сообщения заданного размера при одинаковом числе потребителей и одинаковой политике подтверждения?» Вопрос задаёт границы. Он не обещает ответа до измерения.</p>\n<p>Критерии должны быть наблюдаемыми или проверяемыми отдельно. Например: p95 времени обработки, доля повторной доставки, сложность миграции, требования к операционному сопровождению. В эти критерии нельзя незаметно включить симпатию к знакомому API. Если удобство важнее задержки, его нужно назвать и объяснить.</p>\n<p>Измерение требует протокола. Зафиксируйте версии, размер и форму входа, число потребителей, длительность прогрева, число повторов и способ вычисления итогового значения. Низкое среднее не компенсирует длинный хвост, если именно хвост ломает пользовательский сценарий.</p>\n<p>Риск связывает наблюдение с последствием. Разница в 2 миллисекунды может быть важной для одного маршрута и бесполезной для другого. Ошибка в оценке миграции может стоить больше, чем разница в скорости. Взвешенная матрица помогает сделать этот обмен видимым, но не делает его объективным автоматически.</p>\n<figure><img src=\"/assets/editorial/2026/technology-evaluation-2026-evidence-handoff-loop.svg\" alt=\"Схема сравнения технологий: проблема переходит в критерии, измерение и проверку ограничений, после чего решение принимают только по наблюдаемым данным\" loading=\"lazy\" /><figcaption>Существующая схема показывает границы сравнения: критерии и условия измерения должны быть видимыми до вывода.</figcaption></figure>\n<h2>Учебный пример: матрица для двух библиотек</h2>\n<p>Ниже приведён учебный пример с условными именами <code>queue-a</code> и <code>queue-b</code>. Он не описывает конкретный продукт и не содержит данных реальной системы. Числа весов нужны только для демонстрации расчёта. Их нельзя выдавать за измеренный результат.</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>p95 времени обработки</td><td>35</td><td>Миллисекунды на одинаковом входе</td><td>Значение для реальной нагрузки</td></tr><tr><td>Повторная доставка</td><td>25</td><td>Доля сообщений с повтором</td><td>Причины отказов за пределами теста</td></tr><tr><td>Стоимость миграции</td><td>25</td><td>Список изменений и трудоёмкость шагов</td><td>Фактическое время команды</td></tr><tr><td>Сопровождение</td><td>15</td><td>Количество обязательных компонентов</td><td>Долгосрочная нагрузка на операторов</td></tr></tbody></table></div>\n<p>Сумма весов равна 100. Это проверка полноты, а не доказательство правильности шкалы. Для каждой строки задайте шкалу от 0 до 3 и опишите смысл каждого балла. Нельзя ставить ноль только потому, что данных ещё нет. Отсутствие наблюдения — это <code>unknown</code>, а не плохое значение.</p>\n<h2>Пример кода с явной границей</h2>\n<pre><code>const criteria = [\n { id: 'p95-latency', weight: 35, scoreA: 0, scoreB: 0 },\n { id: 'redelivery', weight: 25, scoreA: 0, scoreB: 0 },\n { id: 'migration-cost', weight: 25, scoreA: 0, scoreB: 0 },\n { id: 'operations', weight: 15, scoreA: 0, scoreB: 0 }\n];\n\nfunction weightedScore(items, side) {\n const weightTotal = items.reduce((sum, item) => sum + item.weight, 0);\n if (weightTotal !== 100) return { status: 'stop-invalid-weights' };\n\n const hasUnknown = items.some((item) => item[side] === 'unknown');\n if (hasUnknown) return { status: 'stop-missing-observation' };\n\n const score = items.reduce(\n (sum, item) => sum + item.weight * item[side] / 3,\n 0\n );\n return { status: 'score-available', score };\n}\n\nconsole.log(weightedScore(criteria, 'scoreA'));\n// { status: 'score-available', score: 0 }</code></pre>\n<p>Этот код показывает арифметику матрицы. Нулевые баллы здесь означают начальное состояние примера, а не качество <code>queue-a</code>. До подстановки наблюдений функция не выбирает библиотеку. Если один критерий получает <code>unknown</code>, она останавливается. Это важнее красивого итогового числа: неизвестность не должна маскироваться под результат.</p>\n<p>Для измерения времени используйте отдельный инструмент и одинаковые условия. Документация Python <code>timeit</code> прямо отделяет измерение небольших фрагментов от общего профилирования. Даже корректное измерение фрагмента не описывает всю систему. Оно отвечает только на вопрос, который вы ему задали.</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, p99, ошибки и повторы на одном наборе входов</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> и остановить итоговую оценку</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>Выберите критерии, задайте шкалы и веса; проверьте, что каждый вес имеет объяснение, а сумма равна 100.</li><li>Зафиксируйте версии, окружение, входные данные, прогрев, повторы и формулу расчёта.</li><li>Проведите одинаковые измерения для каждой альтернативы и сохраните исходные значения, а не только средний балл.</li><li>Отдельно проверьте хвост распределения, ошибки, повторную доставку и стоимость изменений.</li><li>Отметьте неизвестные поля как <code>unknown</code>. Не делайте вывод, пока критичный критерий не получил наблюдение.</li><li>Сравните результат с порогом решения и укажите, какие условия ограничивают перенос вывода.</li></ol>\n<h2>Ограничения и отрицательный путь</h2>\n<p>Матрица не заменяет нагрузочное испытание, анализ безопасности и разговор с владельцем данных. Она не предсказывает поведение другой версии, другого размера входа или другого окружения. Если библиотека показывает лучший p95 на маленьком сообщении, это не доказывает преимущество на больших сообщениях.</p>\n<p>Не всякий вопрос стоит превращать в число. Совместимость лицензий, доступность специалистов и возможность отката могут быть жёсткими ограничениями. Если альтернатива нарушает такое ограничение, её нельзя «спасти» высоким баллом скорости. Сначала применяют стоп-условие, потом сравнивают оставшиеся варианты.</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 — Measure execution time of small code snippets</a> — официальная документация инструмента для измерения небольших фрагментов. Она не превращает локальное измерение в характеристику всей системы.</li></ul>"
|
||
}
|