коротко

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

Можно семь лет писать требования и ни разу честно не ответить себе на вопрос «а где это вообще выполняется». Я видел ТЗ, где «сделать на странице» означало три разные вещи на трёх платформах — и никто этого не заметил, пока не дошло до разработки. Поэтому короткая база. Не весь интернет — только то, без чего дальше нельзя.

Что такое программа и компьютер (на пальцах)

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

Когда программ и пользователей становится много, держать всё на одном устройстве неудобно. Поэтому работу делят между двумя сторонами — так появляется клиент-сервер.

Клиент и сервер: кто за что отвечает

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

«Не знает в лицо» — это не фигура речи, а архитектура

Сервер обычно делают stateless: он не помнит между запросами, кто вы. Каждый HTTP-запрос приходит сам по себе, и сервер по нему не догадывается, что минуту назад вы уже залогинились. Поэтому клиент кладёт в каждый запрос доказательство личности — токен (часто в заголовке Authorization: Bearer …) или идентификатор сессии в cookie. Сервер проверяет его заново на каждом запросе и только тогда понимает, чьё это действие. Почему так, а не «запомнить разок»: запросы одного пользователя могут попадать на разные машины за балансировщиком, и если бы личность хранилась в памяти конкретной машины, при переключении вы бы «разлогинивались». Stateless — это то, что даёт серверу масштабироваться: добавил ещё одну машину, и она сразу умеет обслужить любого, потому что вся нужная информация приехала в самом запросе.

sequenceDiagram
  participant U as Пользователь
  participant C as Клиент (браузер/приложение)
  participant S as Сервер
  participant DB as База данных
  U->>C: нажал «Купить»
  C->>S: HTTP-запрос: создать заказ
  S->>DB: сохранить заказ
  DB-->>S: ок, id=123
  S-->>C: ответ: заказ создан
  C-->>U: показал «Спасибо за заказ»

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

Какие бывают клиенты

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

  • Веб-клиент — работает в браузере, ничего не нужно устанавливать. Обновляется мгновенно (перезагрузил страницу — новая версия).
  • Мобильный клиент — приложение под iOS/Android, ставится из стора. Обновление проходит через ревью стора и доходит не до всех сразу — отсюда требование обратной совместимости.
  • Десктоп-клиент — программа под Windows/macOS/Linux. Ставится локально, имеет доступ к файлам и железу.

Толстый и тонкий клиент

Тонкий клиент почти ничего не считает сам — вся логика на сервере, клиент только показывает (классический сайт). Толстый клиент держит часть логики и данных у себя и может работать оффлайн (мобильное приложение, редактор документов). Чем «толще» клиент, тем больше вопросов синхронизации и обратной совместимости вам как аналитику придётся закрывать.

Что на самом деле делает браузер

Браузер — это тоже клиент, просто универсальный. Он умеет три вещи: сходить по адресу (URL) и скачать страницу, нарисовать её (HTML — структура, CSS — оформление, JavaScript — поведение) и отправлять на серверы запросы по HTTP. Когда «веб-приложение тормозит», причина почти всегда в одном из трёх: медленный ответ сервера, тяжёлая отрисовка или много лишних запросов.

Откуда это взялось

Первый браузер написал Тим Бернерс-Ли в 1990 году (он назывался WorldWideWeb). Массовым веб сделал Mosaic в 1993-м — он первым показывал картинки прямо в тексте. С тех пор браузер из «смотрелки документов» превратился в полноценную платформу для приложений: сегодня в нём работают почты, таблицы, видеозвонки. Поэтому граница между «сайтом» и «приложением» давно стёрлась — и в требованиях это надо проговаривать явно.

Фронтенд и бэкенд

Отсюда же растёт деление разработчиков. Фронтенд — то, что выполняется на стороне клиента: интерфейс, его поведение. Бэкенд — то, что на сервере: бизнес-логика, работа с базой, интеграции. Фулстек — и то, и другое. Когда вы пишете требование, полезно сразу понимать, чья это зона: «валидировать поле» на фронте (быстро, но обходится) — это не то же самое, что валидация на бэке (надёжно, но требует запроса).

Сеть ненадёжна — и это требование, а не оговорка

Между клиентом и сервером всегда есть сеть, а сеть периодически тупит, теряет пакеты и обрывается. Это не «иногда у пользователя плохой интернет» — это базовое свойство, на которое вы проектируете всегда. Запрос ушёл, ответа нет — и клиент в этот момент не знает, что случилось: запрос не дошёл до сервера, дошёл, но потерялся ответ, или сервер всё сделал и просто медлит. Три разных мира, а с точки зрения клиента — одна и та же тишина. Из этой тишины растут три понятия, которые надо закладывать в требования с самого начала.

  • Таймаут — сколько клиент ждёт ответа, прежде чем считать запрос провалившимся. Ждать вечно нельзя: пользователь смотрит на крутилку. Но и обрезать рано тоже плохо — ответ мог быть уже в пути. Таймаут — это всегда явное число в требовании, а не «подождём, сколько надо».
  • Ретрай — повтор запроса после неудачи. Логичная реакция на тишину: не ответили — спросим ещё раз. Но именно здесь прячется ловушка: первый запрос мог пройти, а потерялся только ответ. Тогда повтор сделает то же самое второй раз.
  • Идемпотентный ключ — уникальный идентификатор намерения, который клиент кладёт в запрос (обычно в заголовок) и не меняет при повторах. Сервер по нему понимает: «это тот же самый запрос, что и пять секунд назад», — и вместо второго действия возвращает результат первого. Повтор становится безопасным.

