$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогКак проектировать REST API с идемпотентными командами: ключи, схемы и повторяемость

Как проектировать REST API с идемпотентными командами: ключи, схемы и повторяемость

$ sudo teach IT
·17 августа 2026 г.·13 мин·28
Как проектировать REST API с идемпотентными командами: ключи, схемы и повторяемость

Разберём, как отличать чтение от команд, где именно вводить idempotency key, как проектировать тела запросов/ответов и какие схемы использовать для гарантии повторяемости при ретраях.

Содержание
Чтение и команды: где идемпотентность действительно нужнаREST и HTTP семантика: не смешивайте чтение и изменениеЧто именно считать «одной и той же» командойКлюч идемпотентности: формат, место и правилаИдея: idempotency key как «намерение клиента»Где вводить key в REST контрактеСтабильность ключа и границы действияКак проектировать тела запросов и ответов для повторяемостиПринцип: ответ должен быть воспроизводимСтандартный контракт: envelope и единая семантикаЧто делать со временем выполнения: accepted/processing/succeededОшибка и повтор: нужно ли сохранять негативный результатСхемы гарантий повторяемости: от «минимума» до строгой детерминированностиСхема 1: Idempotency lock + хранение ответа (практически стандарт)Схема 2: Контроль payload-хэша (защита от конфликтов)Схема 3: Event-sourcing или outbox (усиление детерминизма)Схема 4: Комбинация с PUT и ресурсным идентификаторомТиповые ошибки проектирования идемпотентных командОшибка 1: ставить Idempotency-Key только на «создание»Ошибка 2: игнорировать ретрай при ошибкахОшибка 3: «вернуть 201 заново» вместо сохранения результатаОшибка 4: не документировать политику TTL и конфликтовОшибка 5: слишком маленькое время храненияПрактические паттерны API: что писать клиенту и серверуЗаголовки и ответы: формализуйте коммуникациюКонтракт ошибки: повтор должен быть безопаснымРеализация на практике: пример на условном Python/Flask и уровне идемпотентностиПсевдоконтракт и структура хранилищаКлючевой моментПолитика повторов на стороне клиента: чтобы гарантия работалаREST и идемпотентность: как не сделать “RPC в коробке”Практический чеклист для проектированияНа уровне API контрактаНа уровне хранения/согласованностиНа уровне наблюдаемостиВывод: идемпотентность как часть контракта, а не “добавка к коду”

Проектирование REST API редко ломается на «неправильном URL». Чаще всего реальные проблемы появляются в другом: клиент повторяет запрос из‑за таймаута, сеть флапает, прокси перезапускает соединение, сервер частично выполнил операцию и упал до ответа. В итоге один и тот же логически один раз запрос внезапно превращается в несколько раз.

Идемпотентность — это ответ на эту боль. Но реализовать её «на уровне заголовка» недостаточно: нужно определить, какие операции являются командами, где именно вводить ключ идемпотентности, как проектировать схемы запросов/ответов и как организовать поведение при ретраях так, чтобы клиент мог рассчитывать на повторяемость.

Ниже разберём практический подход к проектированию REST API с идемпотентными командами: от разграничения чтения и команд до схем и примеров контрактов.


Чтение и команды: где идемпотентность действительно нужна

REST и HTTP семантика: не смешивайте чтение и изменение

В REST-подходе удобно начать с базовой классификации:

  • Чтение: получение данных (обычно GET). Его обычно не делают идемпотентным — потому что повтор GET по определению не меняет состояние.
  • Команды (изменение состояния): создание, обновление, отмена, списания и т. п. Повторы могут привести к повторным эффектам, если сервер не умеет распознавать дубликаты.

HTTP различает идемпотентные методы (PUT, DELETE) и неидемпотентные (POST, PATCH). Но в реальных API часто встречается POST /actions/..., где смысл — «выполнить команду». Для клиента это выглядит как выполнение операции, а для сервера — как генерация побочных эффектов. И здесь HTTP-семантика не спасает: вам нужен явный механизм идемпотентности.

Что именно считать «одной и той же» командой

Идемпотентность в прикладном смысле означает:

Повтор одного и того же запроса (в рамках одного и того же намерения) приводит к одному и тому же итоговому состоянию и к согласованному ответу.

Это ключевой нюанс: недостаточно просто «не продублировать запись». Нужно также решить, какой ответ возвращать при ретраях — иногда клиенту важнее повторно получить идентификатор ресурса или подтверждение статуса.


