# Принятые решения Что решено не делать и почему. Страница существует, чтобы очевидная идея не приходила второй раз: каждая из них уже приходила, была проверена и отклонена — с причиной. Формат записи: **решение** — почему — что было бы иначе. ## Архитектура и API **Поля `vetted_status`, `vetted_user`, `vetted_datetime` из API не удаляются.** Парк станций не обновится целиком никогда, а часть клиентов читает именно их (инвариант 7, см. [](../ai/context.md)). Они помечены `deprecated` в схеме; дата снятия появится, когда телеметрия покажет, что их больше никто не запрашивает. Иначе: станция, которая не просила ничего менять, получает ответ без знакомых полей. **`DEFAULT_PAGINATION_CLASS` в DRF не вводится.** Ответ `/api/satellites/` — голый массив, и клиент станции разбирает его как массив; общий пагинатор сменил бы тело на `{count, results}` для всех эндпоинтов разом. Вместо него — тест, который требует у каждого ViewSet либо `pagination_class`, либо явный лимит с причиной. Иначе: одна строка в настройках ломает контракт v1. **`/health/` не заводится.** Есть `/ready/` (база и применённые миграции), его читает healthcheck контейнера. Полной проверки зависимостей никто не потребляет: мониторинга, который бы её опрашивал, у проекта нет. Иначе: эндпоинт, который никто не читает, и который надо поддерживать. **Ретраи Celery стоят на обёртках в `network/celery.py`, а не на самих задачах.** Beat зовёт обёртку, а не `shared_task` напрямую, и `retry()` на вызванной напрямую задаче не срабатывает. Иначе: настройка, которая выглядит рабочей и молчит. ## Зависимости **`django-shortuuidfield` остаётся.** Снять его через `SeparateDatabaseAndState` нельзя: миграции `0001`, `0008` и `0017` импортируют `shortuuidfield.fields` на уровне модуля, а Django загружает весь граф миграций при каждом `migrate` — пакет обязан остаться установленным, даже если поле в состоянии модели заменить на `CharField`. Единственная альтернатива — squash, а он запрещён (см. ниже). Иначе: `migrate` падает на импорте у любого, кто поднимает базу с нуля. **`satellite.js` остаётся на версии 6.** В 7 пакет стал ESM-only, без UMD-сборки, а скрипты портала подключаются классическими тегами без сборщика. Иначе: страницы с расчётом пролётов перестают работать до появления бандлера. **Squash миграций `base` не делается.** Стенды и станции обновляются вразнобой; squash ломает любую базу, которая остановилась внутри схлопнутого отрезка. Правило и шаблон батчей — в [](contributing.md). Иначе: часть парка не мигрирует и восстанавливается только из дампа. ## Фронтенд **Каталоги спутников и передатчиков не переводятся на `serverSide: true`.** Данные и так приходят ajax-фидом, `` в шаблоне пустой, и приёмка «страница каталога меньше 200 КБ» выполнена. Остаток выигрыша — 80–120 КБ gzip — стоил бы сортировки на сервере, счётчиков, переписывания восьми тестов и переноса группировки передатчиков. Иначе: неделя работы ради страницы, которая и так грузится. **Сборка статики включается только двумя переменными сразу**: `COMPRESS_ENABLED=True` и `COMPRESS_OFFLINE=True`. Одна первая ломается на следующем же деплое: `collectstatic --clear` стирает `CACHE/`, а кэш компрессора в Redis всё ещё называет стёртые файлы. Иначе: страницы без стилей и 404 на бандлах после выкатки. **Имена кнопок — из Bootstrap, а не из палитры.** `btn-primary`, `btn-info`, `btn-secondary`, `btn-success`, `btn-danger` и их outline-пары плюс `btn-sm`/`btn-lg`; `btn-orange`, `btn-blue`, `btn-small` и прочие цветовые имена запрещены тестом. Роль действия определяет вариант: удаление сохранённых данных — всегда `btn-danger`. Иначе: одно и то же действие красное на одной странице и синее на другой. **Сегменты полосы успешности станции не доводятся до 24px (WCAG 2.5.8).** Ширина сегмента — это и есть данные: 8,7 % неуспешных нельзя нарисовать шириной в 24px, не соврав. Здесь работает исключение «Essential». Иначе: полоса показывает не то соотношение, которое описывает. **Две навигации DataTables с одинаковой подписью (`landmark-unique`) остаются.** Пагинация рисуется дважды, сверху и снизу таблицы, потому что так задана строка `dom`; крючка для подписи `nav` библиотека не даёт. Иначе: постобработка разметки после каждой перерисовки таблицы. ## Безопасность **`unsafe-eval` снят из `script-src` полностью.** Он пришёл с mapbox-gl 0.40 в 2017 году; в 2.15 ни `eval`, ни `new Function` не вызываются — проверено по всему `static/lib`. Аварийный клапан на случай неожиданностей — `CSP_STYLE_SRC="'self','unsafe-inline'"` в `.env` сервера; на страницах с плеером он не действует, там nonce. Иначе: политика разрешает выполнение строк на всех страницах ради библиотеки, которой это не нужно. **`/api/jobs/` не ограничен троттлингом.** Ключ авторизованного клиента — владелец, один на все его станции, а интервал опроса у старого клиента настраивается: общий лимит на владельца отрезал бы станции, которые ничего не нарушают. Лимиты стоят только на запись — приём SIDS и статус станции. Иначе: станция, которая не обновлялась, начинает получать 429 на штатной частоте.