Введение: вопрос-разминка, который топит кандидатов

«В чём разница между GET и POST?» — обычно это разминка. Но ответ «GET — получает, POST — отправляет» сразу выдаёт кандидата, который не читал спецификацию: интервьюер начинает копать, и за «получает/отправляет» следует три вопроса, на которые ответить сложнее.

В реальной жизни разница между GET и POST выходит далеко за рамки «куда положили данные»: безопасность, идемпотентность, кэширование, поведение при ретраях — именно эти понятия проверяют на собеседованиях. Формально методы описаны в RFC 9110, раздел 9, и оттуда стоит брать формулировки.

Что говорит спецификация

GET: метод запрашивает передачу текущего представления целевого ресурса. Простыми словами — «дай мне данные по этому адресу». GET — основной механизм получения информации в HTTP: по нему работает практически весь веб.

POST: метод просит целевой ресурс обработать представление, переданное в теле запроса, по его собственной семантике. То есть POST не «получает» и даже не всегда «создаёт»: сервер решает, что сделать с присланными данными — создать запись, запустить расчёт, отправить письмо.

Отсюда практические следствия:

  • Параметры GET живут в URI: GET /notes?author=5. Тело запроса для GET не имеет общепринятой семантики — RFC прямо пишет, что клиент не должен отправлять контент в GET-запросе без особой договорённости с сервером.
  • Данные POST передаются в теле запроса, и это уместно для больших объёмов и чувствительных данных: они не попадают в URL, в историю и в логи прокси.

Ещё один нюанс — поведение при перенаправлениях. Коды редиректов 307 и 308 гарантируют, что метод сохранится: если на POST сервер ответил 307, повторный запрос снова будет POST. Это зафиксировано в спецификации, и знание этой детали — приятный бонус в ответе: интервьюер видит, что кандидат не ограничивается списком «основных» кодов.

Короткий ответ на собеседовании

GET — «дай текущее представление ресурса», безопасный и идемпотентный. POST — «обработай это представление по твоей семантике», небезопасный и неидемпотентный. Разница не в «получает/отправляет», а в контракте с сервером.

Справочные страницы GET и POST на MDN — удобный источник формулировок для повторения перед собеседованием.

Безопасность и идемпотентность

Два понятия, которые отличают ответ «читал спецификацию» от «слышал на лекции».

Безопасный метод (safe) — не изменяет состояние сервера: можно вызывать сколько угодно раз без побочных эффектов. GET безопасен, POST — нет.

Идемпотентный метод — эффект от нескольких одинаковых запросов такой же, как от одного. По RFC 9110 идемпотентны: безопасные методы (GET, HEAD), а также PUT и DELETE. POST в этот список не входит — повторный POST может создать вторую запись, отправить второе письмо, списать деньги дважды.

Почему это важно на практике: при обрыве соединения клиент может безопасно повторить GET автоматически — эффект будет тот же. А вот с POST спецификация прямо предупреждает: клиент не должен автоматически повторять неидемпотентный запрос, если нет способа убедиться, что исходный запрос не был применён.

Ошибка-классика: «GET можно повторять, POST нельзя — потому что POST ломает сервер». Формулировка должна быть точной: GET повторять безопасно, потому что он идемпотентный. POST повторять нельзя, потому что эффект повторного применения может отличаться от первого.

Из идемпотентности вытекает и кэширование: ответ на GET кэшируется по умолчанию — это основа работы веб-кэшей, CDN и браузеров. Ответ на POST кэшируется только при явных указаниях. Для Android это значит: повторное открытие экрана со списком заметок — GET, и его можно кэшировать; создание заметки — POST, и его повторять нельзя.

Пример для собеседования — заказ товара. GET /orders/42 можно повторять сколько угодно: на заказ это никак не повлияет. POST /orders при повторе создаст новый заказ, если сервер не защищён от дублей. Поэтому платёжные и заказные API всегда добавляют защиту: ключ идемпотентности или уникальный id запроса в заголовке — и повторный POST с тем же ключом вернёт результат первого применения.

Где кандидаты ошибаются

Три типичных промаха, которые я регулярно вижу на собеседованиях:

Ошибка №1: «GET — для получения, POST — для отправки». Формально неверно: PUT и DELETE тоже «отправляют» данные, а POST может вообще ничего не создавать. Правильно говорить про семантику и свойства метода, а не про направление данных.

Ошибка №2: «POST можно повторять после таймаута». Повтор POST после обрыва соединения — риск дублирования на сервере. Правильное решение — идемпотентность на уровне приложения: ключ идемпотентности в заголовке или проверка на сервере, а не слепой ретрай.

Ещё два промаха:

  • GET с телом. Кандидат рассказывает, что отправлял данные в GET-запросе. По спецификации у тела GET нет общей семантики — такое работает только при договорённости с конкретным сервером.
  • POST для чтения. «Читать большие данные через GET неудобно, сделаю POST» — работает, но это нарушение контракта: ломает кэширование и семантику. Правильно — GET с пагинацией или фильтрами.
  • GET — безопасный и идемпотентный: «дай текущее представление ресурса».
  • POST — небезопасный и неидемпотентный: «обработай представление по твоей семантике».
  • Параметры GET — в URI, данные POST — в теле запроса.
  • Ответ на GET кэшируется, ответ на POST — только по явным указаниям.
  • Повторять GET после обрыва безопасно, повторять POST — риск дублирования.

GET и POST в Android

В Android разница проявляется на уровне Retrofit: аннотация метода определяет, как сформируется запрос.

ApiService.kt
interface ApiService {
    @GET("notes/{id}")
    suspend fun getNote(@Path("id") id: Long): Note

    @GET("notes")
    suspend fun getNotes(@Query("author") authorId: Long): List<Note>

    @POST("notes")
    suspend fun createNote(@Body note: Note): Note
}

Обратите внимание на @Query в GET — параметры уходят в URI, как и положено безопасному методу. @Body в POST — данные уходят в тело запроса, сериализованные конвертером.

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

  • Экран со списком и деталями — GET. При повторном открытии можно показать кэш, потом обновить.
  • Создание, изменение, удаление с эффектом — POST (или PUT/DELETE, если API умеет). Повтор — только по явному действию пользователя.
  • Ретрай-логика: GET можно автоматически повторить при обрыве сети, POST — с осторожностью: например, показать пользователю сообщение и дать ему решить, повторять ли.

Кэширование в Android строится на той же семантике: OkHttpClient.Builder().cache(...) — и GET-ответы начнут отдаваться из кэша. Это легальная оптимизация именно потому, что GET кэшируем по спецификации. POST в кэш не попадает: сервер каждый раз получит запрос.

Типичные вопросы-продолжения после «в чём разница»:

  • «Какой метод для поиска с фильтрами?» — GET с @Query-параметрами.
  • «А если список большой?» — пагинация, тоже GET: страница и размер в параметрах.
  • «Как сделать повтор POST безопасным?» — ключ идемпотентности в заголовке или проверка на сервере, а не слепой ретрай.

Ошибка-классика в проекте: «попробуем ещё раз через секунду» для POST при ошибке сети. Пользователь дважды нажал «создать» — получили два дубля заметки. Правильно: блокировать кнопку на время запроса и не делать автоматический ретрай неидемпотентных методов.

Нужна помощь в переходе на middle?

Бесплатная диагностика: разберу ваши пробелы в сети, HTTP и архитектуре и составлю план роста за 30–40 минут. Без обязательств — если менторство вам не подойдёт, честно скажу.

Итоги

GET и POST — не «получить и отправить», а два разных контракта с сервером:

  • GET — безопасный, идемпотентный, кэшируемый: «дай текущее представление ресурса».
  • POST — небезопасный, неидемпотентный: «обработай переданное представление по твоей семантике».
  • На практике: GET для чтения и кэширования, POST для создания и действий — без слепых автоматических ретраев.

Главное, что нужно запомнить

Скажете «GET безопасный и идемпотентный, POST — нет, поэтому GET можно повторять и кэшировать, а POST — нельзя» — и вопрос про разницу закрыт. Дальше интервьюер обычно спрашивает про ретраи и кэш, и вы уже готовы.

Если чувствуете, что сетевой блок — ваше слабое место, не нужно зубрить по десять статей. Напишите в Telegram, разберём вашу ситуацию и составим план подготовки.