Ключ идемпотентности: формат, место и правила

Идея: idempotency key как «намерение клиента»

Общий паттерн: клиент отправляет в заголовке Idempotency-Key уникальный идентификатор, который соответствует логической команде. Сервер сохраняет результат выполнения (или хотя бы достаточные данные для воспроизведения ответа) и при повторе с тем же ключом возвращает тот же результат.

Сильная сторона подхода: он переносит контроль на уровень контракта. Клиент не «угадывает», что произошло на сервере, а сообщает: «это тот же intent».

Где вводить key в REST контракте

Практически применимые варианты:

  1. Заголовок: Idempotency-Key: <uuid/строка>

    • Плюсы: не превращает API в запутанную структуру URL, совместимо с REST, стандартнее для HTTP.
    • Минусы: нужно договориться с клиентами и посредниками (прокси, API gateway).
  2. Тело запроса: поле idempotencyKey/requestId

    • Плюсы: проще сериализация, легче хранить вместе с payload.
    • Минусы: в некоторых случаях сложнее обеспечить единообразие (особенно если gateway валидирует тела или выносит часть логики наружу).

На практике заголовок чаще оказывается предпочтительным, потому что идемпотентность — это метаданные запроса, а не часть доменной модели. Но в некоторых доменах (например, где payload имеет единый envelope-формат) поле в теле тоже хорошо приживается.

Стабильность ключа и границы действия

Важно зафиксировать правила:

  • Ключ должен быть уникальным на уровне клиента и операции: обычно достаточно UUIDv4.
  • Повтор ключа означает повтор того же намерения. Если клиент изменит payload при том же ключе — поведение должно быть детерминированным: либо запретить конфликт (400/409), либо игнорировать часть полей (что обычно плохо).
  • Границы хранения: сервер не может хранить forever. Нужно определить TTL в зависимости от бизнес-рисков: от часов до дней. При истечении TTL повторы могут стать небезопасными — это нужно понимать и документировать.
  • Сочетание с авторизацией: ключи должны быть уникальны как минимум в рамках одного пользователя/tenant. На практике таблица идемпотентности строится по (tenant_id, user_id, key) или (principal, key).

Как проектировать тела запросов и ответов для повторяемости

Принцип: ответ должен быть воспроизводим

Если клиент повторяет запрос после таймаута, он ожидает, что сервер вернёт «тот же результат», даже если запрос физически прошёл уже один раз.

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

  • Команда должна иметь понятный идентификатор результата (например, orderId, paymentId, reservationId).
  • Структура ответа должна позволять клиенту однозначно продолжить процесс.
  • Сервер должен сохранять либо сам ответ, либо достаточную информацию, чтобы восстановить его идентичным способом.

Стандартный контракт: envelope и единая семантика

Удобный паттерн — использовать единый envelope в командах:

  • В запросе: параметры команды + идемпотентность (в заголовке).
  • В ответе: status, resultId, message, возможно resource или links.

Например, для создания ресурса:

code
POST /v1/orders
Idempotency-Key: 7f2c3e2e-3b5b-4eaa-9c6f-1d8d9f8d3c12
Content-Type: application/json

{
  "customerId": "cus_123",
  "items": [
    {"sku": "ABC", "qty": 2}
  ],
  "currency": "RUB"
}

Ожидаемый ответ при успешном выполнении:

code
{
  "status": "accepted",
  "orderId": "ord_987",
  "links": {
    "self": "/v1/orders/ord_987"
  }
}

При ретраях с тем же Idempotency-Key сервер возвращает тот же orderId и status, а не создаёт новый заказ.

Что делать со временем выполнения: accepted/processing/succeeded

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

  • status: "processing" — сервер принял команду и выполняет асинхронно.
  • status: "succeeded" — операция завершилась.
  • status: "failed" — операция завершилась ошибкой с детерминированным кодом.

Но тогда появляется вопрос: что возвращать при ретраях, если первый запрос ещё «в процессе», а клиент повторил?

Тут есть два рабочих варианта:

  1. Вернуть состояние на момент ретрая: если операция ещё выполняется — возвращайте processing и тот же resultId. Клиент может поллинговать GET /v1/operations/{id}.
  2. Дождаться завершения в рамках синхронного HTTP (если это укладывается в SLA). Иначе таймаут вернёт клиента назад — а сервер в повторе должен вернуть завершённый результат.

Какой вариант лучше зависит от модели нагрузки, но контракт должен оставаться стабильным: клиент по одному и тому же ключу должен получать один и тот же resultId, а статусы — непротиворечиво переходить.

Ошибка и повтор: нужно ли сохранять негативный результат

Обычно да, и это важнее, чем кажется.

Если команда вернула 400 Bad Request из‑за некорректного payload, повтор с тем же ключом должен вернуть то же 400 (и желательно то же тело ответа с объяснением). Это снижает «тремор» клиента, который при ретраях не должен внезапно получать 201.

Если ошибка возникла на сервере (500) — здесь особенно полезно сохранять результат выполнения как минимум до определённого TTL, чтобы не создавать двойные побочные эффекты.

Практический вывод:

  • сохраняйте результат выполнения для всех конечных исходов, которые могут повлиять на состояние (успех/отказ/конфликт);
  • для «в процессе» сохраняйте ссылку на операцию (operation id), чтобы ретрай мог вернуться в предсказуемую ветку.

Схемы гарантий повторяемости: от «минимума» до строгой детерминированности

Схема 1: Idempotency lock + хранение ответа (практически стандарт)

Это базовый и рабочий подход:

  • При первом запросе по ключу:

    • создать запись в таблице идемпотентности со статусом in_progress;
    • выполнить команду;
    • сохранить итоговый статус и результат (ответ) в таблицу;
    • пометить completed.
  • При повторе:

    • если статус completed — вернуть сохранённый ответ;
    • если in_progress — либо вернуть processing + resultId, либо подождать (с осторожностью к таймаутам).

Чтобы избежать гонок, ключ должен быть защищён уникальным ограничением в БД.

Пример таблицы (схематично):

  • idempotency_requests(tenant_id, principal_id, key, status, response_json, result_id, created_at, updated_at)

В SQL-подобном виде уникальный индекс:

code
CREATE UNIQUE INDEX uniq_idempotency_key
ON idempotency_requests (tenant_id, principal_id, key);

Схема 2: Контроль payload-хэша (защита от конфликтов)

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

Решение: хранить хэш тела запроса и сравнивать при повторе.

  • При первом запросе: посчитать hash = SHA256(normalized_body) и сохранить.
  • При повторе с тем же ключом: сравнить хэш. Если не совпадает — вернуть 409 Conflict (или 400 с понятным сообщением).

Важно: нормализация тела — отдельная дисциплина. JSON в разных порядках ключей может быть эквивалентным по смыслу, но разным по байтам. Если вы сравниваете хэш «как есть», конфликтов будет больше. Если вы нормализуете — конфликтов меньше, но вы должны быть уверены в стабильности нормализации.

Пример логики:

code
import json, hashlib

def stable_json_hash(obj) -> str:
    normalized = json.dumps(obj, separators=(",", ":"), sort_keys=True, ensure_ascii=False)
    return hashlib.sha256(normalized.encode("utf-8")).hexdigest()

Схема 3: Event-sourcing или outbox (усиление детерминизма)

Если ваша архитектура ближе к event-sourcing или у вас есть outbox pattern для доставки событий наружу, идемпотентность становится частью общего механизма гарантии «не дублировать побочные эффекты».

Схема обычно комбинируется:

  • При выполнении команды создаётся event/intent по ключу.
  • Outbox хранит запись о том, что именно отправлено.
  • При ретраях не создаются повторные события.

В таком варианте идемпотентность — не только про HTTP-ответ, но и про консистентность интеграций.

Схема 4: Комбинация с PUT и ресурсным идентификатором

Иногда команда по смыслу — это «создать ресурс с известным id». Тогда можно использовать PUT и в URL зашить resourceId:

  • PUT /v1/orders/{orderId}

В этом случае повтор PUT с тем же orderId уже семантически ближе к идемпотентности: вы обновляете один и тот же ресурс. Но на практике проблема «таймаут на ответе» остаётся: клиент может не знать, что именно сервер сохранил. Поэтому даже при PUT часто всё равно нужен idempotency key или контроль версий/ETag для спокойствия.


Типовые ошибки проектирования идемпотентных команд

Ошибка 1: ставить Idempotency-Key только на «создание»

Наивный взгляд: ключ нужен только для POST /create.

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

  • платежи (POST /payments)
  • резервирование (POST /reservations)
  • отмена подписки (POST /subscriptions/{id}/cancel)
  • отправка сообщений (POST /messages:send) — часто особенно опасно, потому что дубли реально заметят пользователи

Ошибка 2: игнорировать ретрай при ошибках

Если при первом запросе случился 500, клиент повторит — и вы снова попытаетесь выполнить команду. Если операция не защищена, вы получите двойные эффекты (например, двойное списание).

Правильнее: ретраи должны возвращать ту же картину исхода — иначе клиент не может строить корректные сценарии.

Ошибка 3: «вернуть 201 заново» вместо сохранения результата

Если сервер просто «не создаёт второй раз», но при ретраях отдаёт новый orderId или изменённый ответ, клиент потеряет воспроизводимость контракта.

Идемпотентность — это не «не было второго эффекта», а «результат воспроизводим».

Ошибка 4: не документировать политику TTL и конфликтов

Документация должна включать как минимум:

  • на какой период сервер помнит ключи;
  • что будет при истечении TTL;
  • что при несовпадении payload (хэш) и как это кодируется в ответах.

Без этого идемпотентность превращается из гарантии в «почти гарантию», что противоречит самой цели механизма.

Ошибка 5: слишком маленькое время хранения

Если TTL слишком короткий, ретраи на уровне клиента при умеренной деградации сети могут происходить позже — и вы получите дубликаты. TTL — компромисс между стоимостью хранения и риском. Лучше оценить по SLO/ретрай-политикам клиентов и типичному времени выполнения команд.


Практические паттерны API: что писать клиенту и серверу

Заголовки и ответы: формализуйте коммуникацию

Рекомендуемая минимальная спецификация для идемпотентных команд:

  • Клиент:

    • генерирует Idempotency-Key (обычно UUID),
    • повторяет запрос с тем же ключом при таймауте/сети,
    • при 409 Conflict должен понимать, что ключ конфликтует с другим payload — чаще всего это означает баг в клиенте.
  • Сервер:

    • принимает ключ,
    • валидирует payload (и/или payload-хэш),
    • возвращает предсказуемый ответ.

Пример кода ответа при конфликте payload-хэша:

code
409 Conflict
Content-Type: application/json

{
  "error": "IdempotencyKeyConflict",
  "message": "Same Idempotency-Key used with different request payload.",
  "idempotencyKey": "7f2c3e2e-3b5b-4eaa-9c6f-1d8d9f8d3c12"
}

Контракт ошибки: повтор должен быть безопасным

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

Практический набор полей ошибки:

  • error (машиночитаемый код),
  • message (человекочитаемый),
  • details (если нужно),
  • traceId (для диагностики).

Реализация на практике: пример на условном Python/Flask и уровне идемпотентности

Ниже не «идеальная» реализация, а показательная: как встроить идемпотентность как механизм вокруг команды.

Псевдоконтракт и структура хранилища

Предположим, у нас есть функция execute_command(payload) и БД с таблицей идемпотентности.

code
import hashlib, json, uuid
from datetime import datetime, timedelta

def stable_json_hash(obj) -> str:
    normalized = json.dumps(obj, separators=(",", ":"), sort_keys=True, ensure_ascii=False)
    return hashlib.sha256(normalized.encode("utf-8")).hexdigest()

def idempotency_key_required(request):
    key = request.headers.get("Idempotency-Key")
    if not key:
        raise ValueError("Idempotency-Key header is required for this endpoint")
    return key

def handle_create_order(request, db):
    key = idempotency_key_required(request)
    payload = request.json

    payload_hash = stable_json_hash(payload)

    principal_id = request.user.id
    tenant_id = request.tenant.id

    # 1) Попытка "забронировать" ключ
    # INSERT ... ON CONFLICT DO NOTHING / или транзакционная логика
    record = db.idempotency_get(tenant_id, principal_id, key)

    if record is None:
        # Создаём запись
        db.idempotency_create(
            tenant_id=tenant_id,
            principal_id=principal_id,
            key=key,
            payload_hash=payload_hash,
            status="in_progress",
            created_at=datetime.utcnow()
        )

        try:
            # 2) Выполняем команду
            result = execute_command(payload)  # side effects happen here
            response_body = {
                "status": "succeeded",
                "orderId": result["order_id"],
                "links": {"self": f"/v1/orders/{result['order_id']}"}
            }

            # 3) Сохраняем завершение
            db.idempotency_complete(
                tenant_id=tenant_id,
                principal_id=principal_id,
                key=key,
                status="completed",
                response_json=response_body,
                result_id=result["order_id"]
            )

            return 201, response_body

        except Exception as e:
            # Здесь важно: сохранять негативный результат.
            error_body = {
                "error": "InternalError",
                "message": "Order creation failed.",
                "traceId": "..."  # в реальном коде из логов/observability
            }
            db.idempotency_complete(
                tenant_id=tenant_id,
                principal_id=principal_id,
                key=key,
                status="completed",
                response_json=error_body,
                result_id=None,
                http_status=500
            )
            raise

    # 4) Ключ уже существовал
    if record.payload_hash != payload_hash:
        return 409, {
            "error": "IdempotencyKeyConflict",
            "message": "Same Idempotency-Key used with different request payload.",
            "idempotencyKey": key
        }

    if record.status == "completed":
        # Возвращаем сохранённый ответ детерминированно
        return record.http_status or 200, record.response_json

    # in_progress: либо вернуть processing+resultId, либо подождать
    return 202, {
        "status": "processing",
        "orderId": record.result_id,  # может быть None, если ещё не присвоили
        "links": {"self": "/v1/operations/..."}
    }

Ключевой момент

Идемпотентность — это не «магия заголовка», а транзакционная дисциплина:

  • уникальность ключа на уровне (tenant, principal, key),
  • сохранение результата (или достаточной информации для воспроизведения ответа),
  • защита от конфликтов payload-хэша.

Политика повторов на стороне клиента: чтобы гарантия работала

Даже идеальный сервер не создаст гарантию, если клиент повторяет неправильно. Минимальные правила:

  1. Ретраить только команды и использовать Idempotency-Key только для них.
  2. Повторять с тем же ключом до получения финального ответа.
  3. Ожидать одинакового результата и использовать orderId/resultId из ответа для дальнейших шагов.
  4. При получении 409 IdempotencyKeyConflict — это обычно сигнал о проблеме сериализации/построения запроса на клиенте, а не о временной сетевой ошибке.

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


REST и идемпотентность: как не сделать “RPC в коробке”

Одна из причин, почему идемпотентность обсуждают сложно — потому что граница между REST-ресурсами и RPC-командами размывается. Но подход с идемпотентными ключами одинаково применим в обоих случаях, если вы:

  • чётко отделяете операции чтения (ресурсы, GET) от изменений;
  • для изменяющих операций проектируете контракт результата и поведение при ретраях;
  • не маскируете команды под «нейтральные» методы, не документируя semantics.

Даже если endpoint командный по смыслу (POST /payments, POST /cancel), идемпотентность делает его предсказуемым для HTTP-мира.


Практический чеклист для проектирования

На уровне API контракта

  • Endpoint изменяет состояние? → нужна идемпотентность.
  • Клиент может получить таймаут/обрыв? → нужен Idempotency-Key.
  • Ответ при ретраях должен быть воспроизводим? → сохраняйте result и ответ.
  • Что при несовпадении payload? → 409 Conflict и/или сравнение payload-хэша.
  • Политика TTL и правила повторов описаны документально.

На уровне хранения/согласованности

  • Уникальный индекс на ключ (с учётом tenant/principal).
  • Транзакции или атомарная запись статуса in_progress → completed.
  • Сохранение негативных исходов (ошибок), если они могли быть финальными для состояния.
  • Отдельная сущность/таблица идемпотентности, а не «память в приложении».

На уровне наблюдаемости

  • Trace ID в ответах и логах.
  • Метрики: доля in_progress, доля конфликтов, время выполнения команд, hit-rate по идемпотентности.
  • Корректная обработка ретраев в логике gateway/ingress (чтобы ключ не терялся).

Вывод: идемпотентность как часть контракта, а не “добавка к коду”

Идемпотентные команды в REST API — это не просто паттерн заголовка. Это проектирование предсказуемости на уровне контракта и хранилища: вы фиксируете, что означает «та же команда», где храните результат, как возвращаете ответ на ретрай и что делаете при конфликте.

Если свести к главному, то хороший дизайн идемпотентности отвечает на три вопроса:

  1. Что идентифицирует намерение клиента? — Idempotency-Key (и при необходимости payload-хэш).
  2. Где гарантируется повторяемость результата? — в БД с атомарной моделью статусов и сохранением ответа.
  3. Как клиент продолжает сценарий? — через детерминированный resultId и согласованные статусы (succeeded/processing/failed).

Если вы хотите глубже разобраться в том, как системно проектировать такие вещи (включая практики интеграций, ретраи, наблюдаемость и тестирование идемпотентных сценариев), полезно посмотреть специализированные материалы — например, курс по этой теме можно изучить через /course/ как один из вариантов структурировать знания и закрепить их на практике.


Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу

Приложения для iPhone и Apple Watch на SwiftUI

Разработка приложений для iPhone и Apple Watch на SwiftUI: навигация, SwiftData, виджеты, часы, выпуск. Нужен Mac с Xcode 27, сами устройства не нужны.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу

Приложения для macOS на SwiftUI

Разработка приложений для Mac на SwiftUI: окна и меню, Liquid Glass, SwiftData, сеть, выпуск. Нужен Mac с macOS 27 и Xcode 27.

Перейти к курсу

Другие статьи

Как читать и писать REST API контракты: статусы, ошибки и схемы ответов
читать

Как читать и писать REST API контракты: статусы, ошибки и схемы ответов

Разберём типовые ошибки в дизайне API и научимся фиксировать контракт: коды HTTP, формат ошибок, версионирование и примеры запросов/ответов.

20 июля 2026 г.
680
Проектируем REST API без боли: контракты версий, идемпотентные операции и совместимость клиентов
проектируем

Проектируем REST API без боли: контракты версий, идемпотентные операции и совместимость клиентов

Соберём «скелет» API-договора: как выбирать версионирование, какие операции делать идемпотентными, как описывать форматы ошибок и полей. Статья поможет заранее договориться о правилах, чтобы изменения не ломали фронт и интеграции.

31 июля 2026 г.
470
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1251
Что такое переменные, типы и условия в коде: объясню без матана на примерах
такое

Что такое переменные, типы и условия в коде: объясню без матана на примерах

Разберём ключевые понятия для старта — переменные, типы данных, сравнения и условные операторы — простыми словами и на бытовых примерах. В конце соберём мини-скрипт, который принимает решение по введённым данным.

30 сентября 2026 г.
20
FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь
fastapi

FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь

Поймём различия между синхронной обработкой, BackgroundTasks и внешними очередями. Разберём idempotency, ретраи и мониторинг фоновых процессов.

22 июля 2026 г.
830
Нужно ли мне знать математику, чтобы начать программировать?
знать

Нужно ли мне знать математику, чтобы начать программировать?

Разберём, где математика реально нужна (и где нет) для новичка: основы Python/веб/автоматизация/аналитика. В конце составим понятный маршрут обучения без лишней теории и подскажем, что повторить, если вы чувствуете пробелы.

28 сентября 2026 г.
70

Комментарии

Пока нет комментариев

Содержание

Чтение и команды: где идемпотентность действительно нужнаREST и HTTP семантика: не смешивайте чтение и изменениеЧто именно считать «одной и той же» командойКлюч идемпотентности: формат, место и правилаИдея: idempotency key как «намерение клиента»Где вводить key в REST контрактеСтабильность ключа и границы действияКак проектировать тела запросов и ответов для повторяемостиПринцип: ответ должен быть воспроизводимСтандартный контракт: envelope и единая семантикаЧто делать со временем выполнения: accepted/processing/succeededОшибка и повтор: нужно ли сохранять негативный результатСхемы гарантий повторяемости: от «минимума» до строгой детерминированностиСхема 1: Idempotency lock + хранение ответа (практически стандарт)Схема 2: Контроль payload-хэша (защита от конфликтов)Схема 3: Event-sourcing или outbox (усиление детерминизма)Схема 4: Комбинация с PUT и ресурсным идентификаторомТиповые ошибки проектирования идемпотентных командОшибка 1: ставить Idempotency-Key только на «создание»Ошибка 2: игнорировать ретрай при ошибкахОшибка 3: «вернуть 201 заново» вместо сохранения результатаОшибка 4: не документировать политику TTL и конфликтовОшибка 5: слишком маленькое время храненияПрактические паттерны API: что писать клиенту и серверуЗаголовки и ответы: формализуйте коммуникациюКонтракт ошибки: повтор должен быть безопаснымРеализация на практике: пример на условном Python/Flask и уровне идемпотентностиПсевдоконтракт и структура хранилищаКлючевой моментПолитика повторов на стороне клиента: чтобы гарантия работалаREST и идемпотентность: как не сделать “RPC в коробке”Практический чеклист для проектированияНа уровне API контрактаНа уровне хранения/согласованностиНа уровне наблюдаемостиВывод: идемпотентность как часть контракта, а не “добавка к коду”