
Биллинг — это не «простая логика». Четыре грабли за два года разработки
«Напишем за неделю, там простая логика» — так начинается каждая вторая история с биллингом. Через два года получаем систему, которую понимает один человек, пересчёты вручную и клиентов, которым непонятно, почему именно такая сумма. Разбираем типичные ошибки и что закладывать до первого клиента.
Проблема начинается с недооценки. Команда видит базовую математику: тариф × количество × период = счёт. На бумаге выглядит элементарно. В реальности через месяц появляются льготные периоды, через три — пересчёты при смене тарифа, через полгода — клиенты с индивидуальными условиями, которые не вписываются ни в одну модель.
Граблі №1: Отсутствие аудита изменений
Первое, что ломается — прозрачность расчётов. Клиент звонит с вопросом «почему 47 320 рублей, а не 45 000». Разработчик лезет в код, смотрит логи, пытается восстановить цепочку применённых правил. Если изменения тарифа или скидок не пишутся в отдельную таблицу с timestamp и причиной — готовьтесь к ручным разборам каждого спорного счёта.
Решение — event sourcing для биллинговых операций. Каждое изменение тарифа, применение скидки, пересчёт — отдельная запись с контекстом. Не нужен полноценный event store, достаточно таблицы billing_events с полями: timestamp, entity_id, operation_type, old_value, new_value, reason, author. Это база для автоматической генерации детализации и разбора конфликтов.
Граблі №2: Пересчёты при смене тарифа
Клиент переходит с тарифа A на тариф B в середине периода. Простая логика говорит: пропорционально разделить. Реальность добавляет нюансы: минимальный платёж по старому тарифу, неделимые единицы потребления, уже выставленный аванс. Без явной модели пропорционального расчёта получается хардкод под каждый кейс.
Закладывайте prorated billing с первого дня. Это не про сложную математику, а про чёткие правила: как считается остаток периода, как учитывается уже оплаченное, что делать с неделимыми единицами. Пропишите эти правила в коде явно, с комментариями и тестами на граничные случаи.
Граблі №3: Единственный человек, который понимает систему
Через год разработки логика биллинга живёт в голове одного разработчика. Он знает, почему вот этот if обрабатывает старых клиентов иначе, почему там hardcoded исключение для корпоративных аккаунтов и почему пересчёт запускается дважды для определённых тарифов. Документации нет, код читается как алгебра с магическими константами.
Биллинг — это не feature, это критическая инфраструктура. Требуется документация на уровне ADR: почему выбрана такая схема пересчёта, какие альтернативы рассматривались, какие trade-offs. Код должен быть самодокументируемым: явные named-константы для льготных периодов, enum для типов тарифов, отдельные функции для каждого правила расчёта.
Граблі №4: Нет разделения на фазы расчёта
Весь расчёт происходит в одной транзакции: собрали данные, применили скидки, выставили счёт, записали результат. Если где-то ошибка — откатываем всё, клиент не получает счёт. Если нужно пересчитать задним числом — переписываем половину логики.
Разделите расчёт на фазы: 1) сбор данных о потреблении, 2) применение правил тарификации, 3) применение скидок и льгот, 4) генерация счёта, 5) отправка клиенту. Каждая фаза — отдельная функция с чёткими входами и выходами. Это позволяет тестировать изолированно, пересчитывать отдельные этапы и логировать промежуточные результаты.
Что закладывать до первого клиента
Аудит всех изменений: таблица событий с timestamp, контекстом и автором операции.
Модель пропорционального расчёта: явные правила для смены тарифа, остатка периода, минимальных платежей.
Разделение на фазы: сбор данных → тарификация → скидки → счёт → отправка. Каждая фаза изолирована и тестируема.
Документация решений: ADR для ключевых правил, комментарии в коде для неочевидной логики.
Тесты на граничные случаи: смена тарифа в последний день, нулевое потребление, отрицательный баланс после возврата.
