Appearance
Премортем: DEV-402 geo address flow frontend
Контекст
- Предмет: Фронт-эпик DEV-402 — миграция геолокации partizap.ru с ручного справочника (СПб + Москва) на полную РФ через ГАР (85 регионов, ~1100 городов, дома); 4 сценария фронта: форма продавца с автокомплитом, шапка/фильтр с иерархическим picker'ом, IP-баннер на главной, расширение admin-references; зависит от бэк-эпиков DEV-379 (импорт ГАР) и DEV-380 (suggest API + GeoProvider абстракция)
- Аудитория: продавцы автозапчастей (вендоры размещают карточки) и покупатели на partizap.ru
- Успех: все 4 сценария работают + 0 регрессий в существующих фичах + полное прохождение manual regression checklist
Дыры
H-001: Принудительный перевыбор адреса ломает legacy-вендоров без миграции
- Угол зрения: Клиент
- Найдена: 2026-06-01 (запуск 1)
- Важность: высокая
- Уверенность: подкреплено контекстом
- Статус: ОТЛОЖЕНО
- Режим: краткий
- Описание: План блокирует submit редактирования legacy-карточек до выбора адреса через autocomplete, но не описывает массовую миграцию старых карточек. Активный продавец с 30-50 карточками, открыв одну на редактирование, обнаруживает блокирующее требование «выбрать адрес заново» — для каждой карточки.
- Решение: Перевыбор остаётся как в плане; добавляется триггер-условие на bulk-миграцию
- Почему: На момент 2026-06-01 база реально пуста (юзеров нет), перевыбор 1-3 карточек на продавца не блокер. Решение зафиксировано на текущем снапшоте состояния.
- Первый шаг: Зафиксировать в спеке: если на момент мержа DEV-380 в проде > 50 продавцов с legacy-продуктами, перед релизом фронт-эпика запускается bulk-миграция (вариант А) как отдельный таск.
- Исполнитель: оба
H-002: Свой suggest на pg_trgm/Meilisearch проиграет DaData по качеству ввода
- Угол зрения: Конкурент
- Найдена: 2026-06-01 (запуск 1)
- Важность: высокая
- Уверенность: требует проверки
- Статус: ПРИНЯТО
- Режим: полный
- Описание: Avito/Авто.ру/Drom используют DaData или эквивалент с обработкой опечаток, сокращений, синонимов и народных названий. Свой suggest на ГАР+pg_trgm без runtime DaData даёт заметно худшие подсказки: «Питер», «СПб», «Сосновый бор» (без пробела), «Мск» не найдутся. Продавец сравнит и уйдёт.
- Решение: Acceptance-критерий качества suggest в контракте бэка + gold-set 50 типичных запросов
- Зачем выбрали: Ставим планку в SLA до старта работ, не пытаемся починить кривое позже
- Первые шаги (prevent):
- Сформировать gold-set из 50 типичных запросов (Питер/СПб/Мск/Спб/опечатки/сокращения ул./пр-кт/мкр/б-р/р-н)
- Добавить требование в openapi.yaml: словарь синонимов на бэке + fuzzy ≥ 0.7 на pg_trgm или typo-tolerance в Meilisearch
- Manual prove на dev-стенде до мержа фронт-эпика: каждый запрос gold-set возвращает релевантный fias_id в топ-3
- Исполнитель: оба
- Сигнал успеха (detect): 50/50 запросов gold-set дают релевантный топ-3 на dev-стенде; после релиза метрика «% suggest-запросов с пустым ответом» < 5%
- Стоп-условие: Бэк не выходит на gold-set качество → откат к варианту Б (возврат DaData fallback после 2 пустых ответов своего suggest)
- Если уже случилось (limit damage): После релиза собирать метрику пустых suggest по запросам; при > 5% — escalation на расширение синонимического словаря
- Открытые вопросы:
- Словарь синонимов хранится в БД или в коде бэка?
- Кто отвечает за поддержку gold-set после релиза?
H-003: Координаты домов для гибрида метро не определены в плане
- Угол зрения: Исполнитель
- Найдена: 2026-06-01 (запуск 1)
- Важность: высокая
- Уверенность: подкреплено контекстом
- Статус: ПРИНЯТО
- Режим: полный
- Описание: Сценарий 1 предполагает автозаполнение ближайшей станции метро по выбранному дому. План фронта требует координаты дома в /store/geo/resolve, но backend-скоуп (DEV-379/380) описан как ГАР-импорт без явного геокодинга домов. ГАР не содержит координат уровня здания — нужен отдельный batch-геокодинг через DaData/OSM/Nominatim, которого нет в видимых тикетах.
- Решение: Отдельный backend-таск — batch-геокодинг домов (OSM Nominatim self-host или DaData геокод-API), таблица houses.coords
- Зачем выбрали: Убираем скрытую зависимость, делаем её явной частью эпика. Без координат гибрид метро не запустится и сценарий 1 деградирует до ручного выбора.
- Первые шаги (prevent):
- Создать YouTrack-таск как subtask DEV-402 на batch-геокодинг домов; DEV-380 → blockedBy на него
- Выбрать источник OSM Nominatim self-host vs DaData геокод-API на основе budget + квоты + качества покрытия малых городов
- Фронт-спека: /store/geo/resolve обязан возвращать coords дома и предзаполненный metro_station_id если станция < 1500m
- Исполнитель: оба
- Сигнал успеха (detect): Для топ-10 случайных домов в МСК и СПб гибрид-метро на dev-стенде предзаполняется верной станцией (manual проверка по карте)
- Стоп-условие: Геокодинг не уложится в сроки или качество покрытия низкое → откат к варианту В (метро вручную в этой версии, гибрид в следующий эпик)
- Если уже случилось (limit damage): Релиз без авто-метро: баннер «Уточните метро вручную» в продуктовой форме; метрика «% продавцов выбравших метро» — если падает > 30%, гибрид P0 на следующий цикл
- Открытые вопросы:
- Достаточно ли покрытия OSM в малых городах РФ?
- Стоимость и time-to-bootstrap self-host Nominatim для всей РФ?
H-004: Debounce 250ms заложен без SLO на suggest от бэка
- Угол зрения: Допущения
- Найдена: 2026-06-01 (запуск 1)
- Важность: высокая
- Уверенность: подкреплено контекстом
- Статус: ПРИНЯТО
- Режим: полный
- Описание: useGeoSuggest зафиксирован на debounce 250ms + LRU 20 — работает только при P95 ответа suggest < 200ms на полном ГАР. Выбор Postgres pg_trgm vs Meilisearch открыт в DEV-380, SLO в контракте не зафиксирован, нагрузочных тестов на полный ГАР в плане нет. При P95 800мс-2с автокомплит «прыгает», AbortController не спасает от ощущения «не работает».
- Решение: SLO в контракте: P95 ≤ 200ms, P99 ≤ 500ms на /store/geo/suggest при 100 RPS; нагрузочный тест бэка до мержа фронт-эпика
- Зачем выбрали: Фронт-debounce 250ms работает только при этом SLO; без зафиксированного контракта UX ломается на ровном месте
- Первые шаги (prevent):
- Добавить SLO в openapi.yaml для /store/geo/suggest
- Согласовать с бэком инструмент (k6 или wrk) и сценарий нагрузочного теста: gold-set queries × 100 RPS × 5 мин
- Acceptance до мержа фронт-эпика: pass нагрузочного теста на dev-стенде с evidence в отчёте
- Исполнитель: оба
- Сигнал успеха (detect): P95 ≤ 200ms на k6-репорте; продакшен-метрика в Sentry/Grafana держит SLO 7 дней подряд после релиза
- Стоп-условие: Бэк не выходит на P95 500ms даже с Meilisearch → откат к варианту В (debounce 400ms + skeleton-row в dropdown)
- Если уже случилось (limit damage): Хотфикс: адаптивный debounce (мерим P95 последних 20 запросов, подстраиваем 150-600ms); оптимизация индекса как P1
- Открытые вопросы:
- Работает ли pg_trgm на полном ГАР (~30M строк) или сразу Meilisearch?
- Где хранится Meilisearch контейнер на инфраструктуре?
H-005: Suggest-API без rate-limit становится бесплатным ГАР-as-a-Service для скрейперов
- Угол зрения: Противник
- Найдена: 2026-06-01 (запуск 1)
- Важность: высокая
- Уверенность: подкреплено контекстом
- Статус: ПРИНЯТО
- Режим: полный
- Описание: Эндпоинт /store/geo/suggest публичный (нужен в форме продавца и в picker'е гостевого каталога), вызывается с каждой клавиши, отдаёт структурированный ГАР с fias_id. В плане нет rate-limiting, токена сессии, CAPTCHA, ограничения глубины. Конкуренты и скрейперы выкачивают весь нормализованный справочник; DDoS на suggest положит каталог и форму продавца одновременно — единая точка отказа.
- Решение: Rate-limit на /store/geo/suggest: 60 req/min/session + 600 req/min/IP; обязательная cookie PARTIZAP_SESSION (HTTP-only, выдаётся гостям при первом GET страницы)
- Зачем выбрали: Скрейпинг невыгоден, DDoS гасится на уровне приложения, фронт ничего не меняет (cookie всегда есть после SSR)
- Первые шаги (prevent):
- В спеку фронта добавить раздел Backend dependencies: rate-limit policy + anon session cookie
- Согласовать с бэком расширение DEV-380 на anon-session middleware + rate-limit middleware
- Acceptance до релиза: load test 1000 RPS не валит сервис, при превышении лимита 429 с Retry-After
- Исполнитель: оба
- Сигнал успеха (detect): На проде: 0% 5xx на /store/geo/suggest, % 429 ответов < 0.1% от нормальных юзеров
- Стоп-условие: Бэк отказывается от rate-limit → откат к варианту Г (Cloudflare-level защита уровня LB)
- Если уже случилось (limit damage): При DDoS экстренно прячем suggest за более жёсткий middleware, прозрачно для фронта; при утечке справочника — обращение в правовую плоскость не требуется (ГАР публичен), но usage-метрики дают раннее предупреждение
- Открытые вопросы:
- Rate-limit state хранится в Redis или DB?
- Анонимная сессия создаётся на SSR (Nuxt) или на первом API-запросе?
H-006: GeoProvider абстракция фиктивна — fias_id размазан по фронту
- Угол зрения: Будущий поддерживающий
- Найдена: 2026-06-01 (запуск 1)
- Важность: высокая
- Уверенность: требует проверки
- Статус: ПРИНЯТО
- Режим: полный
- Описание: План декларирует GeoProvider абстракцию, но fias_id уходит в payload POST /vendor/products как address_fias_id, в legacy-флоу, в IP-баннер, в resolve-схему. Через 8-12 месяцев захочется добавить КЛАДР-fallback, вернуть DaData для метро, или OSM для радиусного поиска — fias_id нельзя выкинуть из БД (legacy продукты + обязательное поле формы). Абстракция превращается в фикцию. WYSIATI: нет голоса бэкенд-инженера / ГИС-архитектора в комнате — фронт фиксирует fias_id в публичном контракте до подтверждения, что абстракция реально позволит подменить источник.
- Решение: Internal partizap_address_id (UUID, генерируется бэком при resolve) как ключ адреса в нашей БД и в продукте; fias_id хранится рядом в таблице addresses как метаданные источника; payload фронта — address_id
- Зачем выбрали: GeoProvider становится реальной абстракцией; смена ключа (КЛАДР/OSM/DaData) не требует переписывать БД и фронт. Обратимое архитектурное решение перестаёт маскироваться под необратимое.
- Первые шаги (prevent):
- Написать ADR «Address Identifier Architecture» в partizap-docs/docs/dev/adr/ до фриза бэк-схемы
- Согласовать с бэком расширение схемы DEV-379: таблица addresses (address_id, source, source_value); миграция текущих СПб/Москва записей
- Обновить фронт-спеку и openapi.yaml: payload address_id вместо address_fias_id; entities/geo.schema на address_id
- Исполнитель: оба
- Сигнал успеха (detect): Ни один фронт-файл не содержит fias_id кроме entities/geo/model/geo.schema.ts (метаданные) и admin-просмотра;
grep -r 'fias_id' apps/main/app/features= 0; lint:endpoints проходит - Стоп-условие: Бэк отказывает в смене схемы → возврат к варианту Г (fias_id в payload, ADR как deferred-tech-debt с явным roadmap миграции)
- Если уже случилось (limit damage): Через 12 мес миграция через bridging-таблицу legacy_fias_to_address_id; 2-фазный rollout: бэк принимает оба ключа → фронт переезжает → бэк дропает fias_id-ключ
- Открытые вопросы:
- UUID генерируется на бэке при resolve или на этапе миграции схемы?
- Нужна ли версионность таблицы addresses (history) для аудита смены источников?
Топ 1–3 — с чего начать
- H-006 — GeoProvider абстракция фиктивна — fias_id размазан по фронту. Первый шаг: Написать ADR «Address Identifier Architecture» в partizap-docs/docs/dev/adr/ до фриза бэк-схемы
- H-003 — Координаты домов для гибрида метро не определены в плане. Первый шаг: Создать YouTrack-таск как subtask DEV-402 на batch-геокодинг домов; DEV-380 → blockedBy на него
- H-004 — Debounce 250ms заложен без SLO на suggest от бэка. Первый шаг: Добавить SLO в openapi.yaml для /store/geo/suggest
Bias-проверка топа
- Главный риск искажения: planning fallacy — все три варианта 'А' в топе требуют расширения scope бэк-эпиков (новая схема БД, batch-геокодинг домов, SLO с нагрузочным тестом); выглядит как простой выбор, но это +2-3 месяца к DEV-379/DEV-380
- Коррекция: явно зафиксировать в спеке раздел Backend dependencies с тикетами, owner и ожидаемой готовностью; не начинать фронт-работы до письменного подтверждения от бэк-команды
Reverse премортем (если запущен)
Заполняется только если финальная рекомендация — отложить/отказаться.
«Прошёл горизонт. Не сделали. Оказалось зря — почему?»
- Optimisation theatre на пустых данных: все 5 принятых А-вариантов — защиты от теоретических проблем при нулевых юзерах; готовились к скрейпингу, DDoS и необратимым схемам которыми никто не пользовался
- Бэк-команда не вытянула расширенный scope (геокодинг + новая схема + acceptance + rate-limit = +2-3 месяца поверх DEV-379/380); выбрали минимум, фронт ждал ещё дольше или работал с компромиссами
- Decision rot без owner и дедлайна: ADR, backend-таски, SLO зависли в чате; через 3 месяца переключились на другие приоритеты, эпик формально жив, фактически забыт
Что перевешивает: H-003 (UX метро без координат ломается) и H-006 (необратимая схема БД через 12 мес) — реальные дыры, не теоретические; отсрочка на 2-4 недели для ADR и backend-тасков пропорциональна; митигация главного риска reverse (decision rot) — в финальной записи зафиксировать owner и дедлайн на каждый блокер