Вопрос 4 · Раздел 6

В чём разница между PUT и PATCH?

Оба метода используются для обновления ресурса, но работают по-разному.

Версии по языкам: English Russian Ukrainian

Junior Level

Оба метода используются для обновления ресурса, но работают по-разному.

PUT — Полное обновление

  • Заменяет всё представление ресурса на то, что прислал клиент. Поля, которых нет в теле запроса, должны быть удалены или сброшены в значения по умолчанию.
  • Нужно отправить все поля, даже те, что не изменились
  • Если не отправить какое-то поле — оно будет удалено или установлено в null
PUT /users/1
{
  "name": "Ivan",
  "age": 30,
  "email": "ivan@example.com"
}

PATCH — Частичное обновление

  • Обновляет только те поля, которые были отправлены
  • Экономит трафик: для объекта в 200 полей изменение одного поля через PATCH экономит ~95% тела запроса.
PATCH /users/1
{
  "age": 31
}
// Остальные поля остаются без изменений

Сравнение:

Критерий PUT PATCH
Объём данных Полный объект Только изменённые поля
Идемпотентность Всегда идемпотентен Зависит от реализации
Аналогия в SQL UPDATE всех колонок UPDATE конкретных колонок

Когда PUT лучше PATCH

Используйте PUT для полных замен небольших объектов — проще в реализации и однозначно. Используйте PATCH для больших объектов или когда несколько клиентов могут параллельно менять разные поля.


Middle Level

Семантика методов

PUT (Replace)

  • Идемпотентность: Всегда идемпотентен. Сколько бы раз ни отправить один и тот же запрос — результат одинаковый
  • DB Analogy: UPDATE users SET name=?, age=?, email=? ... (обновление всех колонок)

PATCH (Partial Update)

  • Идемпотентность: Не гарантируется. Зависит от реализации
  • DB Analogy: UPDATE users SET age = age + 1 (не идемпотентно) или SET age = 30 (идемпотентно)

Спецификации PATCH

  1. JSON Merge Patch (RFC 7386):
    • Клиент шлёт JSON: {"age": 31}
    • Сервер просто мержит его с текущим объектом
    • Минус: Сложно удалить поле (нужно договариваться, что null — это удаление)

    Ключевое отличие: Merge Patch говорит «что изменить» (декларативно), а JSON Patch говорит «как изменить» (последовательность операций: add, remove, replace).

  2. JSON Patch (RFC 6902):
    • Клиент шлёт массив инструкций:
      [
      { "op": "replace", "path": "/age", "value": 31 },
      { "op": "remove", "path": "/middleName" }
      ]
      
    • Плюс: Атомарность и гибкость. Можно добавлять элементы в массивы, удалять конкретные поля

Оптимистическая блокировка (ETag)

  1. Клиент получает ресурс с заголовком ETag: "v1"
  2. Клиент шлёт PUT/PATCH с заголовком If-Match: "v1"
  3. Если ETag стал “v2” — сервер вернёт 412 Precondition Failed

Диагностика

  • 422 Unprocessable Entity: Если PATCH логически невыполнима (например, удаление обязательного поля)
  • Hibernate/JPA: При PATCH используйте nullValuePropertyMappingStrategy = IGNORE в MapStruct, чтобы случайно не затереть поля

Senior Level

Проблема “Lost Updates” и Optimistic Locking

Что если два пользователя одновременно прочитали ресурс (age=20) и решили его обновить?

  • PUT-проблема: Первый обновил до 21, второй — до 25. Изменения первого потеряны.
  • PATCH-решение: Если PATCH делает age = age + 1, итог будет 22 (оба изменения учтены).

Производительность и Highload

  • Bandwidth: PATCH экономит трафик на больших объектах (профиль с сотнями полей)
  • Validation: PUT проще валидировать (полная схема). PATCH требует сложной логики валидации “только присланных полей”
  • Reflection в Java: Реализация PATCH часто требует Jackson JsonNode для определения, было ли поле null или отсутствовало

Реализация PATCH в Spring

Будьте осторожны с merge() в Hibernate. Если создать новый объект и вызвать merge(), можно случайно затереть поля, не пришедшие в PATCH, превратив его в кривой PUT.

Edge Cases

  • Если PATCH содержит {"op": "add", "path": "/tags", "value": "new"} — это не идемпотентно (при повторе добавится ещё один тег)
  • Для идемпотентности PATCH используйте детерминированные операции (replace, remove)

🎯 Шпаргалка для интервью

Обязательно знать:

  • PUT заменяет ресурс целиком — поля, которых нет в запросе, удаляются или обнуляются
  • PATCH обновляет только присланные поля — экономит трафик на больших объектах
  • PUT всегда идемпотентен; PATCH идемпотентен только при детерминированных операциях
  • JSON Merge Patch (RFC 7386) — декларативный подход: «что изменить»
  • JSON Patch (RFC 6902) — императивный подход: массив операций (add, remove, replace)
  • Optimistic Locking через ETag + If-Match предотвращает конфликт параллельных обновлений
  • PUT проще валидировать (полная схема), PATCH требует сложной валидации частичных данных

Частые уточняющие вопросы:

  • Что такое Lost Update Problem? — Два клиента читают ресурс и обновляют параллельно; изменения первого теряются
  • Как реализовать PATCH в Hibernate? — Использовать nullValuePropertyMappingStrategy = IGNORE в MapStruct
  • Что вернёт сервер при 412 Precondition Failed? — ETag изменился с момента чтения, ресурс устарел
  • Когда PUT лучше PATCH? — Для небольших объектов и когда нужна однозначная полная замена

Красные флаги (НЕ говорить):

  • «PATCH всегда идемпотентен» — add в коллекцию не идемпотентен
  • «PUT обновляет только переданные поля» — PUT заменяет весь объект целиком
  • «Разницы между PUT и PATCH нет» — PUT = полная замена, PATCH = частичное обновление
  • «Merge Patch и JSON Patch — одно и то же» — Merge Patch декларативна, JSON Patch — последовательность операций

Связанные темы:

  • [[Что такое идемпотентность (idempotency)]]
  • [[Какие HTTP методы идемпотентны]]
  • [[Какие основные HTTP методы используются в REST]]
  • [[Как правильно именовать REST endpoints]]