Нефункциональные требования: производительность, надёжность, безопасность, как их измерять
Функциональные требования ломают спринт. Нефункциональные ломают архитектуру.
Разница в цене ошибки. Если вы забыли поле в форме, это два дня работы. Если вы забыли, что отчёт должен собираться по 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 перестаёт быть капризом инженеров.
Ещё две ловушки, которые стоит знать, чтобы не подписывать бессмысленные числа:
- Перцентили не складываются. Если страница делает четыре независимых запроса,
её p95 — не «максимум из четырёх p95». Вероятность, что все четыре попадут в быстрые 95%,
равна
0.95⁴ ≈ 0.81: то есть почти каждая пятая загрузка страницы упрётся в хвост. Это и есть эффект, описанный в статье Google «The Tail at Scale» (https://research.google/pubs/the-tail-at-scale/): чем больше вызовов на один пользовательский сценарий, тем сильнее хвост определяет опыт. - 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 запросами в день не проверяемо, и это нужно говорить вслух на этапе согласования.
Один и тот же запрос — три разных числа
Второй источник споров: где именно замеряется. Пока это не зафиксировано, команда и заказчик обсуждают разные числа и оба правы.
сеть, TLS, рендер. Число самое большое CDN->>API: проброс запроса Note over CDN,API: Точка B — то, что видит платформа:
обычно здесь и живёт SLO API->>DB: SELECT по индексу, LIMIT 50 DB-->>API: 50 строк за 90 мс API->>PR: GET /prices, таймаут 120 мс PR-->>API: 200 OK за 40 мс Note over API,PR: Точка C — то, что видит сервис в своих метриках:
самое оптимистичное из трёх чисел API-->>CDN: 200 OK CDN-->>U: 200 OK, 312 мс Note over U,PR: «p95 ≤ 400 мс» в точках A, B и C —
это три разных требования и три разные цены
Правило: пользовательские требования формулируются в точке A (человек не знает про ваш балансировщик), а операционные SLO — в точке B, потому что точку A команда не контролирует целиком. Если в требовании стоит точка A, то в нём обязана появиться оговорка про условия клиента: «на канале 10 Мбит/с, устройство не старше 2020 года, браузер из поддерживаемого списка». Иначе требование нарушается сетью пользователя, а виноватой оказывается команда.
Раскладка бюджета: как одно число становится восемью
Число, которое лежит целиком на «сервисе», — это число, за которое отвечает никто. Рабочая практика: разложить бюджет по участкам пути и назначить каждому куску владельца.
Что даёт раскладка аналитику практически:
- становятся видны требования к смежникам: «внешний сервис цен отвечает за 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/): это готовый список требований с уровнями, из которого можно брать формулировки, не изобретая их.
Какие НФТ обязательны — решает содержимое фичи
Чтобы не полагаться на память, полезно иметь ворота: короткое дерево вопросов, через которое проходит каждая новая история. Это то место, где аналитик приносит максимум пользы за минимум времени.
данные?"} D1 -- да --> R1["Обязательно: правовое основание, минимизация полей,
срок хранения, шифрование, журнал доступа,
маскирование в логах, удаление по запросу"] R1 --> D2{"Затрагивает деньги
или обязательства?"} D1 -- нет --> D2 D2 -- да --> R2["Обязательно: идемпотентность операции, журнал изменений,
сверка итогов, запрет частичного применения,
двойное подтверждение выше порога суммы"] R2 --> D3{"Синхронный ответ
пользователю?"} D2 -- нет --> D3 D3 -- да --> R3["Обязательно: p95 и p99, таймаут, поведение при таймауте,
что показываем вместо результата"] D3 -- нет --> R4["Обязательно: срок готовности, уведомление,
повторы, отдельная очередь для сбоев"] R3 --> D4{"Есть внешние
интеграции?"} R4 --> D4 D4 -- да --> R5["Обязательно: чужой SLA, таймаут, повторы с backoff,
деградация при недоступности, кто узнаёт об отказе"] R5 --> D5{"Меняется схема
данных или контракт?"} D4 -- нет --> D5 D5 -- да --> R6["Обязательно: обратная совместимость, план миграции,
окно совместимости версий, откат"] R6 --> F["НФТ зафиксированы в критериях приёмки"] D5 -- нет --> F
Дерево не заменяет мышление, но снимает системную ошибку «забыли спросить». Пять минут на историю — и вы больше не узнаёте про требование к идемпотентности после инцидента с двойным списанием.
Качества, о которых забывают чаще всего
Производительность, надёжность и безопасность обсуждают почти всегда. Ниже — те, что регулярно выпадают и всплывают в самый неудачный момент.
Наблюдаемость. Это НФТ, и очень часто аналитик — единственный, кто его формулирует.
«Каждый запрос несёт 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.
Как добывать числа, когда «никто не знает»
Самая частая жалоба: «заказчик не может назвать цифру». Он и не должен уметь — это ваша работа. Пять источников по возрастанию усилий:
- Текущий прод. Если система работает, снимите метрики и покажите: «сейчас p95 = 1,8 с, мы предлагаем 400 мс». Обсуждать конкретное число в разы легче, чем абстрактное.
- Аналог или конкурент. Замерьте руками, сколько грузится тот же экран у конкурента. Аргумент «у них 600 мс, у нас 3 с» работает без объяснений.
- Пороги восприятия. Классика Нильсена (https://www.nngroup.com/articles/response-times-3-important-limits/): 0,1 с — ощущение мгновенности; 1 с — мысль не прерывается; 10 с — предел внимания. Отсюда берутся «до 1 секунды на действие в интерфейсе, свыше — с индикатором прогресса».
- Деньги. «Сколько стоит час простоя?» Ответ переводит требование к доступности из вкусовщины в расчёт: если час простоя — 400 000 руб., то разница между 99,9% и 99,95% стоит примерно 150 000 руб. в год, и её можно сравнить с ценой резервирования. Про метрики и деньги — метрики продукта.
- Лестница порогов. Когда всё остальное не помогает, спрашивайте не «сколько нужно», а «при каком значении вы позвоните мне ночью»: 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/). Архитектурная сторона вопроса на портале — в материалах по требованиям.
Чек-лист аналитика по НФТ
Перед тем как отдать требования в разработку, пройдите десять вопросов:
- Каждое числовое НФТ содержит все восемь слотов (объект, метрика, цель, условия, точка замера, инструмент, последствие, владелец)?
- Латентность указана перцентилями, а не средним, и число измерений достаточно для заявленной перцентили?
- Названы профиль нагрузки и объём данных, на котором требование проверяется?
- Есть горизонт («на 12 месяцев») и поведение при превышении ёмкости?
- Доступность определена как доступность сценария для пользователя, а не процесса для мониторинга; посчитана доступность цепочки?
- Описаны состояния деградации и переходы между ними?
- RPO/RTO заданы отдельно по типам данных и подкреплены учениями?
- Требования безопасности выведены из активов и угроз, а не из общих слов; есть матрица доступа и правила по ПД в логах?
- Наблюдаемость сформулирована как требование (correlation id, метрики, журнал переходов)?
- Приоритеты расставлены так, что «обязательных» не больше семи, и по каждому написано, что произойдёт при нарушении?
Мини-итог
- НФТ ломают архитектуру, а не спринт, поэтому спрашивают о них в начале, а не в конце.
- «Нефункциональное» — плохое слово; думайте «атрибуты качества» и ходите по ISO 25010 как по чек-листу.
- Проверяемое НФТ — восемь слотов: объект, метрика, цель, условия, точка замера, инструмент, последствие нарушения, владелец. Отсутствующие слоты становятся конфликтами.
- Производительность — это три величины (латентность, пропускная способность, ёмкость), и говорить о них надо перцентилями, а не средними.
- Один бюджет времени раскладывается по участкам пути; у каждого участка появляется хозяин и требование к смежникам.
- Надёжность считается арифметикой: минуты простоя, произведение доступностей в цепочке, бюджет ошибок. Отдельно описываются деградация, RPO и RTO — и подтверждаются учениями.
- Безопасность не пишется одной строкой: активы → угрозы → требования → проверки, плюс матрица доступа и правила по персональным данным.
- Числа добываются из прода, аналогов, порогов восприятия, денег и «лестницы боли»; «никто не знает» — не ответ, а начало работы аналитика.
- Формальный документ нужен там, где спор будет юридическим или дорогим; в остальном хватает четырёх строк в задаче и фото доски — но пороги, точка замера, последствие и владелец фиксируются всегда.
Источники
- ISO/IEC 25010 — модель качества продукта, восемь характеристик: https://iso25000.com/index.php/en/iso-25000-standards/iso-25010
- ISO/IEC/IEEE 29148 — требования к требованиям, в том числе проверяемость: https://www.iso.org/standard/72089.html
- Google SRE Book, Service Level Objectives — SLI/SLO/SLA и бюджет ошибок: https://sre.google/sre-book/service-level-objectives/; практика внедрения — SRE Workbook, Implementing SLOs: https://sre.google/workbook/implementing-slos/
- Bass, Clements, Kazman, Software Architecture in Practice — сценарии атрибутов качества и утилитарное дерево: https://www.oreilly.com/library/view/software-architecture-in-practice/9780136885979/
- SEI, ATAM — метод оценки архитектуры через атрибуты качества: https://insights.sei.cmu.edu/library/atam-method-for-architecture-evaluation/
- Wiegers, Beatty, Software Requirements (3rd ed.) — главы про качество и проверяемость: https://www.microsoftpressstore.com/store/software-requirements-9780735679665
- Volere Requirements Specification Template — каталог типов НФТ с шаблонами формулировок: https://www.volere.org/templates/volere-requirements-specification-template/
- Dean, Barroso, The Tail at Scale — почему хвост латентности определяет опыт: https://research.google/pubs/the-tail-at-scale/
- Gil Tene, How NOT to Measure Latency — coordinated omission и честные перцентили: https://www.infoq.com/presentations/latency-response-time/; HdrHistogram: http://hdrhistogram.org/
- Brendan Gregg, The USE Method — быстрая диагностика узких мест по ресурсам: https://www.brendangregg.com/usemethod.html
- Michael Nygard, Release It! (2nd ed.) — паттерны устойчивости и деградации: https://pragprog.com/titles/mnee2/release-it-second-edition/
- OWASP ASVS — готовый список проверяемых требований безопасности по уровням: https://owasp.org/www-project-application-security-verification-standard/
- Nielsen Norman Group, Response Times: The 3 Important Limits: https://www.nngroup.com/articles/response-times-3-important-limits/
- WCAG 2.2 — проверяемые критерии доступности: https://www.w3.org/TR/WCAG22/
- OpenSLO — формат описания SLO как кода: https://github.com/OpenSLO/OpenSLO; k6 thresholds — пороги, роняющие сборку: https://grafana.com/docs/k6/latest/using-k6/thresholds/
- BABOK v3, IIBA — НФТ в общей системе техник анализа: https://www.iiba.org/career-resources/a-business-analysis-professionals-foundation-for-success/babok/
Что дальше
Числа мы получили и записали так, что их можно проверить. Но остаётся вопрос, который никакая формулировка не закрывает: а верно ли само предположение, что людям нужна именно эта функция и именно в таком виде? Дешевле выяснить это до разработки — прототипом, экспериментом или сценарием, прогнанным на живом человеке.