Эта часть про то, как я пилю бэкенд, учусь на этом и получаю эмоциональные качели.
Содержание
Для начала, напомню о себе: «у меня свой бизнес, а в IT, я так, для души». Шутка. В общем, после 12 лет отсутствия в сфере я решил вернуться к своему базовому образованию – инженер-программист. Что-то приходится «вспоминать с нуля», но я не люблю начинать изучение полностью с теории, я больше экспериментатор. Во-первых, так можно учиться до бесконечности, оттачивая свои знания, но никогда их не применить. Во-вторых, банально, но ты не узнаешь, что такое горячо пока не потрогаешь. Есть еще, в-третьих, теперь есть ИИ – интерактивная энциклопедия и помощник в одном лице (хотя тут столько своих НО).
Как я рассказывал раньше, одним из учебных проектов я решил сделать симулятор торговли на Московской бирже. Выбрал стек, даже сделал кое-какие приготовления на сервере.
Важное отступление: у меня самый дешевый виртуальный хостинг, не VPS. Почему? Потому что «уже был», под мелкие эксперименты
Так вот. Спустя почти 2 недели, попутно изучая что и зачем, я закончил бэкенд. Насколько я знаю, здесь не очень принято обсуждать опыт использования ИИ, но я вас спрашиваю, как это работает: «я никогда не сталкивался с IT, по образованию я преподаватель литературы, но решил всё изменить, и за один вечер с ИИ написал супер-пупер мега сервис». Как, Карл?
Вы видели, что он выдаёт? О, это вполне работоспособный код, но это «бомба замедленного действия». В первую очередь, меня напрягла куча фреймворков, кажется, буквально на всё. Ладно, может это сейчас так и делается. Потом, выдуманные параметры (например, для ендпоитнов ISS MOEX), или наоборот, заверения, что ничего не надо. Самое прикольное – это когда ИИ выдает полный листинг файла, а потом текстом примечание, типа «я тут использовал устаревший/неэффективный/еще какой-то подход, если хотите, чтоб всё работало как часы – надо сделать то-то и то-то». Блин, а когда ловишь его на ошибке/неточности «да, вы правы. Сейчас исправлю. И, кажется, всё равно кривой код попадает к контекст».
Отдельные перлы
Ты прав по обоим пунктам.
По первому — current_price * quantity абсолютно верно. Рыночная цена облигации уже отражает амортизацию — ISS её считает с учётом остаточного номинала. Нам не нужно ничего пересчитывать.
По второму — да, полный вариант бессмысленен. Мы всё равно берём цену из ISS, так что хранить номинал отдельно и пересчитывать — лишняя работа которая ни на что не влияет.
И это после того, как он мне это вставил в код, я столкнулся с "кривыми" данными, нашел почему и... да, я прав.
ISS хранит только 2 дня внутридневных данных — значит таймфреймы 1м и 5м будут доступны только за текущий и предыдущий торговый день.
Это совершенно не так, ISS отдаёт свечи, чуть ли не c 2008-10 годов по 7 разным интервалам, но там нет 5 минут. Я долго пытался понять почему я получаю пустой массив.
Именно, ты правильно мыслишь. Раз данные в MySQL обновляются раз в минуту — WebSocket вообще не даёт никакого преимущества. Смысла в нём ноль.
А тут он мне на shared-хостинге (да он знает мои ограничения) советует мне заморочиться с WebSocket (это вообще реально?) для данных, которые на сервере обновляются раз в минуту, а на клиенте, если чаще минуты вообще берутся из кэша (на фронте TanStack Query, при чем сам ИИ предложил).
Фуух, всё. В защиту скажу, что для изучения, в чём-то это даже хорошо. Как это работает для меня на конкретном примере. Я понимаю, что нужно сделать. Надо раз в сутки делать запрос к ISS, получать список бумаг и заливать их в MySQL на сервере в несколько таблиц. Затем, ежеминутно запрашивать с ISS котировки и тоже заливать в MySQL. Так сделано, чтобы не долбить ISS запросами от каждого клиента. Я лучше один раз заберу к себе, а потом раздам клиентам, в том числе из кэша. Но я не знаю всех прелестей python (мой стек был Delphi+ADO+MS SQL. Да, я баловался и PHP, и Python, даже Perl чуток, но это не в счёт). Я прошу ИИ написать код и изучаю его. Всё здорово, всё понятно и работает.
Я рад, но не долго. Начинаю делать ручные тесты (пока не в теме QA Automation). Вот тут и начинается обучение: я вижу, что скрипт работает не так как запланировано, разбираюсь с этим (были неправильные ендпоитны, не учитывал пагинацию, уверяя что её нет, брал вообще не те поля, брал названия полей не из модели данных и так далее). Начинаю фиксить сам, потом делаем это вместе с ИИ. Снова тесты. Снова сам, снова ИИ. И на одной из итераций победа! Локально. Всё работает, всё правильно берёт, правильно раскладывает. Деплой.
С первого взгляда всё отлично, но помните отступление про хостинг? А теперь эти скрипты «отжирают» 13% CPU, вместо разрешённых CPU. Разбираемся. Тут, конечно, я развёл руками. Отдался ИИ. Оказывается, мало того, что они долбят ISS каждый раз SSL церемониями на каждую бумагу, так еще и MySQL по несколько раз дергают, т.е. для 100 бумаг – это 100 соединений, и 200-300 обращений к базе через дополнительную прослойку (SQLAlchemy). Это только 1 скрипт, во втором всё еще хуже: на каждую бумагу несколько запросов к ISS (из-за пагинации ответов), и кратно записей в MySQL. Не удивительно, что каждый из них в момент работы занимал до 50-60% процессорного времени.
Починили. В начале скрипта открываем сессию к ISS и запросы шлём в рамках этой сессии, собираем по результатам массив и на native SQL делаем bulk insert. Для меня пока что всё логично, и время CPU сократилось до 2-4%, т.е. имеем ожидаемый результат.
Для тех, кто любит пинать ногами
Первоначальный код
def fetch_coupons(ticker): while True: url = f'{ISS_BASE}/statistics/engines/stock/markets/bonds/bondization/{ticker}.json' try: r = requests.get(url, params=params, timeout=15) r.raise_for_status() data = r.json() # some more code except Exception as e: print(f' Ошибка при загрузке купонов {ticker} (start={start_coupons}): {e}') break while True: url = f'{ISS_BASE}/statistics/engines/stock/markets/bonds/bondization/{ticker}.json' try: r = requests.get(url, params=params, timeout=15) r.raise_for_status() data = r.json() # some more code except Exception as e: print(f' Ошибка при загрузке амортизаций {ticker} (start={start_amort}): {e}') break return sorted_coupons, sorted_amorts with app.app_context(): bonds = Security.query.filter_by(is_active=True, type='bond').all() for i, sec in enumerate(bonds, 1): rows, amort = fetch_coupons(sec.ticker) for c in rows: try: db.session.execute(“INSERT …. ON DUPLICATE KEY UPDATE”) except Exception as e: print(f' Ошибка записи купона {sec.ticker}: {e}') continue for a in amort: try: db.session.execute(“INSERT …. ON DUPLICATE KEY UPDATE”) except Exception as e: print(f' Ошибка записи амортизаций {sec.ticker}: {e}') continue try: db.session.commit() except Exception as e: db.session.rollback() print(f' Ошибка commit {sec.ticker}: {e}')
К чему пришли в итоге
def fetch_coupons(session, ticker): while True: url = f'{ISS_BASE}/statistics/engines/stock/markets/bonds/bondization/{ticker}.json' try: r = session.get(url, params=params, timeout=15) r.raise_for_status() data = r.json() # some more code except Exception as e: print(f' Ошибка при загрузке купонов {ticker} (start={start_coupons}): {e}') break while True: url = f'{ISS_BASE}/statistics/engines/stock/markets/bonds/bondization/{ticker}.json' try: r = session.get(url, params=params, timeout=15) r.raise_for_status() data = r.json() # some more code except Exception as e: print(f' Ошибка при загрузке амортизаций {ticker} (start={start_amort}): {e}') break return sorted_coupons, sorted_amorts with app.app_context(): with requests.Session() as session: bonds = Security.query.filter_by(is_active=True, type='bond').all() for i, sec in enumerate(bonds, 1): rows, amort = fetch_coupons(session, sec.ticker) for a in amort: bulk_amortizations.append({}) for c in rows: bulk_coupons.append({}) if bulk_amortizations: try: db.session.execute( db.text(), bulk_amortizations ) db.session.commit() except Exception as e: db.session.rollback() print(f' Ошибка массовой записи амортизаций: {e}') if bulk_coupons: try: db.session.execute( db.text(), bulk_coupons ) db.session.commit() except Exception as e: db.session.rollback() print(f' Ошибка массовой записи амортизаций: {e}') db.session.remove()
Чему я научился за это время короткое:
доверяй ИИ, но проверяй. Тут как раз становятся актуальны статьи (здесь же, на Habr), почему нельзя вайбкодить если ты не разбираешься в программировании;
не все фреймворки одинаково полезны, вернее не всегда. Тот же SQLAlchemy в других местах мне пока очень нравится, там не должно быть таких затыков;
изучил ряд новых для себя конструкций, как например, декораторы функций в python;
ознакомился сразу с несколькими фреймворками. Не говорю, что изучил, ведь только опробовал часть функционала применительно к данной задаче;
ну и понял множество нюансов, вроде handshake в начале каждой сессии.
Смогу ли я повторить бэкенд без ИИ теперь. Вполне, но в любом случае мне потребуется документация на большинство инструментов, которые я применил. Здесь я рассказал только про два cron скрипта, которые мне показались наиболее интересными именно с точки зрения моего подхода к обучению. Кроме описанного у меня используется кэширование (Flask-Caching), аутентификация JWT (Flask-JWT-Extended), естественно сам Flask, ORM (SQLAlchemy), alembic для миграций.
В качестве вывода скажу, на данный момент мой подход имеет место быть, и может быть даже интересен. Особенно с точки зрения эмоционального подкрепления, т.е., учитывая минимальный уровень в python, я получаю достаточно быстрый результат и учусь на нём, что не может не радовать. Конечно, есть обратная сторона, это такой же «дешевый дофамин» как и в случае с социальными сетями. В итоге может пострадать глубина знаний, а глобально - и сама мотивация, это надо учитывать.
Комментарии (3)

PechoraDev
16.05.2026 03:31Терпение и труд все перетрут. Да и «бывших» разработчиков не бывает. Просто технологии так шагают вперед, что сегодня ты выучил один язык, а завтра окажется, что надо еще столько же учить /изучать. Удачи в разработке!

Dhwtj
16.05.2026 03:31Вы видели, что он выдаёт? О, это вполне работоспособный код, но это «бомба замедленного действия»

отлито в граните
Shalundrive
Каждый заблуждается по своему. Для хобби ради души это, может и сойдет, но к настоящему инженерному ремеслу и уж тем более к профессиональной разработке это не имеет никакого отношения. Главное заблуждение автора, он как не знал ничего так и не знает и без ИИ, у него ничего не получится.