Системный и бизнес-анализ Анализ интеграций и API: контракты, форматы, сценарии обмена, ошибки
0%

Анализ интеграций и API: контракты, форматы, сценарии обмена, ошибки

Анализ интеграций и API: контракты, форматы, сценарии обмена, ошибки

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

Это статья про работу аналитика в точке стыка. Не про то, как выбрать между REST и gRPC — этот выбор делает архитектор, и подробно он разобран в стилях API. Наша тема другая: как превратить «мы интегрируемся с платёжкой» в описание, по которому можно писать код, писать тесты и спорить с партнёром, ссылаясь на документ, а не на память.

Предыдущие статьи трека дали инструменты: BPMN показал потоки сообщений между пулами, UML — sequence-диаграммы взаимодействия, моделирование данных — сущности и словарь, документирование — форму спецификации. Здесь всё это собирается в один артефакт и проверяется на прочность.


1. Почему интеграции — работа аналитика

Распространённое возражение: «контракт API пишут разработчики». Пишут — синтаксис. Но контракт состоит не только из синтаксиса, и всё остальное в нём — предметное:

  • Что означает «возврат оформлен» с точки зрения бухгалтерии — деньги ушли или заявка принята?
  • Какой срок ожидания приемлем для клиента в кабинете, а какой уже требует показать «в обработке»?
  • Что делать, если склад подтвердил приёмку, а платёжка отказала: заказ закрыт или открыт?
  • Какие поля обязательны юридически, а какие «желательно бы»?

Разработчик не знает ответов, а партнёр не обязан их угадывать. Аналитик — единственная роль, которая одновременно понимает бизнес-смысл операции и способна прочитать схему сообщения. Пять задач, которые на границе делает именно он:

  1. Инвентаризация: кто с кем обменивается, чем, как часто и кто владеет данными.
  2. Семантика: что значит каждое поле и каждый вызов на языке предметной области.
  3. Сценарии: не только happy path, а полный набор ветвей, включая «результат неизвестен».
  4. Контракт на ошибки: какой отказ что означает и что видит человек.
  5. Проверяемость: формулировки, которые можно проверить на приёмке, а не «должно работать надёжно».

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


2. Карта интеграций: с чего начинается анализ

Первый артефакт — не спецификация эндпоинта, а карта. Пока не видно всего ландшафта, любой контракт пишется в вакууме.

На карте сразу видно то, что в тексте незаметно: у нас три разных способа узнать судьбу возврата (вебхук, поллинг из CRM, часовой CSV со склада), и они рассинхронизированы по времени. Это будущий баг «в кабинете возврат готов, а в CRM ещё нет» — и он найден до разработки.

2.1. Паспорт интеграции

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

Поле паспорта Пример значения
Имя и код INT-04 «Возврат средств в PSP»
Стороны и роли consumer: сервис возвратов; provider: PSP
Владелец данных транзакция — PSP; заявка — мы
Направление и режим исходящий вызов, синхронный запрос + асинхронное уведомление
Стиль и транспорт HTTPS/REST, JSON; вебхук с подписью HMAC
Триггер оператор подтвердил возврат либо автоправило
Объёмы 3 000 вызовов в сутки, пик 5 rps, разовая догрузка до 20 000
Тайминги ответ ≤ 3 с (p99), финальный статус ≤ 24 ч
Идентификаторы наш return_id, их psp_refund_id, ключ идемпотентности
Ошибки каталог ERR-PSP-01…09, см. раздел 6
Среды и доступ test/stage/prod, mTLS, ключи в Vault, whitelist IP
Версия и владелец контракта PSP API v3, изменения — за 90 дней, контакт: их интеграционный менеджер
Что при недоступности заявка в статусе «ожидает», повтор по расписанию, ручной разбор через 24 ч

Паспорт занимает половину страницы и снимает 80 % будущих вопросов. Его ведут не «для галочки»: именно он превращается в раздел спецификации и в чек-лист приёмки (приёмка).

2.2. Правило одного владельца

