Системный и бизнес-анализ Нефункциональные требования: производительность, надёжность, безопасность, как их измерять
0%

Нефункциональные требования: производительность, надёжность, безопасность, как их измерять

Нефункциональные требования: производительность, надёжность, безопасность, как их измерять

Функциональные требования ломают спринт. Нефункциональные ломают архитектуру.

Разница в цене ошибки. Если вы забыли поле в форме, это два дня работы. Если вы забыли, что отчёт должен собираться по 50 миллионам строк за пять минут, — это не два дня, это другая схема данных, другой способ выгрузки, отдельный воркер, очередь и, возможно, отдельное хранилище под аналитику. Функциональное требование добавляется. Нефункциональное — встраивается, и чем позже, тем дороже: к моменту, когда «надо ускорить», решения о синхронном API, нормализации и одном инстансе БД уже приняты и обросли кодом.

Поэтому НФТ — не раздел в конце документа, который дописывают перед отправкой заказчику. Это то, о чём аналитик спрашивает на первой встрече, потому что ответы меняют не текст спецификации, а цену проекта.

В видах требований мы разобрали, чем НФТ отличается от функционального требования и зачем его переводить в измеримую форму. Здесь — как это делают руками: откуда берутся числа, как их проверять, как торговаться, когда безопасность требует одного, а продажи другого, и почему «система должна быть масштабируемой» — это не требование, а настроение. Про контракты интеграций, у которых свои таймауты и коды ошибок, — в анализе API.

Почему слово «нефункциональные» — плохое

Термин неудачный: он звучит как «не про функции, значит, второстепенное». Именно поэтому в стандартах и у архитекторов чаще говорят атрибуты качества (quality attributes) — это ровно то, чем они являются: характеристики того, насколько хорошо система делает то, что делает.

Проверка на понимание: «пользователь входит по логину и паролю» — функциональное требование. «Пароль хранится в виде bcrypt-хэша, а после пяти неудачных попыток вход блокируется на 15 минут» — нефункциональное (безопасность), хотя описывает вполне конкретное поведение. Граница не в наличии поведения, а в вопросе, на который требование отвечает: что делает против как хорошо и при каких условиях.

