Слогер Создать блог
Разработка

Разбор: 3 бага в API перевода денег, которые пропускают на код-ревью

Эндпоинт перевода средств между счетами выглядит гладко и всё равно опасен. Показываю три проблемы, которые чаще всего проходят мимо ревьюера, и чек-лист, как их ловить.

Проверять чужой код — отдельный навык, и он почти не пересекается с умением писать свой. Человек может свободно читать Python и всё равно пропустить в пул-реквесте ошибку, из-за которой со счёта уедут реальные деньги. Ниже — разбор эндпоинта перевода средств между двумя счетами. В нём спрятаны три проблемы, и каждая встречается в рабочих проектах куда чаще, чем хочется.

Что делает код: принимает sender_id, receiver_id и amount, достаёт из базы оба счёта, проверяет, что они существуют, сравнивает баланс отправителя с суммой, списывает у одного, начисляет другому и делает commit. Синтаксис в порядке. Всё остальное — нет.

Первая: сумму никто не проверяет

Единственное условие — хватает ли денег. Про знак суммы речи не идёт. А теперь смотрим на две строки, которые меняют балансы: у отправителя вычитается amount, у получателя прибавляется.

Передаём amount = -500. Вычитание минуса превращается в сложение: у отправителя денег становится больше, у получателя — меньше. Отправитель фактически «переводит» себе деньги из чужого кармана. Ноль проходит ту же проверку и создаёт пустые транзакции, которые засоряют историю операций.

Лечится скучно: отклонять сумму меньше или равную нулю на входе, до обращения к базе. Валидация схемы запроса тут дешевле и надёжнее любой логики внутри обработчика.

Вторая: деньги в float

Тип amount — float. Для точных денежных значений это неподходящий выбор: 0.1 в двоичной плавающей точке не представляется точно, и на цепочке вычислений накапливается ошибка округления. Сумма, которая на бумаге сходится, в базе может разойтись на копейки — и найти потом, где именно, будет неприятным занятием.

Для денег берут фиксированную точность: Decimal или целые минорные единицы — копейки, центы. То есть хранят 1050 вместо 10.50, а точку ставят только на выводе.

Третья: проверка баланса и списание не защищены от гонки

Эту замечают реже всего, потому что в одной строке она не видна. Она проявляется, только когда два запроса приходят одновременно.

На счету 100. Почти в один момент приходят два перевода по 80. Запрос A читает баланс: 100. Запрос B читает тот же баланс: 100. Оба проходят проверку. Оба списывают.

Ушло 160, на счёте минус 60. Это классическая гонка на устаревших данных: проверка и изменение — две разные операции, и между ними успевает вклиниться второй процесс.

В продакшене нужна защита на уровне транзакции базы. Либо блокировка строки при чтении счетов, либо атомарный условный UPDATE вида «списать, если баланс всё ещё не меньше суммы», где проверка и изменение — один неделимый шаг. Схема «два отдельных SELECT, а потом commit» для денег не годится.

Чем закрывать гонку

ПодходКак работаетКогда подходитЧего не решает
Блокировка строки при чтенииВторой запрос ждёт, пока первый закоммитит изменениеОперации с несколькими объектами в одной транзакцииБлокировка висит до конца транзакции, при разном порядке захвата строк легко получить взаимную блокировку
Атомарный условный UPDATEУсловие по балансу и списание выполняются одним запросомДенежные списания, счётчики, резервыНужно проверять, сколько строк реально изменилось: ноль означает отказ, и это надо обработать
Проверка в коде приложения без блокировкиПрочитали, посчитали, записалиЧерновики, отчёты, тестовые данныеОт параллельных запросов не защищает вообще

Чек-лист для похожего ревью

  • Границы входных значений: отрицательные числа, ноль, максимум, пустая строка, слишком длинное значение.
  • Тип денежного поля: float или фиксированная точность.
  • Разделены ли проверка и изменение — или объединены в одну атомарную операцию.
  • Что произойдёт, если этот обработчик вызовут дважды одновременно с одного и того же счёта.
  • Поведение при ошибке: если commit упал, в каком состоянии остались балансы.
  • Куда логируется операция — при разборе спорной транзакции без этого не обойтись.

Почему третью ошибку пропускают чаще остальных

Первые две видны при чтении строки: глаз цепляется за float и за отсутствие проверки знака. Гонка так не читается — она требует смоделировать в голове два параллельных потока и понять, где именно они пересекутся. Это отдельное усилие, и на уставшем ревью его обычно не делают.