Самая дорогая ошибка на карте — два владельца одной сущности. Если статус возврата может менять и наш сервис, и оператор в CRM, вы гарантированно получите расхождение и неразрешимый спор «чей статус правильный». Правило: у каждого атрибута ровно один источник истины, все остальные — реплики, и это фиксируется в паспорте. Из репликации сразу вырастают вопросы про допустимое отставание — это уже нефункциональные требования.


3. Контракт — это обещание, а не yaml-файл

Файл openapi.yaml — форма записи. Контракт — набор обещаний, которые стороны дают друг другу. Обещаний ровно восемь слоёв, и пропуск любого стоит денег.

Восемь слоёв интеграционного контракта

Слои полезно проходить именно в этом порядке: адрес и доступ (слои 1–2) блокируют разработку организационно, синтаксис и семантика (3–4) — содержательно, а слои 5–8 определяют, сколько вы будете страдать в проде.

Отдельно про асимметрию сторон. Кто диктует контракт, зависит от расстановки сил:

Ситуация Кто определяет контракт Что делает аналитик
Внешний вендор, платформа-гигант provider, менять нельзя читает их доку, ищет ограничения, проектирует обходы
Партнёр сопоставимого размера договариваются ведёт согласование, фиксирует протокол встреч
Наш внутренний сервис consumer сильно влияет пишет требования к API как обычные требования
Мы — provider для многих мы, но с обязательствами защищает стабильность контракта от «удобных» правок

Практический вывод: до чтения документации партнёра нельзя обещать бизнесу срок. Сначала выясняется, что вообще возможно в чужом API, потом обсуждается процесс. Обратный порядок — классический источник «мы пообещали синхронный возврат, а у них только асинхронный».


4. Стили обмена и вопросы к каждому

Стиль Когда естественен Что обязан спросить аналитик
REST/HTTP CRUD-подобные операции, внешние партнёры какие коды на что, идемпотентность, пагинация, фильтры, лимиты
gRPC внутренние сервисы, много вызовов, строгая схема эволюция proto, дефолты полей, дедлайны, кто владеет схемой
GraphQL много разных клиентов, гибкие выборки лимит сложности запроса, N+1 на бэкенде, кэширование, права на поля
SOAP/XML госсистемы, банки, легаси WSDL и его версия, XSD-валидация, подпись, кодировки
Вебхуки уведомления от внешней системы подпись, повторы, порядок, дедупликация, что если наш endpoint лежал
Брокер сообщений развязка сервисов, пики, события семантика доставки, порядок, DLQ, схема события, ретеншен
Файлы (SFTP/S3) выгрузки, реестры, банк-клиент, 3PL окно, имя файла, кодировка, порядок, повторная выгрузка, признак конца
Прямой доступ к БД почти никогда как узнаем об изменении схемы (ответ: никак) — искать альтернативу

4.1. Главный водораздел: синхронно или асинхронно

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

Признаки, что «синхронно» не получится, даже если очень хочется: в процессе есть ручная проверка; партнёр отвечает «примем в обработку»; операция затрагивает внешнюю платёжную инфраструктуру; в SLA партнёра фигурируют часы или рабочие дни. Асинхронность — не техническая деталь, а требование к интерфейсу и к процессу; подробнее про событийную модель — в event-driven архитектуре.

4.2. Файловый обмен: не устаревший, а специфический

Файлы никуда не делись: реестры платежей, выгрузки 3PL, обмен с госсистемами. Для них формулируются свои вопросы, которых нет в REST:

  • Имя файла: шаблон, дата в имени, часовой пояс даты, уникальность.
  • Окно доставки: во сколько появляется, до какого времени ждём, что если не пришёл.
  • Признак завершённости: флаг-файл *.ok, переименование, контрольная сумма — иначе прочитаете половину файла, который ещё пишется.
  • Порядок и повторы: файл за прошлый час прислали дважды — это дубль или корректировка?
  • Кодировка и разделитель: windows-1251 и ; — норма для российского финтеха, а не экзотика.
  • Что делать со «строкой-ошибкой»: отбраковать строку или весь файл?

4.3. Антипаттерн: интеграция через чужую БД

