Практика разработки
Как составить ТЗ на сложный веб-сервис: от бизнес-задачи до критериев приёмки
Плохое техническое задание часто выглядит вполне убедительно. В нём есть десятки страниц, таблицы, описание экранов, список функций и даже красивые схемы.
Проблема обнаруживается позже — когда разработчики начинают задавать вопросы.
Что происходит с заказом после отмены оплаты? Может ли менеджер изменить данные проекта после его согласования? Что увидит пользователь, если внешний API недоступен? Кто имеет право удалить файл? Нужно ли сохранять историю изменения статуса? Какой объём данных система должна выдерживать через два года?
Если ответы на такие вопросы появляются уже во время разработки, значит перед командой было не техническое задание, а расширенное описание идеи.
Для небольшого сайта это ещё может сработать. Для сложного веб-сервиса — почти всегда приводит к переделкам.
Разберём, из чего на самом деле должно состоять ТЗ на сложный веб-сервис и почему описание интерфейса — лишь небольшая часть этой работы.
Сначала определимся: что считать сложным веб-сервисом
Сложность определяется не количеством экранов.
Иногда приложение с десятью страницами архитектурно сложнее сайта с несколькими сотнями.
Сервис становится сложным, когда внутри появляются несколько ролей пользователей, связанные сущности, многоступенчатые процессы, интеграции, фоновые операции, права доступа, уведомления, платежи, документы, история изменений или требования к отказоустойчивости.
Например, B2B-система управления проектами может внешне выглядеть достаточно просто:
клиент создаёт заявку, менеджер её рассматривает, стороны обсуждают детали, проект принимается в работу, затем выполняется по этапам.
Но внутри одного такого сценария могут находиться десятки состояний и ограничений.
Именно поэтому хорошее ТЗ описывает не столько страницы, сколько поведение системы.
ТЗ начинается не с кнопок
Одна из распространённых ошибок — начинать документ с интерфейса:
На главной странице должна находиться кнопка «Создать проект».
Но эта фраза почти ничего не сообщает разработчику о самой системе.
Гораздо полезнее сначала зафиксировать бизнес-процесс:
Авторизованный клиент может создать новый проект. До отправки проект находится в состоянии «Черновик». Клиент может изменять его без ограничений. После отправки проект переходит в состояние «На рассмотрении», после чего исходные данные сохраняются как версия заявки.
Теперь появляется логика.
Из неё уже можно вывести интерфейс, API, модель данных и правила доступа.
Хорошее техническое задание движется именно в таком направлении:
бизнес-задача → процессы → данные → правила → интерфейс, а не наоборот.
Шаг 1. Зафиксировать цель системы
До описания функций стоит ответить на простой вопрос: зачем вообще создаётся продукт?
Формулировка «нужно разработать личный кабинет» почти бесполезна.
Формулировка:
Система должна сократить ручную обработку клиентских заявок и позволить клиенту самостоятельно отслеживать весь жизненный цикл проекта
— уже задаёт направление.
Цель помогает принимать решения позже.
Если функция не влияет на поставленную задачу и существенно увеличивает стоимость системы, её можно осознанно перенести в следующую версию.
Здесь же полезно определить измеримый результат.
Например:
| Показатель | Целевое состояние |
|---|---|
| Создание заявки | без участия менеджера |
| Передача файлов | внутри сервиса |
| Согласование изменений | с сохранением истории |
| Отслеживание проекта | в реальном времени |
| Уведомления | автоматически при важных событиях |
Так ТЗ постепенно превращается из описания продукта в модель будущей системы.
Шаг 2. Описать роли раньше экранов
Практически любой сложный веб-сервис имеет несколько типов пользователей.
Для каждой роли нужно определить не только доступные страницы, но и область полномочий.
Возьмём условную систему управления проектами.
| Действие | Клиент | Менеджер | Администратор |
|---|---|---|---|
| Создать проект | да | да | да |
| Изменить свой черновик | да | да | да |
| Изменить отправленную заявку | ограниченно | да | да |
| Назначить стоимость | нет | да | да |
| Изменить системные настройки | нет | нет | да |
| Просмотреть чужие проекты | нет | назначенные | да |
Такая таблица зачастую полезнее нескольких страниц текстового описания.
Она сразу показывает спорные точки.
Например: что значит «ограниченно»?
Именно здесь необходимо уточнение.
Возможно, после отправки клиент не может менять исходное ТЗ, но может создать запрос на изменение.
Это уже другая бизнес-сущность, другой API и другая таблица в базе данных.
Одно маленькое слово в таблице способно изменить архитектуру целого модуля.
Шаг 3. Описать жизненный цикл ключевых сущностей
У сложных систем почти всегда есть сущности, которые меняют состояние.
Проект может быть новым, находиться на рассмотрении, требовать уточнения, быть принятым в работу, приостановленным или завершённым.
Если эти состояния не определить заранее, логика начинает появляться прямо в коде.
Получается примерно следующее:
Новый
↓
На рассмотрении
├──→ Требует уточнения
│ ↓
│ На рассмотрении
│
├──→ Отклонён
│
└──→ Принят в работу
↓
Выполняется
↓
На согласовании
↓
ЗавершёнНо одной схемы недостаточно.
Для каждого перехода необходимо понимать три вещи: кто может его выполнить, при каком условии и какое событие возникает после перехода.
Например:
«На рассмотрении» → «Принят в работу»
Переход доступен менеджеру только после заполнения стоимости и предварительного срока. После перехода клиент получает уведомление, а в журнал аудита записываются старый статус, новый статус, пользователь и время изменения.
Вот это уже техническое требование.
Его можно реализовать и протестировать.
Шаг 4. Проектировать данные одновременно с функционалом
Функции и данные невозможно качественно описывать отдельно.
Если в ТЗ написано:
Пользователь может прикреплять файлы к проекту.
сразу возникает множество вопросов.
Какой максимальный размер файла? Какие форматы разрешены? Где хранятся файлы? Можно ли заменить файл? Нужно ли хранить предыдущую версию? Что произойдёт с файлом после удаления проекта? Кто имеет доступ по прямой ссылке? Нужно ли антивирусное сканирование?
Поэтому рядом с функциональным требованием должна появляться модель данных.
Для файла она условно может содержать:
File
- id
- project_id
- uploaded_by
- original_name
- storage_key
- mime_type
- size
- checksum
- status
- created_at
- deleted_atДаже такая простая модель сразу заставляет задуматься о вещах, которые невозможно увидеть на макете интерфейса.
Например, наличие checksum позволяет контролировать целостность и дубликаты, а deleted_at показывает, что системе, вероятно, требуется мягкое удаление вместо физического уничтожения записи.
Шаг 5. Функциональные требования должны описывать результат
Фраза:
Реализовать уведомления.
слишком абстрактна.
Лучше написать:
После изменения менеджером статуса проекта система создаёт внутреннее уведомление клиенту. Если у клиента включены email-уведомления, событие также добавляется в очередь отправки email. Повторная обработка события не должна приводить к созданию дубликата уведомления.
Теперь разработчик понимает не только необходимость функции, но и её ожидаемое поведение.
А тестировщик может проверить результат.
Хорошие функциональные требования отвечают минимум на следующие вопросы: кто инициирует действие, какие нужны исходные условия, что изменяется в системе и что пользователь получает в результате.
Шаг 6. Не забыть нефункциональные требования
Очень часто техническое задание подробно описывает возможности сервиса и почти ничего не говорит о качестве его работы.
Между тем именно нефункциональные требования часто определяют архитектуру значительно сильнее интерфейса.
Сравним два требования.
Первое:
Сервис должен быстро открываться.
Второе:
Для основных пользовательских API при штатной нагрузке p95 времени ответа должен оставаться ниже 500 мс, за исключением операций обработки файлов и запросов к внешним сервисам.
Второе требование можно измерить.
А значит — проверить.
Для серьёзного веб-сервиса стоит заранее рассматривать производительность, доступность, резервное копирование, безопасность, масштабирование, журналирование и мониторинг.
Если приложение работает с персональными или коммерчески чувствительными данными, должны быть отдельно определены правила хранения секретов, сессий, журналов аудита и резервных копий.
Шаг 7. API-интеграции описываются как ненадёжные
Почти любой современный сервис взаимодействует с другими системами.
Платёжные шлюзы, Telegram, email-провайдеры, CRM, карты, SMS, облачные хранилища, аналитика.
Ошибка — описывать интеграцию так:
После создания заказа отправить данные в CRM.
Внешний сервис может не ответить.
Может ответить через 30 секунд.
Может принять запрос, но вернуть ошибку.
Может обработать один и тот же запрос два раза.
Поэтому в ТЗ для каждой интеграции нужно определить поведение при сбоях.
Например:
После подтверждения заказа создаётся задача синхронизации с CRM. При сетевой ошибке выполняются повторные попытки с увеличивающимся интервалом. Повторная отправка одного события не должна создавать второй заказ во внешней системе.
Здесь появляется очень важное для распределённых систем понятие — идемпотентность.
Если внешний API или webhook может быть вызван повторно, система должна понимать, обрабатывала ли она это событие раньше.
Без такой логики одна временная ошибка сети способна превратиться в двойную оплату, два заказа или пять одинаковых уведомлений.
Шаг 8. Описывать нужно не только успешные сценарии
В макетах обычно показан идеальный мир.
Пользователь нажал кнопку — всё получилось.
В реальной системе значительная часть кода занимается ситуациями, когда всё пошло не по плану.
Рассмотрим загрузку документа.
В успешном сценарии:
выбор файла → загрузка → сохранение → файл появился в проектеНо возможны и другие ветки:
файл слишком большой
тип файла запрещён
соединение прервалось
объектное хранилище недоступно
антивирус отклонил файл
пользователь потерял права во время операции
такой файл уже загруженЧем важнее функция для бизнеса, тем подробнее должны быть описаны такие сценарии.
Хорошее ТЗ проектирует не только happy path, но и failure path.
Шаг 9. Критерии приёмки пишутся до разработки
Критерии приёмки — одна из самых недооценённых частей технического задания.
Без них фраза «функция готова» имеет слишком много трактовок.
Рассмотрим требование:
Клиент может отправить проект на рассмотрение.
Для него критерии приёмки могут выглядеть так:
| Условие | Ожидаемый результат |
|---|---|
| Обязательные поля заполнены | заявка отправляется |
| Не заполнено обязательное поле | отправка блокируется |
| Пользователь не авторизован | операция запрещена |
| Заявка уже отправлена | повторная отправка не создаёт копию |
| Отправка успешна | статус меняется на «На рассмотрении» |
| Статус изменился | создаётся запись истории |
| Менеджеру включены уведомления | создаётся уведомление |
Теперь понятие «готово» становится объективным.
Разработчик понимает границы реализации.
Тестировщик получает сценарии.
Заказчик понимает, что именно он принимает.
Шаг 10. Интерфейс описывается после логики
Только когда процессы, роли и состояния понятны, имеет смысл подробно описывать экраны.
И здесь тоже есть разница между макетом и ТЗ.
Макет показывает расположение элементов.
ТЗ объясняет их поведение.
Например, поле «Стоимость проекта».
Недостаточно написать, что оно находится справа от названия.
Гораздо важнее указать, кто может его редактировать, допускается ли нулевое значение, какая валюта используется, сохраняется ли история изменений, можно ли менять стоимость после принятия клиентом и что произойдёт с уже созданными документами при её изменении.
Визуальный дизайн отвечает на вопрос как выглядит.
Техническое задание — как работает.
Шаг 11. Для публичной части сразу учитывать SEO и доступность
Если у сервиса существуют публичные страницы, контентный раздел, каталог или документация, эти требования нельзя безболезненно «добавить потом».
Например, динамически создаваемая экспертная статья может требовать серверного HTML, собственного title, description, canonical URL, Open Graph, структурированных данных, Sitemap и корректной обработки удаления или изменения URL.
То же относится к доступности.
Форма, которую можно использовать только мышью, формально работает — но остаётся плохо реализованной.
Поэтому требования к семантической HTML-разметке, клавиатурной навигации, именам элементов формы и сообщениям об ошибках лучше определить до разработки компонентов.
Шаг 12. Продумать эксплуатацию ещё до запуска
Есть интересный момент: даже идеально работающий код ещё не означает готовый продукт.
Нужно понимать, как система будет жить после релиза.
Как выполнить миграцию базы данных? Что делать, если новая версия оказалась неисправной? Как восстановить данные из резервной копии? Где посмотреть ошибку? Как понять, что очередь фоновых задач перестала обрабатываться?
Для production-системы техническое задание должно затрагивать и эти вопросы.
Минимальная эксплуатационная схема обычно выглядит так:
Пользователь
↓
Web / API
↓
Приложение ───→ База данных
│
├────────→ Object Storage
│
├────────→ Queue / Background jobs
│
└────────→ Внешние API
↓
Logs + Metrics + AlertsМониторинг и резервное копирование — это не дополнительные функции «на потом».
Это часть продукта.
Что происходит, когда хорошего ТЗ нет
Обычно проблема проявляется не сразу.
На первом этапе кажется, что разработка даже идёт быстрее: никто не тратил несколько недель на анализ.
Но затем начинается накопление решений, принятых «по ходу».
Один разработчик считает, что удаление должно быть физическим. Другой реализует soft delete. В одном разделе пользователь может отменить операцию, в другом — нет. Один endpoint проверяет права на уровне проекта, другой доверяет ID из запроса.
Каждое отдельное решение может выглядеть разумно.
Проблема в отсутствии общей модели.
В результате сложность системы растёт быстрее количества функций.
Именно здесь появляется технический долг, который возник не из-за плохого программирования, а ещё до первой строки кода.
ТЗ не обязано быть огромным
Хорошее техническое задание не измеряется количеством страниц.
Иногда 40 страниц конкретных требований полезнее 300 страниц канцелярского текста.
Главный критерий — насколько документ уменьшает количество неоднозначных решений во время разработки.
Если разработчик читает требование и вынужден угадывать ожидаемое поведение системы, требование ещё не закончено.
Если тестировщик не понимает, как определить, что функция работает правильно, не хватает критериев приёмки.
Если заказчик и разработчик могут совершенно по-разному интерпретировать одну фразу, её необходимо уточнить.
Как может выглядеть структура ТЗ на сложный веб-сервис
Практичный документ обычно начинается с контекста продукта и заканчивается эксплуатацией.
| Раздел | Что фиксируется |
|---|---|
| Цели продукта | какую задачу решает система |
| Границы проекта | что входит и не входит в разработку |
| Термины | единый словарь предметной области |
| Пользовательские роли | права и ограничения |
| Бизнес-процессы | сценарии работы |
| Состояния сущностей | переходы и условия |
| Функциональные требования | поведение системы |
| Модель данных | сущности и связи |
| API | контракты взаимодействия |
| Интеграции | внешние системы и обработка ошибок |
| Интерфейс | экраны и поведение элементов |
| Безопасность | доступ, секреты, аудит |
| Нефункциональные требования | скорость, нагрузка, доступность |
| SEO и accessibility | требования публичной части |
| Критерии приёмки | проверяемый результат |
| Развёртывание | окружения и конфигурация |
| Мониторинг | логи, метрики, уведомления |
| Backup/restore | резервирование и восстановление |
Такой документ уже можно использовать не только как описание проекта.
Он становится общей точкой синхронизации для заказчика, аналитика, дизайнера, разработчика, тестировщика и человека, который через год будет сопровождать систему.
Главное свойство хорошего ТЗ — проверяемость
Есть простой способ проверить практически любое требование.
Нужно спросить:
можно ли однозначно написать тест, который подтвердит выполнение этого требования?
«Интерфейс должен быть современным» — нельзя.
«На экранах шириной 360 пикселей основные пользовательские сценарии выполняются без горизонтальной прокрутки» — можно.
«Сервис должен быть безопасным» — нельзя.
«После пяти неуспешных попыток входа для учётной записи вводится временное ограничение, а событие записывается в журнал безопасности» — можно.
«Страница должна быстро загружаться» — нельзя.
«Для заданного профиля нагрузки p95 ответа API не превышает установленное значение» — можно.
Чем больше в ТЗ проверяемых формулировок, тем меньше пространства остаётся для разного понимания результата.
Вместо заключения
Техническое задание на сложный веб-сервис — это не документ, который нужен только для того, чтобы разработчики поняли, какие страницы рисовать.
Это модель будущей системы.
Она описывает пользователей, данные, состояния, правила, исключения, интеграции, безопасность, нагрузку и способ определить, что результат действительно готов.
И самое ценное ТЗ делает ещё до начала разработки: заставляет обнаружить противоречия тогда, когда их исправление стоит несколько строк текста, а не несколько недель переделки работающей системы.
Поэтому хороший вопрос при подготовке проекта звучит не так:
«Все ли функции мы перечислили?»
Полезнее спросить:
«Достаточно ли точно мы описали поведение системы, чтобы двум независимым командам не пришлось додумывать его по-разному?»
Если ответ — да, техническое задание начинает выполнять свою настоящую работу.
Есть похожая задача?
Опишите продукт, интеграции и ограничения в брифе. До разработки зафиксируем объём, риски и критерии приёмки.