Помогает привычка задавать один вопрос к каждому изменяющему запросу: что случится, если между чтением и записью влезет кто-то ещё. Ответ «ничего» бывает только у кода, который работает с локальными данными.

Ревью — это не поиск красивых имён переменных. Начните с двух вопросов: какие значения вообще может принять вход и что будет, если этот код вызовут второй раз, пока первый ещё не закончил. На эндпоинте перевода денег таких вопросов хватает, чтобы поймать все три бага до того, как они доберутся до прода.

По материалам: career. Текст переработан редакцией Слогера.

← На главную

Рекламное место — Конец поста
Реклама · Слогер

Комментарии (0)

Войдите, чтобы комментировать.

Пока нет комментариев. Будьте первым.

Разработка

Как переписать личное портфолио с Angular 12 на Angular 22: разбор прыжка через десять версий

Личный сайт на Angular 12, продакшен на Angular 19 — и решение переписать всё сразу на 22. Что даёт чистая переписка вместо цепочки миграций, и почему инфраструктура и SVG-математика важнее списка логотипов в резюме.

Слогер 04.10.2026 ▲ 0
Карьера

Как выбрать тренажёр для mock-интервью: разбор AI-сервисов, живых интервьюеров и банков задач с ценами

Собеседование проверяет не только код, но и умение объяснять. Разбираем, какие сервисы для репетиции интервью стоят своих денег, а какие путают с тренажёрами.

Слогер 04.10.2026 ▲ 0
Разработка

Как отвечать на вопросы по Git на собеседовании: разбор механизмов вместо заученных команд

Git спрашивают почти на каждом техническом интервью, но проверяют не флаги, а модель репозитория в твоей голове. Разбираем частые вопросы и то, что за ними стоит.

Слогер 04.10.2026 ▲ 0
Карьера

Как составить резюме, которое пройдёт ATS: разбор для студентов-инженеров

Автоматические системы отсеивают резюме до того, как их увидит рекрутер. Разбираем, как описать дипломный проект, какие разделы поставить вперёд и какие ошибки в вёрстке убивают документ.

Слогер 04.10.2026 ▲ 0
Fluxs lenta
Реклама · fluxs.ru
Карьера

Как подавать заявки на вакансии, не заполняя формы вручную: разбор в три шага

Расширение для браузера заполняет однотипные формы откликов за вас и через 15 минут присылает отчёт. Разбираем, как это устроено, где границы метода и какие настройки экономят нервы.

Слогер 03.10.2026 ▲ 0
Карьера

5 навыков разработчика, без которых технические знания простаивают

Карьеру в разработке двигает не количество выученных языков, а умение разбираться с задачей, объясняться с людьми и доводить проекты до конца. Разбираем пять навыков и то, как каждый из них проверить на текущей работе.

Слогер 03.10.2026 ▲ 0
Карьера

Как автоматизировать отклики на вакансии: разбор расширения, которое отправляет заявки за вас

Джуниор-разработчик написал бесплатное расширение для браузера, которое фильтрует вакансии и само заполняет формы — за неделю оно принесло ему пять собеседований. Разбираю логику работы, чек-лист безопасности и ошибки, из-за которых массовые отклики вредят.

Слогер 03.10.2026 ▲ 0
Разработка

Чем опенсорс-команда отличается от работодателя: опыт участника проекта Pyxel из ESA

Инженер пришёл в открытый проект ESA с одним коммитом и получил там то, чего не дал ни один наниматель: подробную обратную связь, включённость и спасибо за каждый шаг. Разбираем, что именно сработало — и как повторить это у себя.

Слогер 02.10.2026 ▲ 0
Fluxs lenta
Реклама · fluxs.ru
Карьера

Почему процент откликов врёт: как считать воронку поиска работы и просроченные счета

Одна арифметическая идея работает и в таблице поиска работы, и в переписке с клиентом, который не платит: считайте только закрытые исходы и договоритесь о словах заранее.

Слогер 02.10.2026 ▲ 0
Карьера

Как получать пользу от IT-ассоциации: пять слоёв участия и план на первые 30 дней

Членство само по себе ничего не даёт. Разбираем, что реально можно делать внутри профессионального сообщества, что доступно всегда, а что зависит от людей, города и обстоятельств.

Слогер 02.10.2026 ▲ 0
Fluxs lenta
Реклама · fluxs.ru
Деньги

Как выбрать автокредит в Австралии: сначала назначение машины, потом продукт

Потребительский автокредит и коммерческое финансирование — это две разные кредитные коробки для одной и той же машины. Разбираем, как определить своё и что требовать у кредитора письменно.

Слогер 01.10.2026 ▲ 0