Разработчики, издатели, маркетологи, лидеры игровой и мобильной индустрии на одной площадке. Кейсы, инструменты продвижения и монетизации, тренды рынка и премия RuStore 2026.
Москва + онлайн. Участие бесплатное, по регистрации.
⏰ Напоминание пришло на три часа позже, и Python не выдал ни одной ошибки
Бот для автосервиса писал клиенту за час до визита. Локально всё работало, а в Docker на VPS сообщения стали опаздывать ровно на три часа.
Виноваты «наивные» datetime, то есть даты без часового пояса. Напоминание хранилось как «17:00» неизвестно по каким часам. А datetime.now() берёт время системы: на ноутбуке Москва, в контейнере UTC. Вот и вышло 17:00 по UTC, или 20:00 по Москве.
Правило простое: берите datetime.now(timezone.utc), храните в базе UTC, а в часы пользователя переводите только на входе и выходе. Сравнить наивную дату с датой с поясом Python не даст, так что забытое место всплывёт исключением.
Замена на utcnow() не спасает: он тоже возвращает наивный объект, а с Python 3.12 ещё и помечен устаревшим.
А ещё в статье: почему ежедневная сводка после перевода часов съезжает на час.
Партнёрская программа для сервисных центров, магазинов компьютерной техники, сайтов для скачивания файлов и авторов статей.
Вы можете предлагать его своим клиентам и аудитории — и зарабатывать на новых установках.
Выплаты до 500₽ за каждую установку Яндекс Браузера.
Подать заявку
#реклама 0+
partner.browser.yandex.ru
О рекламодателе
🔍 Первый запрос в тесте FastAPI проходит, второй падает
TestClient из FastAPI не поднимает сервер. Он вызывает приложение как функцию в фоновом потоке со своим событийным циклом. Без with на каждый запрос заводится новый цикл.
Пул asyncpg, созданный лениво при первом запросе, привязан к его циклу. Цикл закрывается после ответа, и второй запрос падает с «another operation is in progress», хотя другой операции нет. Под uvicorn цикл один на процесс, и ошибки нет.
Лечится так: пулы создаются в lifespan, а клиент живёт в with на всю сессию тестов:
import pytest
from fastapi.testclient import TestClient
from app import app
@pytest.fixture(scope="session")
def client():
with TestClient(app) as c:
yield c
Но если пул создать в асинхронной фикстуре pytest-asyncio, не спасёт и with: у фикстуры свой цикл.
В статье ещё три расхождения TestClient с настоящим сервером, включая 500, которую тест не видит.
Демо с AI чаще всего выглядят одинаково: агент за десять минут пишет сервис с нуля.
Вот только в работе бэкендера всё сложнее: легаси, рискованные миграции, неработающие тесты и ревью, которое становится бутылочным горлышком.
🗓 С 26 по 30 октября в сезоне Podlodka Backend Crew «AI в бэкенд-задачах» участники:
• Научатся передавать агенту инженерные стандарты и правила, чтобы он писал так, как нужно, а не как получится
• Соберут ревью, где несколько моделей независимо проверяют изменения, а финальное решение остаётся за человеком
• Посмотрят, куда переезжает сложность миграции легаси, когда код пишет агент
• Выяснят, где ломается мечта отдать миграцию агенту целиком
• Разберут, какие антипаттерны тестов агенты уже успели перенять от разработчиков.
Формат — пять дней живых Zoom-сессий утром и вечером, закрытое сообщество в Telegram и общение со спикерами.
Будет много интерактива и практики. Базовый ликбез оставят за бортом — этот сезон для тех, кто уже пробует AI в реальных проектах. Присоединяйтесь с 26 октября!
😄 Клиент нажал /cancel, а менеджеру пришла заявка от человека по имени «/cancel»
Такое случилось у разработчика Telegram-ботов на aiogram 3, библиотеке для Python. Бот вёл анкету по шагам: сначала ждал имя, потом телефон.
В aiogram 3 обработчики внутри роутера проверяются в порядке регистрации. Хендлер шага с именем ждал любой текст, а команда тоже текст. Он сработал первым, и до обработчика /cancel дело не дошло.
Лечится так: служебные команды живут в отдельном роутере, который подключается раньше остальных. А в шагах со свободным текстом команды ещё и отсекаются фильтром. Это страховка на случай, если кто-то потом добавит команду не в тот роутер:
💳 Как не выдать товар дважды, если об оплате сообщили двое
В Казахстане оплату в Telegram часто принимают так: клиент переводит на Kaspi и шлёт скриншот, а человек ищет платёж глазами.
Автор разбора собрал бота на aiogram 3, который сам выставляет QR Kaspi Pay и выдаёт товар. Счета идут через сервис Aitole, в разработке которого автор участвует.
Об оплате бот узнаёт двумя путями: по вебхуку за секунды и опросом статуса раз в минуту, если вебхук потерялся. Сработать могут оба сразу, поэтому paid ставится одним условным апдейтом:
UPDATE orders SET status = 'paid', paid_at = now()
WHERE op_id = $1 AND status = 'pending'
RETURNING chat_id;
Строку обновит только один из них, он и выдаст товар.
Есть и ловушка. У Kaspi одна активная сессия на номер, и если войти в Kaspi Pay под номером, отданным сервису, бот продолжит отвечать, но деньги принимать перестанет.
Подпись вебхука, Idempotency-Key и песочница разобраны в статье, весь бот около 200 строк.