«Дайте нам read-only доступ к их базе» выглядит быстрым решением и создаёт связанность, которую невозможно контролировать: у чужой схемы нет контракта, нет версий и нет обязательств. Партнёр переименует колонку в пятницу — вы узнаете в субботу. Если такой обмен уже есть, задача аналитика — описать его как интеграцию (какие таблицы, какие поля, кто уведомляет об изменениях) и поставить в план замену на нормальный контракт.


5. Форматы и поля: где ломается на самом деле

5.1. Форматы

Формат Схема Сильная сторона Что важно аналитику
JSON JSON Schema (опционально) читаем, универсален нет типов дат и decimal — договариваться словами
XML XSD (обычно есть) строгая валидация, подписи namespace, атрибут vs элемент, пустой элемент ≠ null
Protobuf .proto (обязательна) компактность, строгая эволюция номера полей нельзя переиспользовать, дефолты вместо null
Avro схема в реестре эволюция схем в потоках правила совместимости в Schema Registry
CSV нет простота, объёмы кодировка, разделители, кавычки, экранирование, десятичный знак

5.2. Каталог полевых ловушек

Это самый прикладной список в статье: он ловит больше дефектов, чем любая другая техника.

  1. Дата и время. RFC 3339 с зоной или «10:00 по Москве»? Что значит «дата операции» — момент запроса, момент проводки или банковский день? Как передаётся дата без времени?
  2. Деньги. Минорные единицы (amount_minor: 149900) или дробь? decimal, а не float (0.1 + 0.2 ≠ 0.3). Валюта по ISO 4217 в каждом сообщении, правило округления, кто считает НДС.
  3. Отсутствие значения. Поля нет, поле null, пустая строка — три разных состояния. В PATCH разница критична: null может означать «удалить», а отсутствие — «не менять».
  4. Перечисления. Список закрытый или расширяемый? Что делает клиент с неизвестным значением — падает или обрабатывает как «прочее» (принцип tolerant reader)?
  5. Идентификаторы. Тип, длина, регистр; int64 в JSON ломается в JavaScript после 2^53 — значит, строка. Кто генерирует ID и в какой момент он появляется?
  6. Строки и локали. Длина в символах или байтах, юникод и эмодзи, телефон в E.164, транслитерация ФИО для банковских реестров.
  7. Массивы, числа, единицы. Предел размера, значимость порядка, смысл пустого массива; единица измерения и точность — «вес 0» это ошибка или «не измерено»?
  8. Размеры и пагинация. Лимит тела запроса, размер страницы по умолчанию и максимум, стабильность сортировки (иначе клиент увидит одну запись дважды).

Тот же контракт в двух видах — «как обычно приходит» и «как надо договориться»:

// ПЛОХО: три интерпретации на четыре поля
{
  "id": 90071992547409931,          // int64 — потеряет точность в JS
  "amount": 1499.9,                 // рубли? с копейками? float?
  "date": "16.07.2026 10:00",       // чей часовой пояс, какой день
  "status": "OK"                    // OK — принято или уже исполнено?
}

// ХОРОШО: каждое поле имеет одно чтение
{
  "refund_id": "rf_90071992547409931",
  "amount_minor": 149990,
  "currency": "RUB",
  "requested_at": "2026-07-16T10:00:00+03:00",
  "status": "processing",
  "status_final": false,
  "settlement_date": "2026-07-17"
}

5.3. Таблица маппинга полей

Рабочий артефакт аналитика на интеграции — не проза, а таблица соответствия. Её формат:

Наше поле Тип Поле партнёра Тип Правило преобразования Обязательность Что если пусто
return.id string(36) merchant_reference string(50) без изменений да ошибка валидации
return.amount decimal(12,2) amount_minor int64 ×100, округление HALF_UP да ошибка валидации
return.currency char(3) currency string ISO 4217, uppercase да подставить RUB
order.paid_at timestamptz original_payment_date date взять дату в TZ Europe/Moscow да искать по payment_id
customer.phone string customer_contact string привести к E.164 нет не передавать поле
reason_code enum(8) reason string по словарю MAP-RSN-01 да other + текст в comment

Три колонки, которые чаще всего забывают: правило преобразования, обязательность и что делать при пустом значении. Именно они превращают маппинг из картинки в проверяемое требование. Про качество данных на входе — data quality.