Связка простая: сеть ненадёжна → нужны таймаут и ретрай → ретрай порождает дубли → от дублей защищает идемпотентный ключ. Пропустите любое звено — и получите классику.

Двойной клик «Купить» → два заказа

Пользователь нажал «Купить», крутилка крутится (медленный ответ сервера), и он жмёт ещё раз. Уходят два одинаковых запроса. Если бэкенд просто «создаёт заказ на каждый пришедший запрос» — будет два заказа и два списания. То же самое случается без всякого пользователя: клиент сам делает ретрай по таймауту, хотя первый запрос на самом деле прошёл. Это и есть доставка «хотя бы один раз» (at-least-once): по сети честно гарантируется только что сообщение дойдёт не меньше одного раза, а вот «ровно один раз» сеть не обещает. Поэтому в требованиях к любой создающей деньги или сущности операции пишут: повторный запрос с тем же идемпотентным ключом не создаёт второй заказ, а возвращает тот же результат. Кнопку на фронте задизейблить после клика — полезно для UX, но это не защита: запрос всё равно может прийти дважды по сети. Как устроена эта защита внутри — подробно в записи про идемпотентность.

Спросить и ждать или отправить и забыть

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

ПаттернКогда уместенЧто закладывать в требования
Синхронный (HTTP-запрос-ответ)Ответ нужен немедленно, чтобы продолжить (проверить логин, показать цену, подтвердить заказ). Операция быстрая.Таймаут; что показать, если сервис не ответил; ретрай + идемпотентный ключ для повторов.
Очередь (асинхронно)Ответ можно отдать потом; нужно пережить пики нагрузки и падение получателя; долгая обработка.Доставка at-least-once → идемпотентность обработчика; статус «в обработке» для пользователя; порядок и дубли сообщений.
Вебхук (сервер сам зовёт клиента)Внешняя система сообщает о событии, когда оно произошло (платёж прошёл, посылка доставлена), а не когда мы спросили.Проверка подлинности вызова (подпись); повторные доставки → идемпотентность; что делать, если наш endpoint лежал.
Поллинг (клиент периодически спрашивает)Вебхук недоступен или ненадёжен; нужно простое решение «узнать статус задачи».Интервал опроса (баланс свежесть/нагрузка); до каких пор опрашивать; статусы, которые сервер обязан вернуть.

Здесь это только обзорная карта развилки — чтобы вы узнавали паттерны в чужой архитектуре. Глубоко trade-off'ы синхронного и асинхронного разобраны в отдельной записи про синхронные и асинхронные интеграции.

Где валидировать: фронт — это UX, бэк — это безопасность

Раз код фронтенда выполняется на устройстве пользователя, значит, он полностью под контролем пользователя. Любую проверку в браузере можно обойти: открыть инструменты разработчика (DevTools) и поменять значение, отключить JavaScript, отредактировать форму или вообще отправить запрос на сервер напрямую — мимо вашей страницы, любым HTTP-клиентом, с любым телом. Из этого следует жёсткое правило, которое стоит выучить наизусть.

  • Валидация на фронте — это UX. Она нужна, чтобы пользователь сразу видел «email без @» или «пароль слишком короткий», не дожидаясь запроса. Быстро и приятно — но никакой защиты не даёт, потому что обходится.
  • Валидация на бэке — это безопасность. Она нужна, чтобы система не сломалась и не пустила мусор в базу. Серверная проверка обязательна всегда, даже если ровно та же проверка уже есть на фронте.

Главный принцип целиком звучит так: клиенту не доверяй. Никаким данным, которые пришли с клиента, верить нельзя — ни цене товара, ни идентификатору пользователя, ни флагу «я админ». Всё, что влияет на деньги, права и целостность данных, сервер обязан проверить и пересчитать сам. Это значит, что в требованиях проверки дублируются осознанно: одна на фронте «для удобства», та же — на бэке «по-настоящему», и это не избыточность, а разные роли. Подробнее, как это разворачивается в безопасность системы, — в записи про основы безопасности для аналитика.

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

Частые вопросы

Что такое клиент-серверная архитектура простыми словами?

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

Чем тонкий клиент отличается от толстого?

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

Зачем аналитику понимать, как работает браузер?

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