Как я спас релиз от юркошмара: legal‑by‑design для веб‑разработчиков без лишней бюрократии

Пятница, 21:15. В офисе пахнет пережаренным кофе, за окном мокрый снег, а у нас через 40 минут релиз промо‑лендинга. В слэке пингуют дизайнеров, продакта несёт к принтеру какую‑то бумагу (да, это ещё те времена, весна 2022‑го), и тут на общий ящик приходит претензия на 120 800 ₽ за баннер с «редакционной» лицензией стока. Баннер уже уехал в прод, пиксели пляшут. Менеджер, который «проверил права», краснеет как статус билда. Я материться не люблю — просто открыл Terminal и начал катить откат. Холодный пот и лёгкая тошнота прилагаются.

Смешно, но это был не первый звоночек. За месяц до этого нам прилетела жалоба на автоподписку: галочку «согласен с офертой» спрятали под кнопку, и люди жали «Оплатить», не видя текста. В поддержку за три дня упало 47 сообщений, конверсия в оплату просела на 2,1%, а маркетинг вертел у виска. Короче, мы делали отличный фронт и честный бэк, а правовая часть жила где‑то в чатике «юристы, посмотрите потом».

После той пятницы я написал в тетрадь с котиками: «Больше так не работаем». Не потому что «страшно», а потому что это тупо: чинить юридические дыры после деплоя дороже и скучнее, чем заложить правила в процесс. Называть это legal‑by‑design модно, но по сути — обычная инженерная дисциплина: чек‑листы, шаблоны, логи, фичефлаги.

Что именно сломалось и как это починили

Разобрали инцидент по косточкам утром в понедельник. Выяснилось: у баннера лицензия «Editorial Use Only», а мы использовали его в коммерческой акции; у страницы оплаты оферта была, но доступ к ней открывался из микроскопической ссылки, которую мобильные пользователи не видели; логи согласий писались где‑то в аналитику, а не в нашу базу, и мы не могли быстро доказать, что человек ставил галочку. Классика «потом разберёмся».

Я сел и переписал наш Git‑флоу. В pull request появился обязательный раздел «Право и данные»: ссылка на текст оферты в конкретной версии, скриншот модалки согласия на целевом брейкпоинте, список внешних медиа с id лицензии и сроками действия, чек про куки (ttl, категория), и короткий ответ разработчика, где лежат логи согласий и retention. Код без этого блока нельзя смёржить — CI ловит и вешает красный флажок. На то, чтобы собрать шаблон, ушло 2 часа; плюс ещё 40 минут на интеграцию в репо темплейтов.

Параллельно сделали маленькую библиотеку «legal‑ui»: стандартная модалка оферты с нормальным скроллом, фокус‑ловушкой и кнопкой «Скачать PDF», компонент чекбокса с обвязкой для фиксации event в бэке (timestamp, IP, user‑agent, версия оферты). На бэке добавили таблицу consent_log с хешем «пользователь+версия+время», чтобы мгновенно поднимать доказуху. Оверхед на рендер — 6–8 мс, никто не умер.

Самое больное — контент. Мы завели licenses.json в репозитории c полями: source, license_type, allowed_use, expire_at. При сборке фронт проверяет, не прошёл ли expire_at; если до окончания осталось меньше 72 часов — падает ворнинг в Slack и бьётся флажок в админке. Как бонус — единая панель, где видно, какие картинки из каких источников и кто их подтаскивал. В первом же спринте нашли 11 изображений без источника, и дизайнер тихо побледнел.

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

Что это дало по цифрам

Три недели спустя у нас был очередной маркетинговый релиз — попапы, лендинги, рассылки. Юр‑тикетов в Jira упало с 15 до 11 за спринт (минус 27%). Среднее время на ревью PR уменьшилось с 1:34 до 1:18 — просто потому что вопросы стали предсказуемыми и видны сразу. Поддержка отметила снижение жалоб на «не видел оферту» с 13 до 3 в первые 7 дней кампании. Конверсия в оплату на мобильных выросла с 2,6% до 2,9% — мелочь, а приятно: модалка стала читабельной и не лагает.

Сколько стоило. На всё уйдёт меньше, чем кажется: 12 часов фронта, 6 часов бэка, 3 часа дизайна, 2 часа легала на ревью текстов и кейсов. Итого одна неделя человека растянутая тонким слоем по спринту. Счёт за ту претензию мы в итоге сбили до 38 500 ₽ и извинений — просто потому что быстро показали логи и убрали картинку в тот же вечер. Нервов, правда, потратили гораздо больше.

Мини‑чек‑лист, который мы используем сейчас

Перед мёрджем: в PR есть ссылка на версию оферты и скрин модалки на всех брейкпоинтах; для каждого внешнего медиа указан источник, тип лицензии и срок; куки размечены по категориям и есть ttl; consent_log пишет timestamp, IP, user‑agent и версию документа, а доказательство достаётся одним SQL‑запросом; в UI нет «по умолчанию согласен»; оплату без явного согласия не пропускают тесты.

После деплоя: в админке видно, где истекают права на медиа ближайшие 7 дней; есть быстрый экспорт PDF‑версии оферты на момент согласия; на мобильных оферта открывается без «адского» скролла; доступность сохранена — фокус не теряется, aria‑метки на месте. Раз в квартал легал чекает тексты, а мы просто подтягиваем версию компонента.

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

Обсуждение закрыто.