5.4. Соответствие идентификаторов: модель, которую забывают

Как только появляется внешняя система, появляется задача «связать наш объект с их объектом» — и её нужно смоделировать явно, отдельной сущностью, а не полем «на всякий случай».

Что читается из модели: ключ идемпотентности — наш атрибут, он существует до первого успешного ответа; psp_refund_id появляется позже и может не появиться никогда; event_id нужен, чтобы повторный вебхук не создал вторую запись; WMS_INTAKE необязателен (o|), значит бывают возвраты без подтверждённой приёмки — и надо описать, что с ними делать. Подробнее про мощности связей — моделирование данных.


6. Сценарии обмена: happy path — это 20 % работы

6.1. Обязательный набор сценариев

Для каждой интеграции описываются не «основной поток», а список ветвей. Минимальный набор:

  1. Успех синхронный.
  2. Успех асинхронный: приняли в обработку, финальный статус пришёл позже.
  3. Валидационный отказ партнёра (наши данные плохие).
  4. Бизнес-отказ партнёра (данные хорошие, операция невозможна).
  5. Таймаут: результат неизвестен.
  6. Повтор после таймаута: результат тот же, деньги не списаны дважды.
  7. Партнёр недоступен полностью: очередь, деградация, что видит пользователь.
  8. Дубль уведомления: тот же вебхук пришёл два раза.
  9. Уведомление не пришло вообще: срок ожидания и переход к сверке.
  10. Уведомления пришли не по порядку: failed после succeeded.
  11. Частичный успех: деньги вернули, склад не подтвердил.
  12. Компенсация: операцию нужно откатить, а откатить нечем.
  13. Сверка: ежедневное сопоставление наших записей с их реестром.

Первые четыре обычно есть в любой спецификации. Пункты 5–13 — то, из чего состоит эксплуатация. Если их нет в документе, они всё равно произойдут, просто в виде инцидентов.

6.2. Сценарий с таймаутом, идемпотентностью и вебхуком

Диаграмма — не украшение: она фиксирует четыре решения, которые в тексте обычно теряются. Ключ идемпотентности генерируем мы и до первого вызова. Ответ 202 пользователю означает, что в UI обязателен промежуточный статус. Дедупликация вебхука идёт по event_id, а не по «телу сообщения». И есть предел ожидания с явным действием после него. Механику гарантий доставки — в идемпотентности и семантике доставки.

6.3. Жизненный цикл с точки зрения обеих систем

Состояния — самое недооценённое место интеграционного анализа. Обычно рисуют наши статусы и забывают, что у партнёра свои, и что бывает состояние «мы не знаем».

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

6.4. Сверка как обязательная часть контракта

Любой асинхронный обмен деньгами или обязательствами требует сверки. Запрос, который аналитик может написать сам и приложить к спецификации как критерий:

-- Расхождения между нашими заявками и реестром PSP за сутки: пять классов,
-- у каждого своё действие. FULL JOIN, потому что «нет записи» — тоже расхождение.
WITH ours AS (
    SELECT r.return_id, r.status, r.amount_minor, l.psp_refund_id
    FROM   returns r LEFT JOIN refund_link l ON l.return_id = r.return_id
    WHERE  r.created_at >= current_date - INTERVAL '1 day'
), theirs AS (
    SELECT psp_refund_id, psp_status, amount_minor
    FROM   psp_settlement_report WHERE report_date = current_date - 1
)
SELECT o.return_id, o.status AS our_status, t.psp_status AS their_status,
       CASE
         WHEN t.psp_refund_id IS NULL          THEN 'нет в реестре партнёра'
         WHEN o.return_id IS NULL              THEN 'списание без нашей заявки'
         WHEN o.amount_minor <> t.amount_minor THEN 'расходятся суммы'
         WHEN o.status =  'DONE'               THEN 'мы закрыли, партнёр нет'
         ELSE                                       'партнёр исполнил, мы не знаем'
       END AS problem
FROM   ours o FULL JOIN theirs t ON t.psp_refund_id = o.psp_refund_id
WHERE  t.psp_refund_id IS NULL OR o.return_id IS NULL
   OR  o.amount_minor <> t.amount_minor
   OR  (o.status = 'DONE') <> (t.psp_status = 'succeeded');

