Инженерия за пределами
идеального сценария.
Заметки о системах на Python, которые выдерживают реальные нагрузки.
Решения, компромиссы и код, который за ними стоит.
Все статьи доступны ниже. Открыть архив →
С чего начать
Выберите интересную темуСтатьи
Заметки об инфраструктуреCircuit breaker должен быть отдельным для каждого origin, а не клиента
Circuit breaker проще всего объяснить и проще всего привязать не к тому ключу. Объяснение помещается в предложение: после достаточного числа сбоев ненадолго прекратить вызовы…
PgBouncer в режиме транзакций и асинхронный SQLAlchemy: рабочая конфигурация, которой не хватает в документации
Конфигурация SQLAlchemy прекрасно работает напрямую с PostgreSQL. Затем перед БД ставят PgBouncer в режиме транзакций, ради которого обычно и нужен пулер, и прежние предположения…
Retry-After, backoff и jitter: что делает HTTP-клиент в продакшене
Обычный цикл повторов — четыре строки: попытаться, поймать, подождать, повторить. Рабочая политика HTTP-клиента требует примерно восьми решений, которые этот цикл молча принимает…
UUIDv7 как ключ партиционирования PostgreSQL
Диапазонное партиционирование по времени обычно требует изменить первичный ключ: PostgreSQL требует включить колонку партиционирования во все уникальные ограничения.
Безопасные повторы gRPC: какие статусы действительно стоит повторять
max_attempts=3 — самая частая и редко осмысленная строка gRPC-конфигурации. Повтор — ставка на то, что сервер ещё не сделал работу и повторение ничего не стоит.
Двенадцать библиотек, один инженерный стандарт, без монорепозитория
Bedrock Python — шестнадцать репозиториев: двенадцать библиотек, два инструмента, шаблон и этот сайт.
Должно ли приложение создавать топики Kafka при запуске?
Кто-то должен создать топик. Кандидатов три: брокер автоматически при первом упоминании имени, приложение при запуске или человек через процесс управления кластером.
Доставка exactly-once — миф; однократные эффекты реальны
Kafka не может гарантировать ровно одно обновление вашей БД. Без общей транзакции этого не может гарантировать ни один брокер: база и брокер — две системы с двумя фиксациями, и…
Идемпотентность в цепочке микросервисов
Один ключ идемпотентности в одном сервисе — решённая задача. В цепочке всё сложнее: важный повтор происходит наверху, важный эффект — внизу, а между ними два–три перехода с…
Идемпотентность фоновых задач и консьюмеров Kafka
Заголовок Idempotency-Key привлекает внимание, потому что у него есть имя и спецификация. Но та же проблема возникает у каждого worker очереди, причём чаще: очереди по своей…
Как отображать исключения Python в статусы gRPC без утечки внутренних данных
У gRPC шестнадцать кодов ошибок помимо успешного статуса, а у вашего сервиса сотня типов исключений: кому-то нужно задать соответствие.
Как партиционировать существующую таблицу PostgreSQL без переписывания приложения
ALTER TABLE ... PARTITION BY не существует. Чтобы сделать рабочую таблицу партиционированной, нужно создать нового родителя, присоединить старую таблицу как DEFAULT-партицию и…
Как я начинаю разработку Python-библиотеки для продакшена в 2026 году
Каждая библиотека этой серии начиналась одинаково: одна команда, сорок один файл и зелёные проверки примерно через три секунды. Речь о распределении усилий.
Ключи идемпотентности: часть, которую обычно реализуют неправильно
Idempotency-Key — одна из самых копируемых и чаще всего неправильно реализуемых идей платёжных API.
Когда при сбое Redis стоит продолжать работу?
Redis недоступен, а он обслуживает кеш, ограничитель частоты и ключи идемпотентности. Каждый запрос должен решить, что делать. Отклонять все — превратить сбой кеша в общий отказ.
Корректная остановка в Kubernetes — это протокол, а не обработчик сигнала
Каждый веб-фреймворк обрабатывает SIGTERM: прекращает принимать соединения, даёт текущим запросам завершиться и аккуратно выходит.
Корректная остановка консьюмера Kafka в Kubernetes
Kafka consumer в Kubernetes развёртывается заново несколько раз в день, и каждый раз получает SIGTERM посреди пакета.
Модели и схема БД разошлись. Заметит ли CI?
Миграция прошла, развёртывание завершилось, модели и БД теперь утверждают разное. Ошибки нет: обычный alembic upgrade head на пустой БД не проверяет соответствие результата…
Мы начали писать документацию для ИИ-агентов
В 2024 году я писал документацию для разработчиков. В какой-то момент 2026-го заметил, что значительная часть читателей — не люди.
Надёжность — это не retry=3
retry=3 первым добавляют в HTTP-клиент и последним пересматривают. Это одна настройка, а не стратегия надёжности. В обычный день она незаметна, поэтому переживает любое ревью.
Один жизненный цикл для HTTP, gRPC, воркеров и cron
HTTP API, gRPC-сервер, Kafka consumer и ночная задача — одно приложение с четырьмя способами получения работы.
Ошибки без привязки к транспорту: одна ошибка предметной области, ответы HTTP и gRPC
Сервис с внешним HTTP и внутренним gRPC формирует два ответа на каждый сбой, и они расходятся.
Паттерн Unit of Work в SQLAlchemy 2
Почти каждый впервые написанный репозиторий содержит commit(): чтобы получить id, передать его дальше, прочитать строку в тесте.
Перестаньте передавать AsyncSession повсюду
Во всех проектах с async SQLAlchemy, где я работал, на каждом уровне повторялась одна сигнатура:
Переход с pg_partman на управление партициями из приложения
pg_partman — стандартный и хороший ответ на обслуживание партиций PostgreSQL, когда доступны расширения и команда готова управлять логикой внутри БД.
Повторы могут усугубить сбой: проектируем бюджет повторных попыток
Три попытки на каждом переходе цепочки из пяти сервисов — усилитель нагрузки, который сильнее всего действует именно при сбое нижнего сервиса, когда тот меньше всего способен…
Политика хранения партиций — это не DROP TABLE
Задача очистки — та строка настройки партиционирования, которую никто не проверяет: найти старые партиции, удалить, запускать каждую ночь.
Почему жизненный цикл приложения не должен принадлежать FastAPI
lifespan FastAPI — хороший API: асинхронный контекстный менеджер с запуском до yield и остановкой после. Обычно туда помещают пул БД, прогрев, проверки здоровья и клиенты.
Почему перехватчики gRPC ломаются на потоковых RPC
Перехватчик, измеряющий вызов, считающий ошибки и связывающий request id, занимает двадцать строк и работает.
Почему ревью кода с ИИ не должно быть полностью автономным
Самый очевидный способ сделать ИИ-ревьюера — webhook: появляется merge request, модель читает diff, публикуются комментарии.
Почему я перестал писать обёртки над HTTP-клиентами
В каждой компании, где я работал, появлялась своя HTTP-обёртка: помощник повторов, класс конфигурации, метрики, затем class HttpClient в общем пакете, от которого зависят все и с…
Проверка здоровья Redis: одного PING недостаточно
Проверка, возвращающая True для сервера, неспособного принять запись, хуже отсутствующей: по её ответу принимают решения.
Проверки aiokafka перед выходом в продакшен
Aiokafka — хороший клиент со значениями по умолчанию для библиотеки, а не конкретного сервиса. В разнице между ними возникают инциденты.
Прогрев, readiness и liveness — три разные задачи
Большинство сервисов отвечает на все три вопроса одним обработчиком, обычно ping базы. Каждое смешение создаёт свой сбой.
Публикация в PyPI без API-токенов: полный путь Trusted Publishing
API-токен PyPI в секретах репозитория — пароль без срока действия, ограниченный проектом, но не объясняющий, кто именно его использовал.
Пул gRPC-каналов нельзя строить только по адресу
gRPC-канал дорого открывать и дёшево держать открытым. Поэтому у каждого сервиса с несколькими gRPC-зависимостями появляется пул каналов, а первый пул — всегда словарь с ключом…
Пять тестов миграций, которые стоит запускать в CI каждого Python-проекта
Мы тестируем приложение до зелёного значка покрытия, а затем развёртываем миграции БД, запущенные ровно один раз на ноутбуке в одну сторону.
Тестирование миграций с Testcontainers: вперёд, назад и снова вперёд
Статья о пяти тестах миграций объяснила, зачем они нужны. Здесь пошаговая настройка: от пустого tests/ до зелёной задачи CI, проходящей каждую ревизию вперёд, назад и снова…
Транзакционный inbox: вторая половина паттерна outbox
Outbox заметнее, потому что решает драматичную проблему: событие не ушло. Inbox решает тихую: событие пришло дважды либо consumer погиб посреди обработки.
Устройство gRPC-сервера на Python для продакшена
grpc.aio-сервер занимает шесть строк. Сервер, который можно поставить за балансировщиком, обновлять трижды в день и передать дежурной команде, требует набора решений, за каждым…
Что каждый Python-микросервис заново реализует для продакшена
Откройте репозиторий backend-сервиса, прожившего год в production, и найдите код, не относящийся к продукту.
Что отслеживать в пуле соединений SQLAlchemy
Первым обычно рисуют число занятых соединений, хотя оно мало говорит о качестве обслуживания. Я запустил восемь worker с пулом из четырёх, затем замедлил БД и посмотрел метрики.
Что происходит, когда Kafka недоступна целый час?
Не секунда сбоя, которую скрывает повтор, а час: неудачное обновление брокеров, заполненные диски, сетевое разделение зон.
Ядро без зависимостей: зачем инфраструктурным библиотекам необязательные зависимости
Инфраструктурная библиотека попадает во многие сервисы вместе со всеми своими зависимостями. Обычно об этом рассуждают отвлечённо.
Таймаут — не дедлайн: как теряется бюджет времени в микросервисах
Во всех сервисах, которые я запускал, на каждом исходящем вызове стоял таймаут. И каждый из этих сервисов всё равно умудрялся отвечать дольше любого числа в конфигурации.
Управление партициями PostgreSQL: от одного сбоя к другому
Партиционированная таблица может поднять вас с постели двумя способами. Первый — INSERT в 03:00, который PostgreSQL отклоняет, потому что никто не создал партицию на следующий…
Добро пожаловать в блог Bedrock Python
Здесь мы рассказываем об экосистеме Bedrock Python: что это такое, зачем она существует и какие идеи за ней стоят.
Знакомьтесь: mr-review — проверка merge request с помощью ИИ
Ревью кода — одна из самых полезных практик в разработке, но её качество сильно колеблется. Ревьюеры устают, переключаются между задачами прямо во время проверки и что-то упускают.
Паттерн Transactional Outbox на Python: omni-box
У распределённых систем есть классическая проблема: нужно обновить базу данных и опубликовать событие в Kafka в рамках одной операции, но общей транзакции между ними нет.
Статьи не найдены. Попробуем иначе?
Попробуйте более общий запрос, например «повторы» или «PostgreSQL», либо сбросьте фильтры.