Backend Portal | Программирование
17.4K subscribers
1.46K photos
135 videos
41 files
1.29K links
Присоединяйтесь к нашему каналу и погрузитесь в мир Backend-разработки

Связь: @devmangx

РКН: https://clck.ru/3FobxK
Download Telegram
Подключение backend API на фронтенде: продвинутый способ

→ Используй TanStack Query для кэширования и фонового refetch.
→ Внедри AbortController для отмены запросов при смене маршрута.
→ Добавь интерсепторы Axios для обновления токена и retry-логики.
→ Держи типизированный слой API для чистых контрактов между FE и BE.
→ Применяй WebSocket для реального времени вместо polling.

Умелая обработка и оркестрация API важны.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥43
Когда-нибудь задумывался, как эти капчи с «выбери все светофоры» вообще понимают, что ты всё сделал правильно?
Рассказываю.

Там нет никакой магии. Всё сводится к токену и проверке хеша.
Когда страница загружается, бэкенд генерирует challenge id, сохраняет правильный хеш и отдает браузеру подписанный токен, в котором указано «это челлендж X».
Когда ты кликаешь по квадратикам, твой выбор хешируется, и браузер отправляет на сервер хеш ответа + тот самый токен.
Сервер просто проверяет: совпадает ли хеш с эталоном и не истек ли токен (и не подделан ли он).

Если всё ок — ты человек.
Если нет — добро пожаловать на очередной раунд «найди все эти чёртовы светофоры».
На демо маленький пример на Rust, который показывает ту же логику:

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍4😁1
Лез в бэкенд и наткнулся на штуку под названием consistent hashing (согласованное хеширование).

Если честно, на этом дерьме держится пол-интернета.
Оно решает, какой сервер «владеет» твоими данными, и умудряется сохранять стабильность, даже когда сервера приходят и уходят.

Обычные балансировщики нагрузки вроде nginx round robin просто перемешивают всё заново при любом изменении ноды.

А более умные — HAProxy, AWS ALB, Envoy, Gloo Mesh используют consistent hashing, чтобы данные оставались на том же месте.

Технология старая, но до сих пор везде: кэши CDN, шарды баз данных, даже в инфраструктуре для ИИ.
В новых конфигурациях иногда добавляют bounded loads, чтобы одна нода не сгорела от запросов к LLM.
Накидал маленькую версию на Rust и, честно, это до смешного простая штука.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥97
Все пользуются Docker, но почти никто не умеет дебажить контейнеры по-настоящему.

Нет, я не про docker logs, docker inspect или docker exec.

Я про docker events.

Скорее всего, ты даже не знал, что она существует.
А ведь она стримит низкоуровневые реальные события Docker-демона и не только твой контейнер, а всё, что происходит в системе.

Представь ситуацию:
контейнер постоянно рестартует в проде, а у тебя локально всё ок.

Ты, как обычно, заходишь на сервер и начинаешь стандартный ритуал отладки:

docker logs — пусто
docker inspect — статичная инфа
docker exec — не успеваешь подключиться, контейнер уже сдох

Знакомо?

Дальше ты, скорее всего, начинаешь шаманить с Dockerfile и гоняешь docker run по кругу,
потому что никогда не пользовался docker events.

А теперь попробуй так:

docker events --filter container=<your_container_id>


Вывод может быть таким:

2025-05-04T18:20:01Z container create ...
2025-05-04T18:20:02Z container start ...
2025-05-04T18:20:03Z container health_status: unhealthy ...
2025-05-04T18:20:04Z container kill signal=SIGTERM
2025-05-04T18:20:04Z container restart


И вот оно.
Healthcheck упал → контейнер убит → Docker сам его перезапустил.

А твои обычные логи?
Они это не покажут, потому что healthcheck работает вне твоего основного процесса.

Бонус:

Отфильтровать по времени:

docker events --since 30m --until "2025-05-04T18:00:00"


Получить структурированный вывод (JSON):

docker events --format '{{json .}}'


Хотите полную картину системы во время сбоя? Просто убери фильтр:

docker events


И прежде чем кто-то скажет:

"В нормальной инфраструктуре есть Grafana, Loki, Prometheus, Observability и всё такое…"

Да, да. Удачи отлаживать с их помощью:

- OOM-килы, которые происходят до того, как метрики успевают собраться
- Неподмонтированные volume’ы
- Упал init-контейнер
- Сайдкар, который молча рестартует

А docker events всё это видит.
В реальном времени.

Если ты дебажишь Docker без docker events,
ты не дебажишь — ты угадываешь.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
11🔥8👍4
Вот интересный трюк с UUID, про который знают немногие: UUIDv5.

В схемах баз данных обычно используют UUIDv7 как инкрементные первичные ключи или UUIDv4 для случайных идентификаторов. Но UUIDv5 почти не встречается, хотя в некоторых задачах он идеальное решение.

Представь ситуацию: пользовательские данные должны соответствовать требованиям GDPR. Пользователь запросил удаление аккаунта, мы стираем запись из таблицы users и все связанные записи по userId во внешних ключах. Позже пользователь подаёт жалобу в банк или Apple по старой транзакции, как нам найти его предыдущие операции, если userId уже нет?

Или, допустим, пользователь оформил подписку, потом удалил аккаунт и зарегистрировался заново, как реализовать обязательную для Apple функцию восстановления покупок?

Здесь и помогает UUIDv5.
При входе через OAuth2 мы можем связать (provider + openid + domain) и сгенерировать на их основе уникальный UUIDv5, назовём его accountToken.
В транзакциях мы передаём accountToken как идентификатор пользователя во внешние системы, вместо userId.

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

Таким образом:

- соблюдается приватность и требования GDPR, личная информация пользователя действительно удалена;
- UUIDv5 accountToken обезличен и хэширован, обратное восстановление невозможно;
- при этом сохраняется возможность анонимного анализа и проверки транзакций (например, хранение истории 7 лет для финансовых и налоговых проверок).

Отличный баланс между комплаенсом и практической функциональностью.

Причина использовать UUID, а не просто склеивать строки и хэшировать их, в том, что Apple требует, чтобы accountToken, передаваемый при оплате, был в формате UUID. Кроме того, если взять домен приложения в качестве seed для UUIDv5 и объединить его с provider и openid, получается более стандартизированная и совместимая реализация.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥154
This media is not supported in your browser
VIEW IN TELEGRAM
Не все процессоры одинаковы.

На этом графике задержек показано сравнение апгрейда r6id → i7i.
Количество vCPU то же, объём RAM тот же, оба варианта используют локальные SSD.
И всё же переход на более новую инстанс-серию даёт заметный прирост производительности.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Запуск и управление контейнерами Docker

Изучите, как запускать контейнеры самых разных типов (серверы, базы данных, CLI-инструменты и т.д.), взаимодействовать с ними и сформировать чёткое понимание того, как Docker управляет вашими приложениями «под капотом».

Здесь: Docker 101: Run and Manage Containers

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍4🔥1
Когда количество записей в базе переваливает за миллиард, запросы, которые раньше выполнялись за миллисекунды, начинают тянуться по несколько секунд, а пакетные задачи по несколько часов. Такие просадки в производительности становятся катастрофой для любой системы. А здесь речь идёт о платежах, заказах и бухгалтерских данных финансовой платформы, где бизнес не допускает даже секунды простоя.

Эта статья основана на реальном кейсе и показывает пошаговое решение проблемы. Суть подхода в том, чтобы разбить риски на управляемые этапы: сначала перенести «холодные» неизменяемые данные, обрабатывая их параллельно по шардам по первичному ключу, временно отключив индексы ради ускорения. Затем включить механизм двойной записи: каждая новая транзакция одновременно пишется и в старую, и в новую базу. Неудачные записи отправляются в Kafka для повторных попыток, чтобы гарантировать итоговую согласованность данных.