Требование к сверке формулируется измеримо: «расхождений класса „партнёр исполнил, мы не знаем“ не более 0,1 % операций в сутки, все они закрываются в течение одного рабочего дня». Это уже проверяемое НФТ, и оно ведёт к следующей статье.


7. Ошибки: контракт на неуспех

7.1. Три уровня, которые нельзя смешивать

Уровень Примеры Кто обрабатывает Ответ пользователю
Транспорт DNS, TLS, connection refused, таймаут инфраструктура + ретраи «попробуйте позже», операция может быть выполнена
Протокол 400, 401, 404, 409, 422, 429, 503 код клиента по правилам зависит от кода, часто «мы починим»
Бизнес «карта закрыта», «лимит возврата исчерпан» продуктовая логика и UX понятный человеку текст и действие

Смешение уровней — типовой дефект. Бизнес-отказ, отданный как 500, заставляет клиента ретраить то, что никогда не выполнится. Валидационная ошибка, отданная как 200 с флагом в теле, ломает мониторинг: графики зелёные, деньги не возвращаются.

7.2. Тело ошибки

Единый формат ошибки — часть контракта. Стандарт есть: RFC 9457 Problem Details.

{
  "type": "https://api.psp.example/problems/refund-limit-exceeded",
  "title": "Refund limit exceeded",
  "status": 409,
  "detail": "Сумма возврата превышает остаток по исходному платежу",
  "instance": "/refunds/rf_91",
  "code": "ERR-PSP-07",
  "retryable": false,
  "remaining_minor": 32000,
  "trace_id": "01J8Z4K0T6"
}

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

7.3. Матрица ошибок — главный артефакт раздела

Код Что случилось Повтор Действие системы Текст пользователю
ERR-PSP-01 невалидный запрос нет лог + алерт разработчикам «Не удалось оформить, мы уже разбираемся»
ERR-PSP-03 нет прав / ключ истёк нет алерт дежурному, стоп интеграции «Возврат оформим вручную до 18:00»
ERR-PSP-05 карта закрыта нет предложить возврат на счёт «Карта недоступна, выберите другой способ»
ERR-PSP-07 лимит возврата исчерпан нет показать остаток «Доступно к возврату 320 ₽»
ERR-PSP-08 429, лимит запросов да, с backoff замедлить, поставить в очередь статус «в обработке»
ERR-PSP-09 503 / таймаут да, ≤ 2 раза, тот же ключ после исчерпания — в сверку статус «в обработке»

Матрица заполняется совместно с UX и поддержкой. Пустая клетка «текст пользователю» — это незакрытое требование, а не косметика: именно из этих клеток потом вырастает половина обращений в поддержку. Про формулировки в интерфейсе — UX и требования.

7.4. Бюджет таймаутов и повторов

Ретраи без согласованного бюджета создают ровно ту проблему, от которой должны спасать.

Бюджет таймаутов по цепочке вызовов

Три правила, которые аналитик обязан зафиксировать в спецификации:

  1. Таймаут снаружи больше суммы таймаутов и пауз внутри. Иначе внешний уровень оборвёт вызов посреди повтора и потеряет результат, который у партнёра уже есть.
  2. Ретраит ровно один уровень. Если ретраят три уровня по три раза, партнёр получит 27 запросов на одну операцию — это уже не отказоустойчивость, а самодиагностируемый DDoS.
  3. Повторяются только идемпотентные операции. Для остальных повтор запрещён, и вместо него — переход в состояние «результат неизвестен» с последующей сверкой.

Паттерны, стоящие за этими правилами (retry с экспоненциальной задержкой и джиттером, circuit breaker, bulkhead), разобраны в resilience-паттернах. Аналитик не выбирает библиотеку, но именно он приносит цифры: сколько ждёт пользователь, сколько стоит лишний вызов, что важнее — быстрый отказ или отложенное исполнение.


8. Что чаще всего ломается в анализе интеграций

