Введение: вопрос-разминка, который топит кандидатов
«В чём разница между 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: аннотация метода определяет, как сформируется запрос.
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, разберём вашу ситуацию и составим план подготовки.