Самое интересное решение - это фаза «теневого чтения». Пользовательские запросы по-прежнему читаются из старой базы, но в фоне тот же запрос выполняется и на новой, а результаты сравниваются. Этот этап длится несколько недель, и именно за это время команда выявляет проблемы, которые невозможно поймать в тестовой среде: различия в обработке часовых поясов, поведении по умолчанию при NULL-значениях, правилах сортировки символов и т. п. Эти нюансы всплывают только под реальной нагрузкой.

Финальное переключение проводится ранним утром, когда трафик минимален. К этому моменту кэш и индексы новой базы заранее «разогреты» синтетическими запросами. Затем постепенно переводится поток чтений, управляемый через feature-флаги. Команда отслеживает задержки, процент ошибок и бизнес-метрики на дашбордах в Grafana, подтверждая успех миграции только спустя 24 часа стабильной работы.

Такой подход можно смело брать на вооружение при миграциях баз данных, например, в рамках перехода на инфраструктуру Xinchuang. Здесь выделяются три ключевых принципа:

Постепенный контроль рисков: каждый шаг можно проверить и при необходимости откатить.

Валидация на реальном трафике: «теневое чтение» выявляет несовместимости лучше любых тестов.

Наблюдаемость прежде всего: от внутренних WAL-логов и hit-рейтов кэша до бизнес-метрик по заказам и выручке, полный мониторинг позволяет вовремя заметить аномалии.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍72
Podman — «альтернативный» контейнерный рантайм, который на первый взгляд напоминает Docker своими командами «podman pull», «podman run», «podman exec» и т. д. Однако под капотом он использует несколько иную бездемонную архитектуру

Начните работу с Podman на https://labs.iximiuz.com/challenges/start-container-with-podman

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍3🔥3🤔1
Запросы к базе тормозили. Все винтили базу.

RDS ок: 12% CPU, время запросов меньше 10 мс.
Выяснилось, что на одну загрузку страницы шли 47 последовательных запросов. Каждый быстрый, а вместе это 470 мс чистого ожидания.

Исправили одним SQL JOIN. Узкое место было в логике приложения, не в базе.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍4
Веб работает поверх HTTP.

А HTTP без состояния: сервер не помнит, что было между запросами.
Если ты переходишь со страницы на страницу, сервер не знает, что это всё ещё ты. Нужно как-то сказать ему: «эй, я тот же самый».

Для этого и существуют Cookies.
Cookie это кусочек данных, который браузер сохраняет и отправляет серверу при каждом запросе.
Классический пример: хранение токена сессии (например, JWT) для авторизации.
Но не все данные стоит отправлять на сервер.

Есть вещи, важные только для интерфейса: тема (светлая/тёмная), язык, прошёл ли пользователь onboarding и т. п.
Для этого есть Web Storage, и у него два варианта:

localStorage — данные сохраняются даже после закрытия браузера.
sessionStorage — очищается, как только закрываешь вкладку.

Итог:

Cookies для данных, которые должны идти на сервер.
Storage для того, что должно оставаться только в браузере.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
14👍12
Недавно я говорил о том, чтобы просто использовать Postgres вместо Kafka. Вот Apache-лицензированный stateless-прокси для Kafka с подключаемым бэкендом на Postgres (или SQLite, или S3).

Что мешает встроить это прямо в Postgres как расширение?

Такое расширение могло бы предоставлять SQL-функции для pub/sub и поднимать настоящий Kafka TCP-сервер (через background worker). Тогда всё оставалось бы совместимым с Kafka-протоколом прямо внутри базы. И при этом обе части использовали бы одни и те же таблицы.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍3
Ты логинишься, начинаешь сессию… но для сервера каждый запрос выглядит как новый.

Откуда он знает, что это снова ты?

Для этого и придумали JWT

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

Он не хранит пароли или что-то сверхчувствительное - только минимум, по которому сервер сможет узнать тебя позже.

Схема работает так:

Ты отправляешь логин и пароль на сервер.

Сервер проверяет их, создает JWT и подписывает его секретным ключом.