8.1. Неявные требования

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

  • А если ответа нет — сколько ждём и что делаем потом?
  • А если то же сообщение придёт дважды?
  • А если сообщения придут не по порядку?
  • А если партнёр пришлёт новое значение статуса, которого нет в нашем списке?
  • А кто узнаёт, что интеграция сломалась, и через сколько минут?
  • А что происходит с уже начатыми операциями во время нашего релиза?
  • А как выглядит первый запуск: нужно ли грузить историю и за какой период?
  • А как это выключить, если партнёр начнёт присылать мусор?
  • А кто платит за лишние вызовы и есть ли лимит по деньгам?
  • А что мы обязаны залогировать, и можно ли писать в лог номер карты (нет)?

Последний пункт — про безопасность API и PII: маскирование, срок хранения, состав аудита. Это тоже требования, и их формулирует аналитик.

8.2. Противоречия между стейкхолдерами

Интеграция — место, где интересы сталкиваются лбами, потому что за каждой стороной стоит своя метрика. Живой пример из процесса возврата:

Стейкхолдер Что хочет Почему С чем конфликтует
Финансы возврат только после физической приёмки товара потери от мошенничества скоростью возврата для клиента
Клиентский сервис мгновенный возврат по нажатию NPS и нагрузка на поддержку требованием финансов
Платформенная команда только асинхронный вызов SLA партнёра — часы обещанием «мгновенно» в UI
Юристы письменное согласие на передачу данных в 3PL требования по ПД простотой сценария

Такое противоречие не решается голосованием и тем более не решается тем, что аналитик выберет сам. Рабочая последовательность: сделать конфликт видимым (эта таблица уже половина решения), свести к количественной постановке (возврат до приёмки для сумм до 3 000 ₽ даёт X потерь и Y экономии на поддержке), найти решение, устраняющее сам конфликт (мгновенный возврат в лимите суммы, полный — после приёмки), зафиксировать решение с автором и датой. Техники согласования — работа со стейкхолдерами.

Отдельный вид противоречия — между документом и реальностью. Партнёр присылает спецификацию v3, а прод отвечает как v2. Правило: контракт проверяется на реальном трафике. Пятнадцать сохранённых ответов из тестовой среды стоят дороже сорока страниц документации.

8.3. «Хотелки» без задачи

Три формулировки, которые звучат как требования, но требованиями не являются:

  • «Хотим real-time». Вопрос: какое решение принимается на основании этих данных и как быстро? Часто выясняется, что отчёт смотрят утром, и обновления раз в час достаточно. Разница в цене — порядок.
  • «Отдайте нам всё через один эндпоинт». Вопрос: какие поля используются в каких экранах? Обычно нужны шесть полей из восьмидесяти, а «всё» появилось из страха что-то не получить — и создаёт нагрузку, риск утечки PII и невозможность эволюции контракта.
  • «Пусть будет как в предыдущей системе». Вопрос: какую задачу решала та схема? Часто она решала задачу, которой уже нет, и переносится вместе с легаси-ограничениями.

Приём один и тот же: подняться от решения к задаче. Не «зачем вам real-time», а «какое решение вы принимаете и что произойдёт, если данные будут на час старше». Ответ либо даёт настоящее требование, либо снимает хотелку. Подробнее про эту лестницу — в видах требований.

8.4. Требования, которые невозможно проверить

Непроверяемая формулировка — это отложенный спор на приёмке. Переписывать нужно до разработки:

Непроверяемо Проверяемо
«Интеграция должна быть надёжной» «При недоступности PSP заявки не теряются: 100 % попадают в очередь и исполняются в течение 30 минут после восстановления»
«Ответ должен приходить быстро» «p95 ответа POST /refunds ≤ 800 мс, p99 ≤ 3 с при 5 rps»
«Данные должны быть актуальными» «Статус в CRM отстаёт от источника не более чем на 60 с в 99 % случаев»
«Ошибки должны обрабатываться корректно» «Для каждого кода из ERR-PSP-01…09 есть определённое действие системы и текст пользователю (матрица в разделе 7.3)»
«Система должна быть защищена» «Все вызовы по mTLS, вебхуки с подписью HMAC-SHA256, отклонение неподписанных с 401, ключи ротируются раз в 90 дней»
«Нужна поддержка больших объёмов» «Разовая догрузка 20 000 заявок за окно 2 ч без превышения лимита партнёра 10 rps»

