Backend Matrix
СтатистикаСсылка: https://t.me/+FeuZC0lOM5wwNjI6 По всем вопросам: @Shustov_d
- Последний пост
- 6 авг.
- Последнее чтение
- 12:42
- Постов за неделю
- 0
- Всего постов
- 20
- Тип
- открытый
- Язык
- русский
- Категория
- Технологии (по похожим)
- В каталоге с
- 13 авг.
- 1/24сутки в ленте
- 209
- 1/48двое суток
- 239
- 1/72трое суток
- 258
Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.
Посты
В чем разница между P95, P99 и Average Latency? «Наш сервис показывает среднее время ответа (Average Latency) 50 мс. Всё отлично?» Правильный ответ: Нет, среднее значение в мониторинге — это иллюзия. 📊 Почему Average врет: Если из 100 пользователей 99 получили ответ за 10 мс, а 1 завис на 10 секунд, среднее время составит ~109 мс. Статистика выглядит неплохо, но 1% ваших пользователей просто уходит к конкурентам. 💡 Мониторьте минимум P95 и P99 в Prometheus/Grafana.
Почему архитектура Event-Driven не всегда спасает (и когда лучше вернуть REST) Event-Driven Architecture (EDA) на Kafka или RabbitMQ стала стандартом де-факто для распределенных систем. Но в 2026 году всё больше команд совершают ретракт к классическому REST. Почему? 1️⃣ Distributed Tracing — ад: Отследить путь запроса через 15 топиков Kafka, когда что-то упало на третьем шаге, требует колоссальных затрат. 2️⃣ Eventually Consistency (Согласованность в конечном счете): Когда интерфейс говорит юзеру «Оплачено», а сервис списывания денег еще в очереди, UX страдает первым. 3️⃣ Скрытая связность: Изменение структуры одного события в Pub/Sub может незаметно сломать 3 потребителя, о которых вы даже не догадывались. 💡 Правило: Используйте Синхронное взаимодействие (gRPC/HTTP) для critical-path операций (авторизация, платежи, проверка остатков) и Асинхронное (Events) — строго для побочных эффектов (отправка email, аналитика, генерация отчетов).
🚀 Оптимизация: ujson / orjson вместо стандартного json В бэкенде сериализация и десериализация JSON при высоком RPS может съедать до 30-40% времени обработки запроса. Стандартный модуль json написан на чистом Python и работает медленно. import orjson # Быстрая библиотека на Rust data = {"id": 101, "status": "success", "items": list(range(1000))} # Быстрая сериализация в байты: json_bytes = orjson.dumps(data) # Быстрый парсинг из байтов: parsed_data = orjson.loads(json_bytes)
🗄 SQL: Как избежать трагедии с N+1 запросами в ORM Главный грех бэкендера при работе с ORM - случайно сгенерировать сотни запросов к БД в обычном цикле. # ❌ ПРОБЛЕМА (1 запрос за постами + N запросов за авторами): posts = session.query(Post).all() for post in posts: print(post.author.name) # Каждая итерация делала отдельный SELECT! # ✅ РЕШЕНИЕ (1 запрос с JOIN): from sqlalchemy.orm import joinedload posts = session.query(Post).options(joinedload(Post.author)).all() for post in posts: print(post.author.name) # Данные уже загружены заранее!
⚡️ Трюк: asyncio.gather vs asyncio.TaskGroup (Python 3.11+) Если ты до сих пор используешь asyncio.gather для параллельного запуска асинхронных тасок, пора обновляться. В Python 3.11 появились TaskGroup import asyncio async def fetch_user(user_id: int): ... async def fetch_orders(user_id: int): ... # ❌ Старый подход (при ошибке в одной таске остальные продолжают работать): # users, orders = await asyncio.gather(fetch_user(1), fetch_orders(1)) # ✅ Современный подход через TaskGroup: async def main(): async with asyncio.TaskGroup() as tg: task1 = tg.create_task(fetch_user(1)) task2 = tg.create_task(fetch_orders(1)) # Сюда код попадет только когда ОБЕ таски завершатся user = task1.result() orders = task2.result() Если одна таска падает с ошибкой, TaskGroup автоматически отменяет все остальные таски в группе и выбрасывает ExceptionGroup
🛡 Как защитить REST API от двойных списаний и дублей Сеть нестабильна. Клиент отправляет POST /payments, списание происходит, но ответ падает по таймауту. Пользователь нажимает «Оплатить» еще раз. Без обработки идемпотентности вы спишете деньги дважды. Стандартное решение в бэкенд-архитектуре — Idempotency-Key в заголовках: sequenceDiagram Client->>Backend: POST /pay (Header: Idempotency-Key: uuid-123) Backend->>Redis: SETNX idempotency:uuid-123 "processing" Backend->>DB: Выполняем платеж Backend->>Redis: Сохраняем ответ сервера в Кэш Backend->>Client: 200 OK (Payment success)
🚀 Почему OFFSET убивает твой бэкенд и чем его заменить Классическая пагинация LIMIT 20 OFFSET 100000 — это тихий убийца производительности. Чтобы вернуть 20 записей с 100-й страницы, СУБД вынуждена прочитать и отбросить 100 000 предшествующих строк. Чем дальше листает пользователь, тем медленнее отвечает API. 💡 Решение: Keyset (Cursor-based) пагинация -- ❌ Плохо (O(N) по времени): SELECT id, title FROM posts ORDER BY created_at DESC LIMIT 20 OFFSET 100000; -- ✅ Быстро (O(1) по индексу): SELECT id, title FROM posts WHERE id < :last_seen_id ORDER BY id DESC LIMIT 20;
⚡️ Пишем свою легкую очередь задач прямо в PostgreSQL Когда вам нужно обрабатывать фоновые задачи (например, отправку писем или обработку платежей) несколькими воркерами, возникает проблема race condition: два воркера могут взять одну и ту же запись из БД. Вместо сложной блокировки всей таблицы используйте SKIP LOCKED: -- Каждый воркер выполняет этот запрос: BEGIN; SELECT id, payload FROM task_queue WHERE status = 'pending' ORDER BY created_at ASC LIMIT 1 FOR UPDATE SKIP LOCKED; -- 👈 Вся магия здесь -- Обновляем статус найденной задачи UPDATE task_queue SET status = 'processing' WHERE id = :found_id; COMMIT;
HTTP-код 429 Too Many Requests Если ваш API ничем не защищен, любой скрипт-парсер или злоумышленник может положить ваш сервер. Для защиты используется Rate Limiting Когда клиент превышает лимит (например, более 60 запросов в минуту), сервер должен вернуть статус 429 Too Many Requests. Но хорошим тоном считается не просто вернуть ошибку, а подсказать клиенту, когда можно повторить попытку, с помощью заголовка Retry-After. HTTP/1.1 429 Too Many Requests Content-Type: application/json Retry-After: 30 { "error": "Too Many Requests", "message": "Вы превысили лимит запросов. Пожалуйста, подождите 30 секунд." }
Зачем нужен SELECT FOR UPDATE и как он спасает от багов Представьте: два пользователя одновременно нажимают кнопку «Купить» для последнего товара на складе. Оба процесса параллельно читают остаток из БД (он равен 1), оба видят, что товар есть, и оба успешно проводят списание. В итоге — у вас продано два товара, хотя физически был один. Чтобы этого избежать, при чтении строки используйте блокировку FOR UPDATE. Она заставит вторую транзакцию ждать, пока первая не завершит свою работу. -- Транзакция 1 блокирует строку с товаром BEGIN; SELECT quantity FROM products WHERE id = 42 FOR UPDATE; -- (БД возвращает 1, строка заблокирована для других изменений) UPDATE products SET quantity = 0 WHERE id = 42; COMMIT; -- Блокировка снимается
⚠️ Главный убийца производительности: Проблема N+1 запросов Классическая ловушка при работе с ORM (Django ORM, SQLAlchemy, Hibernate, Sequelize). Это ситуация, когда вместо одного умного запроса к базе данных твой бэкенд делает сотни мелких. Представь, что нужно вывести 10 постов и имена их авторов. ❌ Как ORM делает по умолчанию (Проблема N+1): # 1 запрос: получает 10 постов posts = Post.objects.all() for post in posts: # Еще 10 отдельных запросов: по одному на каждого автора! print(post.title, post.author.name) 1 запрос для постов + 10 для авторов = 11 запросов к базе. А если постов будет 1000? ⚡ Как это чинится Мы сразу говорим базе вытащить постов вместе с авторами через JOIN. # Всего 1 ленивый запрос, который сразу соберет все данные posts = Post.objects.select_related('author').all() for post in posts: print(post.title, post.author.name) # Данные уже в памяти, запросов к базе нет!
🔄 В чем разница между PUT и PATCH в REST API? Оба метода используются для обновления данных, но их путают примерно 80% начинающих разработчиков. Запоминай разницу раз и навсегда. 🟢 PUT — Полная замена (обновление всего объекта) Ты должен прислать на бэкенд объект целиком. Если ты забудешь указать какое-то поле, старое значение сотрется или заменится на null. PUT /api/users/42 { "name": "Alex", "age": 26, "city": "Moscow" } 🟡 PATCH — Частичное изменение (патч) Ты присылаешь только те поля, которые реально хочешь изменить. Остальные бэкенд не трогает. PATCH /api/users/42 { "age": 27 -- изменится только возраст, имя и город останутся прежними }
🚀 Почему твоя база данных «ложится» на простых запросах Представь книгу на 1000 страниц. Если тебе нужно найти главу про транзакции, ты не будешь читать всю книгу с начала — ты откроешь оглавление в конце. В базах данных такое оглавление называется индексом. Когда ты делаешь поиск по полю без индекса, СУБД делает Full Table Scan (перебирает миллионы строк вручную). ❌ Медленный запрос: SELECT * FROM users WHERE email = 'test@example.com'; ⚡ Решение — добавляем индекс: -- Делаем это один раз при создании или миграции базы CREATE INDEX idx_users_email ON users(email); теперь тот же запрос SELECT выполнится не за 500 мс, а за 2 мс, потому что база сразу прыгнет к нужной строке. ⚠️ Индексы ускоряют выборку (SELECT), но немного замедляют запись (INSERT, UPDATE), так как базе нужно перестраивать свое «оглавление» при каждом изменении.
🔄 Что такое идемпотентность в API? Идемпотентный запрос — запрос, результат которого не меняется при повторном вызове, не меняя состояние сервера дальше. 🟢 Идемпотентные методы: GET PUT DELETE 🔴 Неидемпотентные методы: POST — обычно создает новую сущность. Нажмет пользователь кнопку «Оплатить» три раза из-за зависшего интернета — и у него трижды спишутся деньги Зачем это знать? Чтобы защитить критические операции (например, платежи)
💡 Быстрый HTTP-клиент на чистом встроенном Fetch Нативный fetch уже полностью оптимизирован и стабилен. // Чистый Node.js / Bun / Deno — без внешних зависимостей async function fetchUserData(userId) { try { const response = await fetch(`https://api.example.com/users/${userId}`, { headers: { 'Authorization': `Bearer ${process.env.API_KEY}` }, signal: AbortSignal.timeout(5000) // Нативный таймаут на 5 секунд! }); if (!response.ok) throw new Error(`HTTP error: ${response.status}`); return await response.json(); } catch (err) { console.error('Ошибка запроса:', err.message); } } Меньше node_modules — выше безопасность и скорость старта контейнера.
🛠 Как не положить продакшн при миграциях БД Сохраняйте базовые правила, которые уберегут от 500-х ошибок: 1. Добавление новой колонки с Default-значением ❌ ALTER TABLE orders ADD COLUMN status VARCHAR DEFAULT 'pending'; на большой таблице это заблокирует её на чтение/запись, пока СУБД будет физически перезаписывать каждую строку. ✅Добавить колонку без default, накатить код, который умеет обрабатывать NULL как значение по умолчанию, и заполнять старые строки фоновым батч-процессом. 2. Переименование колонки ❌ Просто переименовать поле в БД и надеяться на быстрый деплой нового кода. ✅ Создать новую колонку рядом. Настроить триггер, который пишет данные в обе колонки одновременно. Начать миграцию старых данных. Перевести чтение на новую колонку, после чего старую можно безопасно удалить. 3. Удаление колонки ❌ Удалить из БД, а потом выкатить код. ✅ Сначала выкатить версию кода, которая вообще не обращается к этой колонке. Убедиться, что всё стабильно, и только потом делать DROP COLUMN.
⚡️ Model Context Protocol (MCP) MCP, изначально созданный Anthropic, стремительно превращается в индустриальный стандарт для построения API. Раньше бэкенд проектировался под фронтенд или мобильные приложения (REST/GraphQL). Теперь бэкенд все чаще проектируется под ИИ-агентов (LLM). MCP — это открытый протокол, который позволяет ИИ-моделям безопасно, унифицированно и из коробки подключаться к вашим источникам данных, СУБД, логам и бизнес-логике.
⚡️ Встречайте ty — убийцу Mypy Команда Astral решила окончательно переписать всю экосистему Python на Rust. Встречайте их новый амбициозный проект — ty, ультрабыстрый тайп-чекер, который призван отправить старый добрый mypy на пенсию. Если вы пишете на Python с жесткой типизацией в больших проектах, то знаете, что mypy или pyright на тысячах строк кода могут задуматься на десятки секунд. ty обещает космическую скорость, совместимость и интеграцию. uv tool install ty ty check src/ #python #astral #ty #backend #opensource
🤔 Чем отличается rebase от merge? В Git команды rebase и merge используются для объединения изменений из разных веток, но делают это по-разному. Основное различие между ними заключается в том, как они сохраняют историю коммитов и как они влияют на структуру репозитория. 🚩Основные отличия 🟠Merge (Слияние) Объединяет две ветки, создавая новый коммит слияния (merge commit), который имеет две родительских ветки. Сохраняет всю историю коммитов обеих веток без изменений. История ветвления и слияния сохраняется. Если есть конфликты, Git предложит их разрешить перед созданием коммита слияния. git merge <branch> git checkout main git merge feature-branch 🟠Rebase (Перебазирование) Переносит все коммиты текущей ветки на вершину целевой ветки. Это делает историю линейной, как если бы изменения были сделаны последовательно. Изменяет историю коммитов, создавая новые коммиты для каждого коммита из текущей ветки. История ветвления исчезает. Если есть конфликты, Git предложит их разрешить по мере переноса каждого коммита. git rebase <branch> git checkout feature-branch git rebase main 🚩Плюсы и минусы 🟠Merge ➕Простота Процесс слияния прост и понятен. ➕Сохранение истории Вся история коммитов сохраняется, включая информацию о ветвлении и слиянии. ➖Коммиты слияния Создаются дополнительные коммиты слияния, что может усложнить историю. 🟠Rebase ➕Чистая история История линейная и более читабельная. ➕Упрощение навигации Проще следить за последовательностью изменений. ➖Изменение истории Изменение коммитов может привести к проблемам, если кто-то уже основывается на этих коммитах. ➖Конфликты Может потребоваться больше усилий для разрешения конфликтов, особенно если коммитов много. 🚩Когда использовать 🟠Merge Когда важно сохранить полную историю изменений, включая ветвление и слияние. В крупных командных проектах, где история изменений важна для отслеживания. 🟠Rebase Когда важно иметь чистую и линейную историю изменений. Для интеграции изменений из основной ветки в текущую рабочую ветку перед отправкой изменений в основную ветку.
На Stepik завирусился полный курс по вайбкодингу c Claude Code Первым делом вы изучите базу инструмента, а после научитесь: ✓ Разрабатывать MCP-серверы и Claude Skills ✓ Создавать собственных ИИ-агентов под свои задачи ✓ Эффективно работать с Claude и не упираться в лимиты ✓ Проектировать приложения через Claude Design ✓ Строить личную систему знаний на базе Obsidian + Claude Cowork ✓ Автоматизировать разработку через Agent Loops и хуки ✓ Запускать параллельные воркфлоу из нескольких агентов ✓ Проектировать приложения вместе с Claude Code ✓ Автоматизировать задачи через Connectors и внешние сервисы ✓ Перевести Claude Code из помощника в полноценного участника команды разработки ✓ Убирать из текстов типичные ИИ-шаблоны с помощью файла anti-ai-writing-style.md. ✓ Работать с таблицами при помощи Cowork В конце дорожная карта и 7 практических способа обхода ограничений Anthropic 🎉 В течении 48 часов действует скидка 25%