Site icon AppTractor

Как одна ошибка ChatGPT стоила нам $10,000+

Я хочу в предисловии к этому сказать, что да, практика здесь очень плохая и постыдная (и мы с тех пор добавили надежные модульные/интеграционные тесты и оповещения/логирование), ее можно/нужно было избежать, это были человеческие ошибки, и очень очевидные в ретроспективе.

Это было в другое время, в условиях больших временных ограничений на самых ранних этапах (первые несколько недель) развития компании. В основном я рассказываю об этом как о забавной истории с уникальными обстоятельствами, связанными с воспроизводимостью ошибок в проде (опять же из-за нашей собственной глупости). Пожалуйста, читайте с учетом этого.

Начало

Мы впервые включили монетизацию для нашего стартапа в мае прошлого года. У нас были низкие ожидания, но мы были приятно удивлены, когда получили первого клиента уже через час после запуска. Это был волшебный момент. Мы отправили ему благодарность, поздравили друг друга  и, учитывая, что мы только что провели две бессоные ночи, чтобы все подготовить, быстро легли спать.

Утром мы проснулись с тем, что на gmail пришло более 40 уведомлений с жалобами пользователей. Казалось, что за ночь все пропало. Никто из этих пользователей не мог подписаться на рассылку. Мы понятия не имели, почему.

Наш путь к монетизации

Для справки: в мае стартовало наше обучение S23 YC, и мы не знали, в каком направлении двигаться после запуска. Далтон, наш партнер по группе YC, посоветовал нам использовать платящих подписчиков в качестве компаса и сказал нам удвоить ежемесячную цену, которую мы уже имели в голове. В итоге (и с неохотой) мы остановились на 40 долларах в месяц. После встречи мы сразу же приступили к работе по настройке монетизации. Изначально наш проект был полностью на NextJS, но мы хотели сначала перевести все на Python/FastAPI. Мы сделали это (с некоторой помощью от ChatGPT), полностью интегрировали Stripe… А затем последовали пять дней наименьшего сна за весь месяц (да, пять дней — это очень много, чтобы найти этот баг).

В течение этих пяти дней мы начали бояться просыпаться, зная, что нас встретят 30/40/50 писем с жалобами. Мне до сих пор нравится размышлять о том, сколько именно клиентов мы потеряли из-за этого. 50 писем в день х 5 дней х 40 долларов = 10 000 долларов в месяц потерянных продаж — и это только от тех, кому было не все равно, кто жаловался. Мы отвечали на эти письма, как заведенные, каждый день. Люди жаловались на бесконечную загрузку спиннера при нажатии кнопки «Подписаться», мы проводили расследование, открывая новый аккаунт, проверяли, что с подпиской у нас все в порядке, а затем продолжали свой день в замешательстве. Ничто не помогало нам решить проблему, и, что еще более странно, мы получали почти ноль жалоб в течение всего рабочего дня.

Галлюцинация на 10 000 долларов

Путь от выявления проблемы до ее реального решения занял, казалось, несколько месяцев. Спустя пять дней, бесчисленные электронные письма, сотни журналов Sentry, длинные переписки в Discord с инженерами Stripe и многочасовые просмотры пяти ключевых файлов мы нашли ее 🎉. Попробуйте обнаружить его сами, прежде чем читать дальше.

Если вы еще не нашли его, то виновником была одна-единственная, невинная на вид строчка. Она стала бичом нашего существования на той неделе. Строка, которая в буквальном смысле стоила нам 10,000 долларов. Страшная строка 56.

Дело в том, что в рамках миграции бэкенда мы переводили модели баз данных из Prisma/Typescript в Python/SQLAlchemy. Это было очень утомительно. Мы обнаружили, что ChatGPT отлично справляется с этим переводом, и поэтому использовали его почти на протяжении всей миграции. Мы копировали сгенерированный им код, видели, что все работает нормально, пробовали его в продакшене, видели, что он тоже работает, и продолжали свой веселый путь. Однако на этом этапе мы все еще использовали наш Next API для всех вставок в базу данных. Python только читал из базы данных. Впервые мы начали вставлять записи в БД на Python, когда реализовали подписки. Хотя в процессе мы создали совершенно новые модели SQLAlchemy вручную, в итоге мы просто скопировали тот же формат, который ChatGPT написал для наших существующих моделей. Мы не заметили, что копировали одну и ту же проблему со способом генерации идентификаторов во всех наших моделях.

Ловля ошибки

Проблема в строке 56 заключалась в том, что мы просто передавали одну жестко закодированную строку ID вместо функции или лямбды для генерации UUID для наших записей. Это означало, что для любого экземпляра нашего бэкенда, как только один новый пользователь подписывался и использовал этот ID, ни один другой пользователь не мог повторить поток подписки, так как это приводило к столкновению уникальных ID. Эта проблема была действительно хорошо скрыта из-за нашей настройки бэкенда. У нас было восемь ECS-задач на AWS, на каждой из которых работало по пять экземпляров нашего бэкенда (да, мы знаем, что это излишество, но, честно говоря, у нас были кредиты AWS). Это означало, что у любого отдельного пользователя был пул из 40 уникальных идентификаторов, на которые он мог попасть.

В течение рабочего дня это было нормально. Мы, вероятно, совершали 10-20 коммитов в день (конечно, непосредственно на main), что приводило к новым развертываниям бэкенда, давая нам 40 новых ID для потенциальных клиентов. Однако ночью, когда мы, наконец, переставали делать коммиты (как же мы ленивы, правда?), единый ID каждого сервера перехватывался и приводил к тому, что все новые подписки сталкивались с блокировкой ID. Пользователи начинали с 40 возможных серверов, на которых они могли подписаться, и быстро заканчивали почти нулевым числом, когда наступала ночь. Решив эту проблему, мы словно сняли груз с плеч. Обнаружив это, Адам быстро выпустил исправление, и впервые за неделю мы смогли наконец-то спокойно отдохнуть (ну, не совсем, поскольку у нас оставалось еще десять срочных багов, но это уже история для другого материала).

Заключение

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

Источник

Exit mobile version