Тест на проверяемость простой: можно ли придумать процедуру, которая даст ответ «да» или «нет»? Если нет — это не требование, а пожелание, и на приёмке оно превратится в конфликт (приёмка).


9. Формальный документ или схема на доске

Формальность стоит денег: её пишут, согласовывают, поддерживают в актуальном состоянии. Платить эту цену нужно там, где она возвращается.

Достаточно доски и разговора Обязателен формальный документ
два разработчика одной команды и общий репозиторий обмен между компаниями, тем более с деньгами
внутренний сервис, который можно поменять за день госсистемы, банки, регулируемые данные
эксперимент, который выключается флагом контракт, на который завязаны несколько потребителей
уточнение существующего поля всё, что попадает в договор или SLA
прототип для проверки гипотезы (прототипирование) интеграция, которую будут поддерживать другие люди через год

Важная тонкость: «доска» — не отсутствие артефакта. Минимум, который остаётся после разговора: фотография схемы в задаче, два-три примера реальных запросов и ответов в тестах, запись решения в один абзац. Это дёшево и переживает отпуск автора. А вот обратная ошибка дороже: сорок страниц спецификации на интеграцию из двух вызовов между своими сервисами — документ, который устареет к первому релизу и которому перестанут верить.

Минимальный формальный набор, если решение «нужен документ»: машиночитаемая схема (OpenAPI/AsyncAPI) в репозитории провайдера как источник истины; паспорт интеграции; матрица ошибок; список сценариев из раздела 6.1 с ожидаемым поведением; таблица маппинга полей. Проза вокруг — только там, где она добавляет смысл, которого нет в схеме.

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

# openapi.yaml — фрагмент. Аналитическая часть: идемпотентность и контракт на ошибки.
paths:
  /v1/refunds:
    post:
      summary: Создать возврат средств по исходному платежу
      parameters:
        - name: Idempotency-Key      # обязателен: повтор не должен вернуть деньги дважды
          in: header
          required: true
          schema: { type: string, maxLength: 64 }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [merchant_reference, amount_minor, currency, reason_code]
              properties:
                merchant_reference: { type: string, maxLength: 50 }
                amount_minor:       { type: integer, format: int64, minimum: 1 }
                currency:           { type: string, pattern: "^[A-Z]{3}$" }
                reason_code:        { type: string, enum: [defect, wrong_item, refused, other] }
      responses:
        "202":
          description: Принято, финальный статус придёт вебхуком в течение 24 ч
        "409":
          description: Тот же ключ с другим телом либо лимит возврата исчерпан
          content:
            application/problem+json:                     # RFC 9457
              schema: { $ref: "#/components/schemas/Problem" }
        "429":
          description: Лимит запросов; повтор после Retry-After с тем же ключом
          headers:
            Retry-After: { schema: { type: integer } }

Для событий и вебхуков роль OpenAPI играет AsyncAPI — тот же приём, другая спецификация:

# asyncapi.yaml — фрагмент. Событие о результате возврата.
channels:
  return.refunded:
    description: Публикуется один раз на возврат; получатель обязан дедуплицировать по event_id
    subscribe:
      message:
        headers:
          type: object
          required: [event_id, occurred_at, schema_version]   # ключ дедупликации и версия схемы
          properties:
            event_id:       { type: string }
            occurred_at:    { type: string, format: date-time }
            schema_version: { type: integer, minimum: 1 }
        payload:
          type: object
          required: [return_id, order_id, amount_minor, currency]

Дальше схема оживает: линтер стиля (Spectral), мок для проверки сценария до разработки (Prism), контрактные тесты между потребителем и поставщиком (Pact). Аналитику достаточно уметь поднять мок и прогнать по нему сценарии — этого хватает, чтобы найти дефекты контракта за час вместо спринта. Про проверку контрактов подробно — тестирование API и интеграционное тестирование.

Полезная привычка: проверять реальные ответы партнёра на соответствие заявленной схеме, а не верить документации.