Каталог атрибутов удобно брать из ISO/IEC 25010 (https://iso25000.com/index.php/en/iso-25000-standards/iso-25010) — восемь характеристик с подхарактеристиками. Как чек-лист на старте проекта это 40 минут работы и обычно два-три найденных требования, о которых не думал никто:

Характеристика ISO 25010 О чём вопрос аналитика Кто обычно владеет Чем проверяется
Функциональная пригодность Полнота, корректность, уместность Владелец процесса Тест-кейсы, приёмка
Производительность Время ответа, пропускная способность, потребление ресурсов Архитектор + бизнес Нагрузочный тест, метрики прода
Совместимость Версии API, форматы, соседи по инфраструктуре Архитектор Контрактные тесты
Удобство использования Сколько времени и ошибок стоит задача человеку Продукт, UX Юзабилити-тест с порогом успеха
Надёжность Доступность, отказоустойчивость, восстановление SRE + бизнес SLO, учения, chaos-проверки
Безопасность Доступ, целостность, аудит, неотказуемость ИБ Пентест, ASVS-ревью, автопроверки
Сопровождаемость Модульность, тестируемость, наблюдаемость Тимлид Ревью, метрики CI
Переносимость Установка, миграция, замена окружения Платформа Прогон установки с нуля

Три графы справа важнее левой. «Тип» требования — просто ярлык; ценность таблицы в том, что она сразу называет владельца числа и способ проверки. Требование, у которого нет ни того, ни другого, до релиза не доживёт.

Анатомия проверяемого НФТ: восемь слотов

Практический шаблон, который спасает от 90% споров на приёмке. Каждое НФТ должно содержать восемь элементов; отсутствие любого из них — дыра, через которую позже проедет конфликт.

Анатомия проверяемого НФТ: восемь обязательных слотов

Сравните формулировки:

❌ «Выгрузка должна формироваться быстро».

⚠️ «Выгрузка формируется не более 5 минут». (есть 1–3, нет 4–8: на каких данных? при какой параллельности? кто мерит?)

✅ «Файл выгрузки за 12 месяцев (до 100 000 строк) готов не позднее 5 минут в 95% случаев при 20 одновременных задачах выгрузки и объёме таблицы заказов 50 млн строк. Замер — от нажатия кнопки до появления ссылки, по метрике export_duration_seconds на проде и нагрузочным прогоном в CI. При нарушении релиз не выпускается; владелец числа — тимлид сервиса заказов».

Длинно? Да. Зато это требование невозможно «выполнить» в кавычках. И обратите внимание: третья формулировка — это уже почти готовый тест и почти готовый дашборд.

Отдельный слот, о котором забывают чаще всего, — последствие нарушения. Пока в требовании не написано, что произойдёт, если p95 уехал, оно ни к чему не обязывает: команда посмотрит на график, вздохнёт и поедет дальше. Возможные последствия: релиз блокируется; фича уходит за фича-флаг; открывается работа вне плана; включается деградация; уведомляется владелец цели. Выбор — предмет договорённости, но выбор должен быть.

Производительность: три разные величины, которые постоянно путают

«Производительность» в разговоре — одно слово, в требованиях — три независимые величины:

  • Латентность (время ответа) — сколько ждёт один пользователь. Меряется в единицах времени и всегда с перцентилью.
  • Пропускная способность (throughput) — сколько работы система делает в единицу времени: запросов в секунду, документов в час, строк в минуту.
  • Ёмкость (capacity) — какой объём система держит без деградации: RPS, число одновременных пользователей, объём данных, размер очереди.

Они связаны, но не взаимозаменяемы. Систему можно ускорить, потеряв пропускную способность (например, отключив батчинг), и можно увеличить пропускную способность, ухудшив латентность (добавив буферы и очереди). Требование «система должна быть производительной» не даёт инженеру выбрать, какой из трёх рычагов крутить.

Полезная связка — закон Литтла: L = λ × W, где L — среднее число заявок в системе, λ — интенсивность потока, W — среднее время пребывания. Для аналитика это способ за минуту проверить, что числа в требовании не противоречат друг другу: если вы просите 200 запросов/с при среднем времени ответа 0,4 с, то в системе постоянно живёт около 80 одновременных запросов — и если в требовании к инфраструктуре стоит пул на 20 соединений, числа не сходятся, и это надо заметить до разработки, а не на нагрузочном тесте.

Среднее врёт, перцентиль — нет

Главная техническая деталь, которую аналитик обязан понимать: среднее время ответа — бесполезная метрика для требований. Пример: 99 запросов по 50 мс и один по 10 секундам дают среднее 149 мс — красивое число, за которым спрятан пользователь, который ждал десять секунд и ушёл.

Метрика Что означает Когда использовать в требовании
avg среднее по всем запросам почти никогда; годится для планирования ёмкости
p50 (медиана) половине пользователей было быстрее «типичный опыт», хорош как второй порог
p95 5% запросов медленнее основной порог для пользовательских сценариев
p99 1% запросов медленнее критичные сценарии, деньги, платежи
p99.9 0,1% медленнее внутренние сервисы в длинной цепочке вызовов
max худший случай таймауты и SLA с внешними системами

Масштаб полезно проговаривать с бизнесом в людях, а не в процентах: при 1 миллионе запросов в сутки p99 = 1% — это 10 000 обращений в день, которым было хуже порога. Если каждое из них — брошенная корзина, легко посчитать деньги, и требование к p99 перестаёт быть капризом инженеров.

Ещё две ловушки, которые стоит знать, чтобы не подписывать бессмысленные числа:

  1. Перцентили не складываются. Если страница делает четыре независимых запроса, её p95 — не «максимум из четырёх p95». Вероятность, что все четыре попадут в быстрые 95%, равна 0.95⁴ ≈ 0.81: то есть почти каждая пятая загрузка страницы упрётся в хвост. Это и есть эффект, описанный в статье Google «The Tail at Scale» (https://research.google/pubs/the-tail-at-scale/): чем больше вызовов на один пользовательский сценарий, тем сильнее хвост определяет опыт.
  2. Coordinated omission — систематическое занижение измерений, когда нагрузочный генератор ждёт медленного ответа и не отправляет запросы, которые должен был отправить. Классический разбор — доклад Гила Тене «How NOT to Measure Latency» (https://www.infoq.com/presentations/latency-response-time/) и библиотека HdrHistogram (http://hdrhistogram.org/). Практический вывод для аналитика: в требовании указывайте не только число, но и модель нагрузки — «постоянная интенсивность 180 запросов/с» (open model), а не «50 виртуальных пользователей в цикле».

Вычислить перцентиль по выборке — не магия. Псевдокод метода nearest-rank:

ПЕРЦЕНТИЛЬ(значения, q):
    если значения пусты: ошибка
    данные <- отсортировать(значения)          # по возрастанию
    ранг   <- округлить_вверх(q / 100 * длина(данные))
    вернуть данные[max(ранг, 1) - 1]           # индексация с нуля

Реализация и оценка сложности:

import math
from collections.abc import Sequence


def percentile(values: Sequence[float], q: float) -> float:
    """Перцентиль методом nearest-rank (как считают k6, Prometheus histogram_quantile
    и большинство инструментов нагрузочного тестирования).

    Время: O(n log n) на сортировке, память: O(n) на копию.
    Для потока данных используют приближённые структуры (t-digest, HDR-гистограмма):
    время O(1) на элемент, память O(1) при контролируемой относительной погрешности.
    """
    if not values:
        raise ValueError("пустая выборка: перцентиль не определён")
    if not 0 < q <= 100:
        raise ValueError("q должно быть в диапазоне (0, 100]")
    data = sorted(values)
    rank = math.ceil(q / 100 * len(data))
    return data[max(rank, 1) - 1]


# 99 быстрых запросов и один очень медленный
sample = [50.0] * 99 + [10_000.0]
print(sum(sample) / len(sample))   # 149.5 — «среднее время ответа 150 мс»
print(percentile(sample, 95))      # 50.0  — p95 честно говорит: типично быстро
print(percentile(sample, 99))      # 50.0
print(percentile(sample, 100))     # 10000.0 — вот где живёт проблема

Обратите внимание на последнюю пару строк: на выборке из 100 значений p99 физически не видит единственный выброс. Отсюда правило: перцентиль имеет смысл только при достаточном числе измерений — для p99 нужны хотя бы тысячи запросов, для p99.9 — десятки тысяч. Требование «p99.9 ≤ 300 мс» для сервиса с 200 запросами в день не проверяемо, и это нужно говорить вслух на этапе согласования.

Один и тот же запрос — три разных числа

Второй источник споров: где именно замеряется. Пока это не зафиксировано, команда и заказчик обсуждают разные числа и оба правы.

Правило: пользовательские требования формулируются в точке A (человек не знает про ваш балансировщик), а операционные SLO — в точке B, потому что точку A команда не контролирует целиком. Если в требовании стоит точка A, то в нём обязана появиться оговорка про условия клиента: «на канале 10 Мбит/с, устройство не старше 2020 года, браузер из поддерживаемого списка». Иначе требование нарушается сетью пользователя, а виноватой оказывается команда.

Раскладка бюджета: как одно число становится восемью

Число, которое лежит целиком на «сервисе», — это число, за которое отвечает никто. Рабочая практика: разложить бюджет по участкам пути и назначить каждому куску владельца.

Бюджет времени ответа 400 мс, разложенный по участкам пути запроса

Что даёт раскладка аналитику практически:

  • становятся видны требования к смежникам: «внешний сервис цен отвечает за 60 мс, таймаут 120 мс, при таймауте показываем цену из кэша со сноской» — это отдельное требование, которое иначе всплывёт на интеграционном тестировании;
  • появляется резерв, а без резерва бюджет нарушается в первый же пик;
  • при нарушении сразу понятно, чей участок съел время, — разговор идёт про участок, а не про «у вас всё тормозит».

Инженерную часть темы — как это измеряют и оптимизируют — см. в измерении производительности, нагрузочном тестировании и тестировании производительности. Для веб-сценариев пороги удобно брать из Core Web Vitals — см. веб-производительность.

Ёмкость: требование с датой годности

«Система должна выдерживать нагрузку» — самая частая формулировка и самая пустая. Ёмкостное требование состоит из четырёх частей: профиль нагрузки, объём данных, горизонт и что происходит при превышении.

Профиль нагрузки считается из бизнес-цифр, а не выдумывается. Показываю арифметику, которую аналитик должен уметь делать на встрече в уме или в блокноте:

def peak_rps(dau: int, actions_per_user: int,
             peak_hour_share: float = 0.20, burst_factor: float = 2.0) -> dict:
    """Оценка нагрузки из продуктовых цифр.

    dau               — активные пользователи в сутки;
    actions_per_user  — обращений к API на пользователя за день;
    peak_hour_share   — доля суточного трафика в самый загруженный час (0.15–0.30);
    burst_factor      — во сколько раз минутный всплеск выше среднего в пиковом часе.
    """
    daily = dau * actions_per_user
    avg_rps = daily / 86_400
    peak_hour_rps = daily * peak_hour_share / 3_600
    return {
        "запросов в сутки": daily,
        "средний RPS": round(avg_rps, 1),
        "RPS в пиковый час": round(peak_hour_rps, 1),
        "RPS во всплеске": round(peak_hour_rps * burst_factor, 1),
    }


print(peak_rps(dau=200_000, actions_per_user=8))
# {'запросов в сутки': 1600000, 'средний RPS': 18.5,
#  'RPS в пиковый час': 88.9, 'RPS во всплеске': 177.8}

Разница между «18 RPS» и «178 RPS» — это разница между одним инстансом и кластером с автоскейлингом. Именно поэтому в требовании пишут пик, а не среднее: среднее не проектирует ничего. Сложность самой оценки — O(1), но ценность её высока: она превращает разговор «нам нужно, чтобы всё летало» в разговор про 178 запросов в секунду, который уже можно оценить в деньгах.

Дальше — сезонность и горизонт. Формулировка, которая работает:

НФТ-12. Ёмкость. Система обслуживает 180 запросов/с к API заказов и 20 одновременных задач выгрузки при объёме 50 млн заказов и 400 ГБ файлов, с сохранением НФТ-3 (p95) и НФТ-7 (доступность). Горизонт — 12 месяцев от релиза с учётом декабрьского пика ×3 к среднему месяцу. При превышении: новые задачи выгрузки становятся в очередь, пользователь видит позицию и оценку времени; доля ответов 5xx не превышает 0,1%; генерируется событие для команды платформы. Проверка — квартальный нагрузочный прогон по профилю «декабрь».

Три вещи здесь принципиальны. Во-первых, горизонт: без него требование становится вечным, и через два года по нему предъявляют претензии. Во-вторых, связка с другими НФТ: ёмкость без сохранения латентности бессмысленна — «держит 1000 RPS» с ответом за 30 секунд формально выполнено. В-третьих, поведение при превышении: система, которая при перегрузке отвечает «встаньте в очередь, вы 14-й», гораздо лучше системы, которая отдаёт 502 всем сразу. Это тоже требование, и его надо написать.

Надёжность: SLI, SLO, SLA и бюджет ошибок

Начнём с арифметики доступности, потому что она мгновенно охлаждает разговоры про «мы хотим пять девяток».

Доступность Простой в месяц (30 дней) Простой в год Что это значит на практике
99% 7 ч 12 мин 3,65 суток одна длинная авария в месяц — нормально
99,5% 3 ч 36 мин 1,83 суток плановые работы можно делать в рабочее время
99,9% 43 мин 8 ч 46 мин дежурство, но без ночных подъёмов ради каждого алерта
99,95% 21 мин 36 с 4 ч 23 мин резервирование, автоматический перевод трафика
99,99% 4 мин 19 с 52 мин круглосуточное дежурство, мульти-зона, автоматика без человека
99,999% 26 с 5 мин 15 с почти всегда неправда: одна перезагрузка съедает годовой бюджет

Отсюда первый вопрос стейкхолдеру, который просит 99,99%: «за 4 минуты простоя в месяц вы готовы платить круглосуточным дежурством и удвоением инфраструктуры?» Обычно после такого вопроса требование становится 99,9% с оговорками — и это здоровый результат работы аналитика, а не «мы не смогли».

Второй вопрос — доступность чего и с чьей точки зрения. «Сервис доступен» может значить: процесс отвечает на /health; API возвращает 200 на реальный запрос; пользователь может оформить заказ. Это три разных требования; последнее — единственное, которое интересует бизнес.

Терминологию стоит держать строго — путаница в ней стоит денег:

  • SLI (indicator) — метрика: доля успешных запросов = хорошие события / все валидные события.
  • SLO (objective) — целевое значение SLI за окно: «99,9% за 28 скользящих дней».
  • SLA (agreement) — юридическое обещание внешнему клиенту с компенсацией за нарушение. SLA всегда мягче SLO: если вы обещали клиенту 99,5%, внутри держите 99,9%, чтобы оставался зазор.
  • Бюджет ошибок (error budget) — 1 − SLO, разрешённое количество плохого поведения. Это самый полезный инструмент из всего набора: он превращает надёжность из спора в арифметику. Бюджет не израсходован — команда катит фичи; израсходован — команда занимается надёжностью. Канонический разбор — глава про SLO в Google SRE Book (https://sre.google/sre-book/service-level-objectives/) и практика внедрения в SRE Workbook (https://sre.google/workbook/implementing-slos/).
import math


def error_budget(slo: float, window_days: int = 28) -> dict:
    """Бюджет ошибок в минутах простоя и в запросах."""
    window_minutes = window_days * 24 * 60
    return {
        "окно, мин": window_minutes,
        "бюджет, мин": round(window_minutes * (1 - slo), 1),
        "бюджет, % запросов": round((1 - slo) * 100, 3),
    }


def chain_availability(*components: float) -> float:
    """Доступность последовательной цепочки: отказ любого звена — отказ сценария."""
    return math.prod(components)


print(error_budget(0.999))                       # {'окно, мин': 40320, 'бюджет, мин': 40.3, ...}
print(round(chain_availability(0.999, 0.999, 0.999, 0.999) * 100, 3))   # 99.601
print(round(chain_availability(0.999, 0.995) * 100, 3))                 # 99.401

Вторая функция — источник самого частого недоразумения в интеграционных проектах. Если сценарий «оформить заказ» проходит через четыре сервиса с доступностью 99,9%, доступность сценария — 99,6%, то есть почти 3 часа простоя в месяц вместо 43 минут. Требование «сценарий оформления доступен на 99,9%» при такой архитектуре недостижимо без резервирования, кэшей и деградации — и это разговор, который аналитик обязан начать до того, как цифру подпишут. Подробнее про модели отказов — в отказах распределённых систем.

Деградация как требование, а не как случайность

Самая недооценённая часть требований к надёжности — что система делает, когда ей плохо. Бинарная картина «работает / не работает» почти всегда неверна: между ними живут несколько полезных состояний, и их надо описать заранее. Иначе решение примет разработчик в три часа ночи.

Каждый переход на этой схеме — требование. Каждое состояние требует ответа на четыре вопроса: что видит пользователь; что происходит с данными, которые он отправил; кто узнаёт о переходе; как система выходит обратно. Практический приём: попросите на воркшопе назвать одну функцию, которую можно выключить, чтобы остальное работало. Если стейкхолдеры не могут назвать ни одной, у вас не выйдет ни деградации, ни разумного бюджета — и об этом стоит написать в риски (см. управление рисками).

RPO и RTO: два числа, которые путают всегда

  • RPO (Recovery Point Objective) — сколько данных допустимо потерять, измеряется во времени: «RPO 5 минут» = после аварии могут пропасть последние 5 минут изменений. Определяет схему резервного копирования и репликации.
  • RTO (Recovery Time Objective) — сколько времени допустимо восстанавливаться: «RTO 60 минут» = через час после начала аварии сервис работает.

Оба числа задаёт бизнес, а не инженеры, и задаёт по-разному для разных данных: для журнала аудита RPO = 0 (потеря недопустима), для кэша рекомендаций RPO может быть сутки. Требование без разделения по типам данных заставляет платить за самый строгий случай везде.

И главное: RPO/RTO без учений — фикция. Требование пишется так: «проверяется учением по восстановлению из резервной копии на изолированном стенде раз в полгода, протокол с фактическими значениями прикладывается». Резервная копия, из которой ни разу не восстанавливались, статистически не существует.

Безопасность: от «должно быть безопасно» к проверяемым пунктам

Безопасность — не атрибут в состоянии «есть/нет», а набор требований, привязанных к активам и угрозам. Поэтому нельзя написать одно НФТ «система должна быть безопасной»; можно пройти по активам и получить десять конкретных.

Рабочая последовательность для аналитика: перечислить активы → для каждого прогнать угрозы по STRIDE → превратить каждую значимую угрозу в требование с проверкой. Подробнее про сам метод — моделирование угроз.

Актив Угроза (STRIDE) Требование Чем проверяется
Файл выгрузки с ПД клиентов Раскрытие информации Ссылка одноразовая, живёт 72 ч, привязана к пользователю; файл шифруется на стороне хранилища Автотест на повторное скачивание, ревью настроек бакета
Данные клиента в API Повышение привилегий Продавец видит только свои заказы; проверка владения — на сервере, не в UI Тест «чужой ID возвращает 404», а не 403
Журнал операций Подделка данных Запись только на добавление, срок хранения 3 года, изменение невозможно ролью приложения Проверка прав на таблицу, тест на UPDATE
Пароли и токены Спуфинг Хэш bcrypt (cost ≥ 12), блокировка на 15 мин после 5 попыток, 2FA для роли «администратор» ASVS-ревью, автотест блокировки
Публичный эндпоинт выгрузки Отказ в обслуживании Лимит 10 выгрузок в час на пользователя и 200 на арендатора; при превышении 429 с Retry-After Нагрузочный тест лимитера
Действия администратора Отказ от совершённого Каждое действие в журнале: кто, что, когда, с какого IP; журнал недоступен на изменение Ручной прогон + проверка полноты полей

Обратите внимание на строку про 404 вместо 403: это классическое требование, которое пишут неверно. Ответ 403 на чужой идентификатор подтверждает, что объект существует, — утечка через код ответа. Такие детали аналитик обязан фиксировать в требовании, иначе разработчик выберет «логичный» вариант.

Отдельная категория — требования, вытекающие из авторизации. Матрица доступа на 15 строк экономит недели споров:

Операция Продавец Старший продавец Поддержка Администратор
Смотреть свои заказы
Смотреть заказы других продавцов ✅ (свой отдел) ✅ (только чтение)
Выгружать файл с ПД ✅ (маскированные телефоны) ✅ (с записью в журнал)
Отменять заказ ✅ (до оплаты)
Менять роли ✅ (с 2FA)

Механика ролей и проверок — в авторизации, а требования, приходящие от регуляторики (152-ФЗ, GDPR: правовое основание, минимизация, сроки хранения, право на удаление), — в приватности и комплаенсе. Как чек-лист проверяемых пунктов удобен OWASP ASVS (https://owasp.org/www-project-application-security-verification-standard/): это готовый список требований с уровнями, из которого можно брать формулировки, не изобретая их.

Какие НФТ обязательны — решает содержимое фичи

Чтобы не полагаться на память, полезно иметь ворота: короткое дерево вопросов, через которое проходит каждая новая история. Это то место, где аналитик приносит максимум пользы за минимум времени.

Дерево не заменяет мышление, но снимает системную ошибку «забыли спросить». Пять минут на историю — и вы больше не узнаёте про требование к идемпотентности после инцидента с двойным списанием.

Качества, о которых забывают чаще всего

Производительность, надёжность и безопасность обсуждают почти всегда. Ниже — те, что регулярно выпадают и всплывают в самый неудачный момент.

Наблюдаемость. Это НФТ, и очень часто аналитик — единственный, кто его формулирует. «Каждый запрос несёт correlation_id, который приходит в ответе и виден в поддержке по номеру заказа»; «переход выгрузки между статусами пишется в журнал с меткой времени и причиной»; «дашборд показывает долю успешных выгрузок и p95 длительности». Без этого любое ваше числовое требование непроверяемо на проде. Инженерная часть — наблюдаемость и дежурства.

Совместимость и версионирование. «Старое мобильное приложение версии 4.x продолжает работать 6 месяцев после релиза нового API»; «удаление поля из ответа — мажорная версия». Без этого требования обратная совместимость ломается в первом же рефакторинге.

Локализация, часовые пояса, форматы. «Все метки времени хранятся в UTC, показываются в часовом поясе пользователя»; «суммы округляются до копеек по правилу half-up»; «имена сортируются по правилам локали». Это скучные требования, которые стоят дорого, если их не написать: перепутанный часовой пояс в отчёте — это часы разбирательств с бухгалтерией.

Доступность (accessibility). Формулируется абсолютно проверяемо: «интерфейс соответствует WCAG 2.2 уровня AA (https://www.w3.org/TR/WCAG22/); все действия доступны с клавиатуры; контраст текста не ниже 4,5:1». Для госсектора и крупного enterprise это часто ещё и юридическое требование — см. доступность: зачем и стандарты.

Удобство использования как число. «Новый продавец без обучения оформляет первую выгрузку за 3 минуты, успех у 4 из 5 участников теста». Порог + процедура — и «удобно» становится проверяемым. Механика — юзабилити-тестирование.

Стоимость эксплуатации. «Себестоимость одной выгрузки не превышает 2 руб. при профиле нагрузки НФТ-12». Внезапно это самое действенное НФТ: оно заставляет команду выбирать между «быстро» и «дорого» осознанно. Про экономику решений — стоимость облака и компромиссы.

Сохранность истории и ретеншен. «Заказы хранятся 5 лет, журнал доступа — 3 года, черновики удаляются через 90 дней». Отсутствие этого требования — гарантированный конфликт между юристами (хранить долго) и инфраструктурой (не хранить мусор).

Где НФТ живут: реестр, а не абзац в конце документа

Типичная ошибка оформления: раздел «Нефункциональные требования» в конце ТЗ, куда никто не заглядывает. НФТ работают, когда они привязаны к сценариям и к проверкам. Минимальная модель, которую стоит держать в трекере или таблице:

Смысл модели не в том, чтобы завести шесть таблиц (в большинстве команд это одна таблица и несколько полей в задачах), а в том, чтобы каждое НФТ имело живые связи: с чем связано, чем измеряется, где проверяется и что было в последний раз. Про технику связей и словарь — моделирование данных; про трассировку к критериям приёмки — приёмка.

Машиночитаемая часть реестра прекрасно живёт рядом с кодом. Пример SLO в формате OpenSLO (https://github.com/OpenSLO/OpenSLO):

apiVersion: openslo/v1
kind: SLO
metadata:
  name: orders-api-latency
  labels: { nfr: "НФТ-3", scenario: "UC-4" }   # связь с реестром требований
spec:
  service: orders-api
  description: "95% запросов к списку заказов быстрее 400 мс, замер на балансировщике"
  indicator:
    metadata: { name: fast-requests-ratio }
    spec:
      ratioMetric:
        counter: true
        good:   # запросы, уложившиеся в 400 мс
          metricSource:
            type: Prometheus
            spec: { query: 'sum(rate(http_request_duration_seconds_bucket{route="/orders",le="0.4"}[5m]))' }
        total:  # все валидные запросы
          metricSource:
            type: Prometheus
            spec: { query: 'sum(rate(http_request_duration_seconds_count{route="/orders"}[5m]))' }
  objectives:
    - displayName: "p95 не хуже 400 мс"
      target: 0.95
  timeWindow:
    - duration: 28d
      isRolling: true

А проверка в CI — это порог в нагрузочном тесте, который умеет ронять сборку (документация k6 по thresholds: https://grafana.com/docs/k6/latest/using-k6/thresholds/):

import http from 'k6/http';
import { check } from 'k6';

export const options = {
  // Открытая модель: держим интенсивность, а не число пользователей,
  // иначе получим coordinated omission и слишком красивые числа.
  scenarios: {
    december_peak: {
      executor: 'constant-arrival-rate',
      rate: 180, timeUnit: '1s', duration: '20m',
      preAllocatedVUs: 100, maxVUs: 400,
    },
  },
  thresholds: {
    // НФТ-3: p95 не хуже 400 мс, p99 не хуже 1200 мс
    'http_req_duration{name:orders}': ['p(95)<400', 'p(99)<1200'],
    // НФТ-7: доля ошибок не выше 0,1%
    'http_req_failed': ['rate<0.001'],
  },
};

export default function () {
  const res = http.get('https://stage.example.com/api/orders?period=12m',
                       { tags: { name: 'orders' } });
  check(res, { 'статус 200': (r) => r.status === 200 });
}

И те же числа — в критериях приёмки, языком, который читает заказчик:

Функционал: Выгрузка заказов за период
  Сценарий: Крупная выгрузка в декабрьский пик укладывается в бюджет
    Дано в таблице заказов 50 млн строк
    И включён профиль нагрузки "декабрь": 180 запросов в секунду к API заказов
    И одновременно выполняется 20 задач выгрузки
    Когда продавец запрашивает выгрузку за 12 месяцев на 100 000 строк
    Тогда ссылка на готовый файл появляется не позднее 5 минут в 95% случаев
    И доля ответов 5xx на API заказов не превышает 0,1%
    И в журнале есть запись о доступе к персональным данным

Три артефакта — YAML, k6-скрипт и Gherkin — описывают одно и то же требование на трёх языках: для эксплуатации, для CI и для человека. Это и есть «измеримое НФТ» в готовом виде.

Что ломается чаще всего

Ниже — список отказов, который я бы повесил на стену. Почти каждый пункт я видел в проектах, и почти каждый стоил недель.

1. НФТ, которое невозможно проверить. «Система должна быть масштабируемой», «интерфейс должен быть интуитивным», «код должен быть поддерживаемым». Тест на пригодность: попробуйте сформулировать, как требование будет опровергнуто. Если опровержение невозможно, требования нет. Ремонт: заменить прилагательное на процедуру с порогом — «добавление второго инстанса увеличивает пропускную способность не менее чем в 1,7 раза без изменения кода; проверяется прогоном».

2. Число без условий. «p95 = 200 мс» — не требование, пока не указаны нагрузка, объём данных и точка замера. На пустой базе выполнится всё что угодно. Самый частый сценарий провала: приняли на стенде с 10 тысячами записей, в проде 40 миллионов.

3. Неявные НФТ — молчаливые ожидания. Никто не произносит вслух то, что «и так понятно»: что система работает в мобильном браузере; что отчёт можно открыть в Excel без ошибок кодировки; что данные не пропадут при обрыве связи; что вход не отвалится через 5 минут простоя; что удаление обратимо в течение суток. Техника выявления — спрашивать не про требования, а про катастрофы: «представьте, что мы сдали систему и вы в ужасе. Что случилось?» Ответы почти всегда нефункциональные. Больше приёмов — в выявлении требований.

4. Противоречия между стейкхолдерами. НФТ противоречат друг другу чаще, чем функциональные, потому что делят один ресурс — время и деньги:

Сторона A Сторона B Чем платим за компромисс
ИБ: 2FA, сессия 15 мин, пароль 14 символов Продажи: вход в один клик, конверсия Число шагов входа против риска компрометации; выход — разные правила для разных ролей и рисковых операций
Юристы: хранить журналы 5 лет Платформа: не хранить лишнее, дорого Стоимость хранения против штрафа; выход — «горячие» 90 дней и холодный архив
Бизнес: 99,99% доступности Финансы: не увеличивать бюджет Резервирование стоит денег; выход — 99,9% для всего и 99,99% для оплаты
Аналитика: полное логирование запросов ИБ и приватность: не писать ПД в логи Полнота диагностики против утечки; выход — маскирование полей и раздельные потоки логов

Правило разрешения: не искать середину, а поднять вопрос к владельцу цели. Компромисс «давайте 99,95%, чтобы всех устроить» — худший вариант: за него платят, а никому он не нужен. Задача аналитика — не решить конфликт своей властью, а сделать его видимым с ценой каждого варианта и принести владельцу решения. Подробно — в работе со стейкхолдерами.

5. «Хотелка» без задачи. «Хотим ответ за 100 мс» — почему 100? Правильные вопросы: какой инцидент вы вспоминаете, когда это говорите; что произойдёт при 300 мс; при каком значении вы начнёте жаловаться. Часто за красивым числом стоит одна конкретная медленная страница, а не требование ко всей системе.

6. Среднее вместо перцентиля. «Среднее время ответа не более 500 мс» — требование, которое выполняется при том, что каждый двадцатый пользователь ждёт восемь секунд. Замена на p95/p99 не стоит ничего на этапе формулировки и экономит квартал позже.

7. Все НФТ критичны. Если у 40 требований приоритет «обязательное», приоритетов нет. Практика: не более 5–7 действительно жёстких чисел на релиз, остальные — «желаемые» с явной пометкой, что их можно нарушить без блокировки релиза.

8. НФТ без поведения при нарушении. Что делает система, когда внешний сервис отвечает дольше таймаута; когда очередь переполнена; когда диск заканчивается? Отсутствие ответа означает, что решение примет случай. Каталог типовых решений — «Release It!» Майкла Найгарда (https://pragprog.com/titles/mnee2/release-it-second-edition/): таймауты, предохранители, переборки, деградация.

9. НФТ, замеренное не там, где болит. Дашборд зелёный, пользователи жалуются. Обычно причина в том, что метрика снимается в точке C (внутри сервиса), а страдает точка A (человек с мобильным интернетом и тремя последовательными запросами).

10. НФТ, написанное для системы, но не для процесса. «Заявка обрабатывается за 2 часа» — если 1 час 50 минут занимает согласование человеком, оптимизация кода не поможет. Раскладывать по участкам нужно не только запрос, но и процесс: см. моделирование процессов.

Формальный документ или разговор у доски

НФТ — та область, где формализация нужна чаще, чем в функциональных требованиях, но не всегда. Критерий тот же экономический: платим за документ, когда знание живёт долго и/или ошибка дорога.

Ситуация Формат Почему
Числа в договоре с внешним клиентом (SLA, штрафы) Документ с версией, подписью, датой Спор будет юридическим, а не техническим
Регуляторные требования: ПД, сроки хранения, аудит Документ + ссылка на норму Понадобится показать проверяющему
Бюджеты латентности и доступности сервиса Реестр НФТ + SLO в коде (YAML) Живут годами, меняются осознанно, нужны для алертов
RPO/RTO и план восстановления Документ + протокол учений Читают ночью незнакомые люди
Матрица доступа по ролям Таблица в вики, версионируемая Меняется, но каждая версия важна
Порог по времени для внутреннего фонового скрипта Строка в задаче Цена ошибки — час работы
Оценка нагрузки на этапе идеи Схема на доске + фото в задаче Числа изменятся через две недели
Выбор между двумя способами кэширования Разговор + короткая запись решения Решение техническое и обратимое

Что фиксируется всегда, даже в самой быстрой команде: числовые пороги (потому что «мы вроде договаривались про три секунды» не работает), точка замера, последствие нарушения и владелец. Это четыре строки, и они умещаются в описание задачи. Фото доски в задаче — легитимный артефакт, если на нём видны числа и стоит дата.

Хороший приём для гибких команд — вынести повторяющиеся НФТ в Definition of Done и в шаблон истории, вместо копирования в каждую задачу: логирование с correlation id, p95 в бюджете сервиса, отсутствие ПД в логах, обратная совместимость API. Тогда в самой истории остаются только НФТ, специфичные для неё. Про эту механику — аналитик в Agile.

Как добывать числа, когда «никто не знает»

Самая частая жалоба: «заказчик не может назвать цифру». Он и не должен уметь — это ваша работа. Пять источников по возрастанию усилий:

  1. Текущий прод. Если система работает, снимите метрики и покажите: «сейчас p95 = 1,8 с, мы предлагаем 400 мс». Обсуждать конкретное число в разы легче, чем абстрактное.
  2. Аналог или конкурент. Замерьте руками, сколько грузится тот же экран у конкурента. Аргумент «у них 600 мс, у нас 3 с» работает без объяснений.
  3. Пороги восприятия. Классика Нильсена (https://www.nngroup.com/articles/response-times-3-important-limits/): 0,1 с — ощущение мгновенности; 1 с — мысль не прерывается; 10 с — предел внимания. Отсюда берутся «до 1 секунды на действие в интерфейсе, свыше — с индикатором прогресса».
  4. Деньги. «Сколько стоит час простоя?» Ответ переводит требование к доступности из вкусовщины в расчёт: если час простоя — 400 000 руб., то разница между 99,9% и 99,95% стоит примерно 150 000 руб. в год, и её можно сравнить с ценой резервирования. Про метрики и деньги — метрики продукта.
  5. Лестница порогов. Когда всё остальное не помогает, спрашивайте не «сколько нужно», а «при каком значении вы позвоните мне ночью»: 1 с — нормально? 3 с — терпимо? 10 с — жалоба? 30 с — катастрофа? Люди плохо называют цели и хорошо называют границу боли. Из границы боли получается порог.

Отдельно про сценарии атрибутов качества (quality attribute scenarios) из практики SEI: требование записывается как «источник → стимул → артефакт → условия → реакция → мера реакции». Это тот же набор слотов, что на схеме выше, только в архитектурной терминологии; подробно — у Басса, Клементса и Кацмана в «Software Architecture in Practice» (https://www.oreilly.com/library/view/software-architecture-in-practice/9780136885979/) и в методе оценки архитектуры ATAM (https://insights.sei.cmu.edu/library/atam-method-for-architecture-evaluation/). Архитектурная сторона вопроса на портале — в материалах по требованиям.

Чек-лист аналитика по НФТ

Перед тем как отдать требования в разработку, пройдите десять вопросов:

  1. Каждое числовое НФТ содержит все восемь слотов (объект, метрика, цель, условия, точка замера, инструмент, последствие, владелец)?
  2. Латентность указана перцентилями, а не средним, и число измерений достаточно для заявленной перцентили?
  3. Названы профиль нагрузки и объём данных, на котором требование проверяется?
  4. Есть горизонт («на 12 месяцев») и поведение при превышении ёмкости?
  5. Доступность определена как доступность сценария для пользователя, а не процесса для мониторинга; посчитана доступность цепочки?
  6. Описаны состояния деградации и переходы между ними?
  7. RPO/RTO заданы отдельно по типам данных и подкреплены учениями?
  8. Требования безопасности выведены из активов и угроз, а не из общих слов; есть матрица доступа и правила по ПД в логах?
  9. Наблюдаемость сформулирована как требование (correlation id, метрики, журнал переходов)?
  10. Приоритеты расставлены так, что «обязательных» не больше семи, и по каждому написано, что произойдёт при нарушении?

Мини-итог

  • НФТ ломают архитектуру, а не спринт, поэтому спрашивают о них в начале, а не в конце.
  • «Нефункциональное» — плохое слово; думайте «атрибуты качества» и ходите по ISO 25010 как по чек-листу.
  • Проверяемое НФТ — восемь слотов: объект, метрика, цель, условия, точка замера, инструмент, последствие нарушения, владелец. Отсутствующие слоты становятся конфликтами.
  • Производительность — это три величины (латентность, пропускная способность, ёмкость), и говорить о них надо перцентилями, а не средними.
  • Один бюджет времени раскладывается по участкам пути; у каждого участка появляется хозяин и требование к смежникам.
  • Надёжность считается арифметикой: минуты простоя, произведение доступностей в цепочке, бюджет ошибок. Отдельно описываются деградация, RPO и RTO — и подтверждаются учениями.
  • Безопасность не пишется одной строкой: активы → угрозы → требования → проверки, плюс матрица доступа и правила по персональным данным.
  • Числа добываются из прода, аналогов, порогов восприятия, денег и «лестницы боли»; «никто не знает» — не ответ, а начало работы аналитика.
  • Формальный документ нужен там, где спор будет юридическим или дорогим; в остальном хватает четырёх строк в задаче и фото доски — но пороги, точка замера, последствие и владелец фиксируются всегда.

Источники

Что дальше

Числа мы получили и записали так, что их можно проверить. Но остаётся вопрос, который никакая формулировка не закрывает: а верно ли само предположение, что людям нужна именно эта функция и именно в таком виде? Дешевле выяснить это до разработки — прототипом, экспериментом или сценарием, прогнанным на живом человеке.

Прототипирование и проверка гипотез до разработки

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов