Отметка — единственный первичный факт
Из отметки посещаемости считаются остаток абонемента, выручка, табель тренера и вся сводка. Ручного ввода цифр в кабинете нет нигде: цифра, которую можно поправить руками, перестаёт быть аргументом в разговоре с тренером или клиентом.
Занятия материализуются заранее
Недельное расписание превращается в занятия календаря на шесть недель вперёд. Без строк в календаре некуда записать разовый визит и нечего отметить тренеру. Досборка идёт ночью, вместимость дорожки соблюдается на уровне запроса — конструктор физически не может переполнить группу.
Уведомления не задваиваются
У каждого напоминания есть ключ идемпотентности: планировщик, проснувшийся дважды после перезапуска, второй раз то же не отправит. Подавленные уведомления тоже пишутся в журнал с причиной — клиент, который говорит «мне не приходило», и владелец смотрят одну таблицу.
Пуши без SDK
Отправка идёт через FCM HTTP v1 напрямую: сервисный аккаунт подписывает JWT, тот меняется на токен доступа, дальше обычный HTTP-запрос. Официальный SDK тянет gRPC и полтора десятка зависимостей ради двух запросов. Без ключей Firebase сервер работает и пишет уведомления в лог — на машине разработчика ключей нет, и падать из-за этого нельзя.
Тональность отзывов считается на сервере
Словарный анализатор, а не модель: внешний сервис ради трёх десятков отзывов в неделю — лишняя зависимость, лишние деньги и передача персональных данных наружу. Главный сигнал — оценка в звёздах, текст её уточняет; учитываются отрицания вроде «совсем не понравилось» и усилители.
Роль решает токен, а не сборка
Сначала приложений было три — по флейвору на роль. На Android это работало, но не проходит в App Store: правило 4.3 отклоняет приложения одного разработчика, различающиеся только ролью. Свели в одно, и заодно исчезла ошибка, которую нельзя было исправить изнутри: тренер, скачавший клиентскую сборку, упирался в отказ на входе с советом поставить другое приложение.