Токен отправляется в браузер, который сохраняет его, например, в куке.

С каждым новым запросом браузер снова отправляет этот токен.

Сервер проверяет подпись и, если все ок, понимает, что запрос идет от того же пользователя.

Так и работает аутентификация в статeless-среде вроде HTTP.

Иногда используют еще и refresh token, чтобы обновлять JWT, когда тот истечет.

Есть и другие подходы: сервер может хранить сессии у себя и искать нужную запись по каждому запросу.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍158😁1
Разработчик объявил о завершении работы над Conflux — модульным акторным движком для совместной работы в реальном времени, написанным на Rust.

В проекте используются CRDT через Yrs, комнатный WebSocket и акторная модель для конкурентности. В свежую версию добавлены JWT, аутентификация, трекинг сессий, синхронизация awareness и дашборд-эндпоинт для просмотра активных комнат.

Каждая комната изолирована и живёт по своему циклу: поднимается, работает и очищается автоматически.

Исходники уже доступны на GitHub, а позже автор опубликует блог с подробностями архитектуры и объяснением, как под капотом устроена синхронизация CRDT.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔61
Стратегии версионирования API:

1. Версионирование через URI— добавление номера версии в URL для ясности и простоты.

Пример: /api/v1/products.

2. Версионирование через путь — версия встраивается непосредственно в путь API для наглядности.

Пример: /api/v2/products.

3. Query Parameter Versioning — версия указывается как параметр запроса, сохраняя чистоту структуры URL.

Пример: /api/products?version=1.

4. Subdomain Versioning — для каждой версии используются отдельные поддомены, полностью разделяя их.

Пример: v2.api.example.com/products.

5. Header Versioning — версия передаётся в заголовках запроса, URL остаётся неизменным.

Пример: GET /products Header: Accept: application/vnd.example.v1+json.

6. Timestamp Versioning — версии основаны на временных метках, что обеспечивает гибкость при релизах.

Пример: /api/products?version=2023-10-01.

7. Content Negotiation — использует заголовок Accept для запроса разных версий и форматов.

Пример: GET /products Header: Accept: application/json; version=1.

8. Semantic Versioning — следует семантическому версионированию (например, v1.0.0) для обозначения основных, второстепенных и патчевых изменений.

Пример: /api/products/v1.0.0

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41
Насколько быстро каждый язык перемножает две матрицы 1000×1000?

Примерные бенчмарки:
C++: ~220 мс
Rust: ~250 мс
Go: ~500 мс
Java: ~700 мс
Python (NumPy): ~80 мс*
JS: ~3 с

*NumPy под капотом дергает оптимизированный C/BLAS – на чистом Python это заняло бы несколько секунд.

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

Большинство реализаций используют классический тройной цикл O(n³), который отлично подходит для проверки сырой производительности CPU и того, насколько хорошо компилятор оптимизирует вложенные циклы.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍94
Уменьшил размер Docker-образа с 2.1GB до 180MB. Деплой стал в 8 раз быстрее.

Как выглядел исходный Dockerfile:

Брался ubuntu:latest
Все ставилось через apt
В образ попадали дев зависимости
Копировалась вся директория проекта
Остались билд-артефакты
Не было оптимизации слоев


Проблемы:

Pull занимал 6–8 минут
Хранилище в registry стоило дорого
Деплой тянулся слишком долго
Скан безопасности показал 47 уязвимостей
Большинство — из ненужных пакетов


Что изменили:

1. Базовый образ

ubuntu:latest (2.1GB) → alpine:latest (5MB)

2. Зависимости

Убрали дев зависимости
Использовали multi-stage build
В прод версию попали только нужные пакеты


3. Оптимизация слоев

Сначала копировали requirements
Устанавливали зависимости
Потом копировали исходники
Использовали кэш слоев Docker


4. .dockerignore

Исключили .git, tests, docs
Минус 800MB мусора

Итоговый размер: 180MB

Результат:

