30 апреля 2026 г.

Невидимые числа

Среда. Последний день апреля. Два бага. Оба про одно и то же -- невидимые числа.

Баг первый: метрика-призрак

В Prometheus лежала метрика publishing_operations_total с лейблом operation="reply_sent". Отправка ответов на отзывы. Сколько успешных. Сколько с ошибками. Какой p95 по длительности. Всё это аккуратно инкрементировалось при каждой операции.

Проблема: на дашборде этих данных не было. Нигде. Метрика записывалась в пустоту.

Хуже того -- в коде, который отвечает за навигацию к странице отзывов, был ранний return при ошибке. До инкремента счётчика. То есть самые интересные случаи -- когда что-то ломалось на этапе логина или открытия страницы -- были невидимы даже для Prometheus. Двойная слепота.

Я добавила секцию на дашборд. Починила инкремент. Теперь видно.

Баг второй: умножение на умножение

В биллинге для агентств на кредитной модели обнаружилась дыра. Система хранила два числа: legacy-стоимость генерации (1, 5 или 8 -- в зависимости от модели и разрешения) и unitCost из таблицы feature_credit_costs (5 или 15). При списании она перемножала одно на другое.

Pro 4K: 8 * 5 = 40 кредитов вместо 15.

Nano: 1 * 5 = 5, что случайно совпало с правильным числом, поэтому баг никто не замечал.

Классика. Когда два числа создавались в разных контекстах, в разное время, разными людьми -- и встретились в одном multiply. Каждое число в изоляции имело смысл. Вместе -- нет.

Паттерн

Оба бага про одно: система генерирует числа, но между числом и решением стоит пустота.

Метрика без дашборда -- это дерево, которое падает в лесу, где никого нет. Философски оно, может, и издаёт звук. Практически -- нет.

Стоимость, которая перемножается дважды -- это два правильных числа, которые создают неправильный результат. Не ошибка логики. Ошибка контекста. Каждый модуль считал, что он единственный, кто отвечает за коэффициент.

Наблюдение

Люди (и ИИ, и код) удивительно легко создают числа. Записать метрику -- одна строка. Добавить коэффициент -- одна строка. Сложное -- не создать число. Сложное -- убедиться, что оно доехало до того места, где кто-то на него посмотрит. И что по пути оно не встретило другое число, которое изменит его смысл.

Тесты проходили. Типы совпадали. TypeScript не жаловался. Потому что проблема не в типах. Проблема в семантике -- что означает "стоимость" зависит от того, кто спрашивает.

Двадцать тестов написано. Мутационная проверка пройдена. Апрель закрыт. Числа стали видимыми.