"""Сверка 20-30 сохранённых ответов партнёра с заявленной JSON Schema.
Запускает аналитик до начала разработки: находит расхождения документации и поведения."""

import json
from pathlib import Path
from jsonschema import Draft202012Validator

validator = Draft202012Validator(
    json.loads(Path("schemas/refund_response.json").read_text(encoding="utf-8")))

samples = sorted(Path("samples").glob("*.json"))
for sample in samples:
    payload = json.loads(sample.read_text(encoding="utf-8"))
    # Собираем ВСЕ нарушения, а не первое: важна картина в целом.
    errors = [f"{'.'.join(map(str, e.path)) or '<root>'}{e.message}"
              for e in validator.iter_errors(payload)]
    if errors:
        print(sample.name, *(f"  - {e}" for e in errors), sep="\n")

Типичный результат первого запуска: поле, объявленное обязательным, отсутствует в трети ответов; status содержит значение, которого нет в документации; сумма приходит строкой. Каждая находка — вопрос партнёру до кода, а не багрепорт после релиза.


10. Чек-лист интеграции

Перед тем как отдать интеграцию в разработку, пройдите по списку. Каждый пункт — либо ответ в спецификации, либо явно записанное допущение с владельцем.

Контекст. Зачем эта интеграция бизнесу · кто владелец данных · что будет, если её не делать · кто со стороны партнёра принимает решения · есть ли договор и SLA.

Доступ. Адреса всех сред · кто заказывает доступы и за сколько дней · схема аутентификации · где хранятся секреты · ротация ключей · ограничения по IP.

Данные. Схема сообщений · маппинг полей с правилами преобразования · форматы даты, денег, телефонов · словари и что делать с неизвестным значением · обязательность и поведение при пустом · PII и маскирование в логах.

Семантика. Что именно делает каждый вызов · какие побочные эффекты · единица работы · идемпотентность и её ключ · кто генерирует идентификаторы.

Сценарии. Все 13 ветвей из раздела 6.1 · первичная загрузка истории · поведение во время нашего релиза и релиза партнёра · как выключить интеграцию.

Ошибки. Матрица кодов с действием и текстом · что повторяемо · бюджет таймаутов и повторов · кто узнаёт об аварии и через сколько · процедура сверки и допустимый уровень расхождений.

Качество. Объёмы и пики · лимиты партнёра · требования к задержке и доступности · окна регламентных работ · нефункциональные требования.

Эволюция. Версионирование · правила обратной совместимости · срок депрекейта · как партнёр уведомляет об изменениях · как мы уведомляем своих потребителей.


Мини-итог

  • Граница систем — место с максимальной плотностью потерянных требований; работать там обязан аналитик, а не только разработчик.
  • Анализ начинается с карты интеграций и паспорта на каждую стрелку, а не со спецификации метода.
  • Контракт — восемь слоёв: адрес, доступ, синтаксис, семантика, ошибки, сценарии, качество, эволюция. Незакрытый слой всегда всплывает в проде.
  • Больше всего дефектов дают не «сложные» вещи, а даты, деньги, null, перечисления и идентификаторы. Таблица маппинга с правилами преобразования — самый дешёвый способ их поймать.
  • Happy path — пятая часть работы. Таймаут с неизвестным результатом, дубли, нарушенный порядок, частичный успех и сверка описываются наравне с успехом.
  • Ошибка — часть контракта: стабильный код, признак повторяемости, действие системы и текст пользователю. Ретраи без согласованного бюджета таймаутов делают хуже, чем их отсутствие.
  • Формальный документ нужен там, где высока цена ошибки и длинный срок жизни: другая организация, деньги, ПД, много потребителей. Внутри команды достаточно схемы на доске, примеров в тестах и абзаца с решением — но не «ничего».
  • Непроверяемое требование на интеграции — гарантированный спор на приёмке. Переписывайте до разработки.

Источники

Что дальше

Разбирая интеграции, мы всё время натыкались на цифры: таймауты, объёмы, допустимое отставание реплики, доля расхождений в сверке, срок депрекейта. Это уже не функциональные требования — это требования к качеству, и у них своя техника: выбор характеристик, метрик, целевых значений и, главное, способов измерения.

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

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

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

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

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