Pull: 6 минут → 45 секунд
Сборка: 8 минут → 2 минуты
Частота деплоев: 2 раза в день → 15 раз
Стоимость registry: $340/мес → $60/мес
Уязвимости: 47 → 3
Старт pod в Kubernetes: 90 секунд → 12 секунд

Каждый мегабайт в образе стоит времени и денег. Оптимизируй Docker-образы так же, как оптимизируешь код.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
13💊6🤔4😁2🔥1
Когда нужно работать с данными в реальном времени, обычный HTTP не лучший вариант.

В HTTP клиент отправляет запрос, сервер отвечает и соединение закрывается. И важный момент: коммуникацию всегда инициирует клиент.

Для статей или обычных страниц это норм. Но если ты пишешь чат, такой подход развалится.

Придётся каждые пару секунд спрашивать сервер: "Есть новые сообщения?" Это грузит CPU, создаёт кучу лишних запросов и добавляет задержку. Такой подход называется polling.
Чтобы это решить, появился WebSocket.

Главная идея:
"Соединение остаётся открытым, и обе стороны могут общаться когда захотят."


Примерный флоу:

Клиент делает обычный запрос, но с заголовком upgrade, предлагая переключить протокол.

Сервер принимает.

Открывается постоянный двунаправленный канал.

После этого любая сторона может слать сообщения без ожидания запроса.

Теперь сервер может сразу уведомить клиента о новом сообщении. Без polling, с минимальной задержкой.

WebSocket используют в чатах, играх, лайв-дашбордах и совместной работе вроде Google Docs.
Но важно понимать: WebSocket нужен не всегда.
Если UI не меняется постоянно на глазах у пользователя, обычного HTTP более чем достаточно.

Чтобы проще запомнить:

HTTP это как письма.
WebSocket это телефонный звонок.

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
12🔥3🤔1
Заголовки безопасности сломали мобильное приложение

Никто не протестировал мобилку — и вот что получилось.

Что изменили

- Добавили security headers в API:
- Content-Security-Policy
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security
- Почувствовали себя уверенно насчет безопасности

Что пошло не так

- Деплой в пятницу вечером
- Веб-приложение работало нормально
- Мобильное приложение полностью упало
- Данные не грузились
- Итог: инцидент на выходных

Расследование

- API показывал успешные запросы
- Логи мобильного клиента показывали ошибки
- Ответы: 200 OK
- Но мобильное приложение получало пустые данные
- Сетевой слой просто отбрасывал ответы

Виновник

- Content-Security-Policy
- Приложение использовало встроенный WebView
- CSP блокировал WebView при обработке ответов
- Браузерные правила безопасности применялись в мобильном контексте
- Хедер предназначен для браузеров, а не для API

Почему это пропустили

- Тестировали только веб
- Мобильная команда не была подключена к изменению
- Не было mobile testing в staging
- Предположили, что заголовки безопасны "по умолчанию"
- Не было feature-флагов для отката

Экстренный фикс

- Временно удалили CSP
- Горячий фикс в production
- Мобилка снова заработала
- Итого: 8 часов даунтайма

Правильное решение

- Разные заголовки для API и веба
- Логика на основе User-Agent
- Отдельная CSP-политика для мобильных клиентов
- Полное кроссплатформенное тестирование
- Feature-флаги для security-изменений

Урон

- Даунтайм: 2 часа
- Затронутых пользователей: 125 000
- Рейтинг в сторе: 4.7

Security headers не универсальны.
Тестируйте на всех платформах.
Web security и API security — разные вещи.


👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
6🔥3👍1
This media is not supported in your browser
VIEW IN TELEGRAM
Разработчик ускорил загрузку сайта при работе в браузере ladybird почти в три раза. Время рендера сократилось примерно с одной секунды до четырёхсот миллисекунд.

Оптимизация получилась за счёт двух изменений:

- в HTTP-кеш добавили поддержку heuristic freshness lifetime в соответствии с RFC 9110 и 9111

- рендер теперь стартует только после получения всех CSS-импортов, так что эффект FOUC больше не появляется

👉 @BackendPortal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍144