TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.74K subscribers
Post #3234 1.64K
День 2701. #ЗаметкиНаПолях
Версионирование API Должно Быть Последним Средством. Продолжение

Начало

Правила совместимости
- Сохраняйте существующие поля и поведение;
- Не превращайте необязательные данные запроса в обязательные;
- Не меняйте то, что делает существующая операция;
- Делайте всё новое в добавление и необязательным по умолчанию.

1. Добавляйте, а не заменяйте
Самое безопасное изменение обычно является аддитивным. Допустим, первоначальный ответ от GET /orders/{id} был таким:
{
"id": "ord_123",
"status": "paid",
"total": 100
}

Вместо замены поля total добавим новое:
{
"id": "ord_123",
"status": "paid",
"total": 100,
"totalMoney": {
"amount": 100,
"currency": "USD"
}
}

Существующие клиенты продолжают использовать total. Новые могут перейти на totalMoney. Помечаем старое поле как устаревшее и удалим его только после реального периода миграции. Иногда некрасивый контракт — это цена совместимости.

2. Делайте клиентов толерантными
Хорошо работающий клиент не должен выдавать ошибку из-за того, что сервер добавил поле, которое он не понимает. Если ответ изменился с:
{
"id": "ord_123",
"status": "paid"
}

на:
{
"id": "ord_123",
"status": "paid",
"estimatedDeliveryDate": "2026-05-29"
}

существующие клиенты должны игнорировать дополнительное свойство и продолжать работу.

В System.Text.Json неизвестные свойства игнорируются по умолчанию. Реальный риск обычно заключается в чрезмерно строгой проверке JSON (об этом позже на канале). Это одна из распространённых проблем. Команды заявляют о желании обратной совместимости, а затем генерируют клиентские модели, которые отклоняют любое неожиданное поле в ответе. Клиенты должны быть достаточно толерантными, чтобы игнорировать то, чего они не понимают.

3. Не меняйте то, что делает существующая операция
Самые опасные критические изменения скрываются в поведении. URL, тело запроса, формат ответа те же. Но то, что делает операция на сервере, отличается. Например, DELETE /orders/{id} сначала реализовывал мягкое удаление, и заказ переходил в «архив», но по-прежнему отображался в отчётах аудита и мог быть восстановлен службой поддержки. Затем команда решила «почистить базу» и изменить поведение на жёсткое удаление. Ни один клиент сразу этого не заметит, но данные теперь «по-тихому» уничтожаются.

Аналогично:
- POST /orders раньше был идемпотентным, а затем незаметно перестал им быть;
- POST /orders/{id}/cancel раньше автоматически возвращал деньги, а затем перестал это делать, потому что «возвраты должны быть отдельным вызовом»;
- PUT /orders/{id} раньше был полной заменой, а теперь стал частичным слиянием;
- Веб-хук раньше срабатывал один раз для каждого заказа, а теперь срабатывает для каждой позиции заказа;
и т.п.

Каждый из этих вариантов сохраняет стабильность URL, метода и структуры JSON, но нарушает все существующие интеграции таким образом, что это не будет видно при сравнении схем. Безопасный шаг тот же: добавлять, а не изменять. Например, параметр в конечной точке для жёсткого удаления DELETE /orders/{id}?purge=true

Как только операция выпущена, её поведение становится частью контракта. Вы можете добавлять новые операции, можете объявить её устаревшей, но не можете незаметно изменять её работу.

4. Будьте осторожны с валидацией
Существует два варианта одной и той же ошибки:
- Сделать обязательным существующее необязательное поле;
- Добавить новое обязательное поле.
Оба варианта ломают существующих клиентов. Путь к конечной точке не меняется, но запросы, которые раньше выполнялись успешно, теперь отклоняются. Более безопасный путь — определить значения по умолчанию или ввести новую операцию для более строгого рабочего процесса.

Изменения в ответах обычно тщательно проверяются с точки зрения проектирования. Изменения в проверке запросов заслуживают такого же внимания. Главное правило: то, что вы добавляете в контракт, должно быть необязательным, и всё, что было необязательным, должно оставаться необязательным.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/api-versioning-should-be-your-last-resort
  • 👍 6
More from @netdeveloperdiary
  1. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  2. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
  3. Sep 24, 2026День 2794. #Оффтоп #Здоровье Сегодня будет необычный пост. Завтра в Москве стартует конфер…
  4. Sep 23, 2026День 2793. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  5. Sep 22, 2026День 2792. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  6. Sep 21, 2026🔍Тестовое собеседование с Senior C# разработчиком уже завтра 22 сентября(уже завтра!) в 1…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →