Часть I: Фундамент — как мы здесь оказались
Глава 2

Краткая история веб-архитектуры (и уроки из неё)

От CGI до serverless — как менялись подходы и что остаётся неизменным.


#Введение: зачем разработчику история?

Если ты читаешь эту книгу, ты пишешь код. Может быть, каждый день. Используешь фреймворки, библиотеки, облачные сервисы. И, скорее всего, даже не задумываешься, почему они устроены именно так. Они просто есть. Как водопровод или электричество.

Но каждый архитектурный приём, который ты считаешь само собой разумеющимся — будь то MVC, REST, микросервисы или serverless — появился не потому, что «так правильно». Он появился как ответ на конкретную боль. Кому-то было реально больно, и он придумал решение. Потом кому-то стало больно от этого решения — и появилось следующее.

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

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

Потому что в архитектуре, как и в жизни, все решения — это трейд-оффы. Всегда.


#1. CGI, PHP, LAMP: хаос, который работал

#Как всё начиналось

1993 год. Тим Бернерс-Ли уже изобрёл Всемирную паутину, но веб — это пока только статические страницы. Ты открываешь браузер, вводишь URL, сервер читает HTML-файл с диска и отдаёт его. Всё. Хочешь что-то динамическое — пиши программу, которая генерирует HTML на лету.

Здесь и появляется CGI (Common Gateway Interface) — первый стандарт для динамических веб-страниц. Идея простая: веб-сервер (тогда это был в основном Apache) получает запрос, запускает отдельный процесс для его обработки, передаёт данные через переменные окружения, ждёт, пока процесс завершится, и отдаёт результат клиенту.

Звучит дико? По сегодняшним меркам — да. Но это был гигантский шаг вперёд. Внезапно ты мог писать веб-приложения на чём угодно: Perl, C, Python, Shell — неважно. Серверу всё равно, лишь бы программа следовала протоколу.

Проблема? На каждый запрос — новый процесс. Если на сайт заходит 10 человек, сервер запускает 10 процессов. Тысяча человек — тысяча процессов. Память улетала в трубу, процессор плавился, а латентность росла как на дрожжах.

И тут на сцене появляется PHP.

#PHP/FI: случайный гений

В 1994 году Расмус Лердорф (Rasmus Lerdorf) написал на Perl небольшой набор скриптов для своей домашней страницы — отслеживать, кто и что читает. Потом переписал на C. Потом добавил возможность встраивать код прямо в HTML. Так родился PHP/FI (Personal Home Page / Forms Interpreter).

Ключевое нововведение: ты больше не запускаешь внешнюю программу. PHP работает как модуль внутри Apache. Он встроен прямо в веб-сервер. Никаких отдельных процессов на каждый запрос. Скорость взлетает, потребление памяти падает.

Но настоящий гений PHP был в другом: он убрал порог входа.

Чтобы сделать динамическую страницу на CGI с Perl, тебе нужно было:

  1. Написать Perl-скрипт
  2. Настроить Apache, чтобы он знал, какие скрипты выполнять
  3. Позаботиться о безопасности (экранирование, инъекции)
  4. Помнить про заголовки HTTP (Content-Type как минимум)

С PHP ты просто брал HTML-файл, переименовывал его в .php, вставлял <?php echo "Привет, мир!"; ?> — и готово. Сервер сам понимал, где код, а где разметка.

Это было не просто удобно. Это демократизировало веб.

#LAMP: первый архитектурный стэк

К середине 2000-х сложился канонический стэк для веба: LAMP (Linux, Apache, MySQL, PHP/Python/Perl). Он не проектировался Архитектором с большой буквы — он просто работал. И работал настолько хорошо, что на нём были построены гиганты: Facebook (PHP), Wikipedia (PHP), Yahoo! (PHP).

Как выглядела архитектура?

Браузер → Apache + mod_php → MySQL

Всё на одной машине. Если сайт не помещался — брали машину побольше (vertical scaling). Если не хватало одной базы — ставили репликацию master-slave. Если Apache не справлялся — ставили nginx в качестве reverse proxy перед Apache.

Никаких микросервисов. Никаких очередей. Никакого Kubernetes. И знаешь что? Для 90% проектов этого было достаточно.

Урок: LAMP победил не потому, что был красивым. Он победил, потому что был простым. Любой разработчик мог сесть, поднять сервер за вечер и запустить приложение. Никакой DevOps. Никакой контейнеризации. Просто apt-get install apache2 php5 mysql-server.

#Боль, которая привела к следующей эпохе

Но у LAMP были проблемы:

  1. Смешивание кода и представления. PHP-файлы превращались в кашу из SQL-запросов, HTML, бизнес-логики и JavaScript. Поддерживать это было адски трудно.
  2. Нет разделения ответственности. Один разработчик писал всё — от базы данных до CSS. Это работало для маленьких проектов, но когда команда росла, начинался хаос.
  3. Состояние на стороне сервера. Каждый запрос мог изменить глобальное состояние. Сессии хранились в файлах на сервере. Масштабирование? Ну, удачи.
  4. Нет тестирования. PHP-код 2000-х было практически невозможно тестировать. Код и HTML были переплетены, зависимости глобальны, всё держалось на вере в лучшее («ну, мы же проверили в браузере»).

К середине 2000-х эти боли стали невыносимыми. Проекты росли, команды росли, а инструменты оставались на уровне «просто вставь код в HTML».

Рынок созрел для перемен.


#2. Эпоха MVC: рождение паттернов

#Rails и «Convention over Configuration»

2004 год. Дэвид Хайнемайер Ханссон (David Heinemeier Hansson, он же DHH) выпускает Ruby on Rails. И это не просто ещё один фреймворк — это тектонический сдвиг.

Rails принёс три идеи, которые перевернули веб-разработку:

Первое — Convention over Configuration (CoC). В LAMP-мире ты каждый раз настраивал всё с нуля: как подключаться к базе, где лежат шаблоны, как обрабатывать формы. Rails говорил: «Мы знаем, как обычно делают. Делай так по умолчанию, а если надо иначе — переопредели». Это сокращало количество кода в разы.

Второе — Don't Repeat Yourself (DRY). Не пиши одно и то же в трёх местах. Если у тебя есть модель User, не надо отдельно описывать таблицу, отдельно — валидации, отдельно — HTML-форму. Один источник истины.

Третье — Model-View-Controller (MVC). Чёткое разделение на три слоя:

  • Model — данные и бизнес-логика. Всё, что связано с предметной областью.
  • View — представление. HTML, который видит пользователь.
  • Controller — обработка запросов. Что делать, когда пользователь что-то запросил.

Звучит очевидно? Сейчас — да. Но в 2004 году это было откровение. Большинство PHP-проектов выглядели как спагетти из SQL и HTML, пересыпанное логинами и проверками.

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

#Почему MVC стал доминантой

MVC не изобрели в Rails — этот паттерн пришёл из Smalltalk (1970-е). Но Rails сделал его массовым. Почему?

  1. Порог входа. Rails был невероятно прост для старта. rails new blog, и у тебя уже рабочее приложение с базой, тестами и роутингом. Это был тот же «эффект PHP», но на новом уровне.
  2. Тестируемость. Модели можно тестировать без браузера, контроллеры — с моками запросов. Это было немыслимо для PHP тех лет.
  3. Активное сообщество. Rails породил культуру sharing — гемы (библиотеки), блоги, подкасты, конференции. У PHP такой культуры не было (она появилась позже, с Composer и Laravel).

Rails породил волну подражаний. Django (Python, 2005), CakePHP (PHP, 2005), Symfony (PHP, 2005), ASP.NET MVC (2009) — все они взяли MVC и адаптировали под свои языки. Даже современные React/Vue/Angular — это по сути MVC на клиенте (с поправкой на компонентный подход).

#А что насчёт Java?

Пока Rails покорял сердца стартапов, Java-мир жил своей жизнью. Там правила задавали Enterprise JavaBeans (EJB), тяжеловесные серверы приложений (WebLogic, JBoss), и архитектура по принципу «напиши 10 файлов конфигурации в XML, чтобы вывести "Hello, World"».

Контраст был разительным. Rails-разработчик писал 5 строк кода и получал рабочий CRUD. Java-разработчик писал 50 строк XML, чтобы настроить connection pool.

Этот контраст привёл к появлению «лёгких» Java-фреймворков — Spring, Hibernate, Guice. Они переняли идеи Rails (DI, ORM, convention over configuration), но в Java-стиле: более строго, более формально, но всё ещё слишком много конфигурации.

Кстати, эта история повторяется и сейчас: когда Rust или Go-разработчики смотрят на Python/Django или Node.js/Express, они видят тот же контраст. Разные языки — разные компромиссы между скоростью разработки и контролем.

#Фундаментальная проблема MVC

MVC прекрасен для простых CRUD-приложений. Но как только бизнес-логика усложняется, начинаются танцы с бубном.

В классическом Rails-приложении модель становится «толстой» (fat model). В неё складывают всё: валидации, вызовы внешних API, отправку email, генерацию отчётов, асинхронные задачи. Контроллеры остаются тонкими, но модели превращаются в монстров на тысячи строк.

Сообщество пыталось это лечить:

  • Service Objects — вынести бизнес-логику из модели в отдельные классы
  • Decorators / Presenters — вынести логику представления
  • Form Objects — вынести обработку форм
  • Query Objects — вынести запросы к базе

Внимание, вопрос: если мы выносим из модели всё, что можно вынести — что остаётся в модели? Только валидации и ассоциации? Тогда зачем она вообще?

Ответ: MVC — это отличная отправная точка, но не конечная архитектура. Он дает тебе начальную структуру, а дальше ты должен думать сам. И это нормально — ни одна архитектура не высечена в камне.

#Боль, которая привела к REST / SOAP разлому

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

В мире LAMP и раннего Rails приложение было монолитным: один сервер, один код, одна база. Но к концу 2000-х стало ясно, что это не работает для больших систем.

  • У тебя есть сайт, мобильное приложение и партнёрский API — каждый хочет одни и те же данные, но в разных форматах
  • У тебя есть несколько команд, которые хотят разрабатывать свои модули независимо
  • Тебе нужно интегрироваться с внешними сервисами (платёжный шлюз, CRM, почтовая служба)

Нужен был стандартный способ общения между системами. И тут началась война.


#3. REST vs SOAP: победа простоты

#SOAP: корпоративный броненосец

В конце 1990-х Microsoft, IBM и другие корпорации решили, что вебу нужен «правильный» протокол для обмена данными. Так родился SOAP (Simple Object Access Protocol).

Название ироничное — он не был ни простым, ни протоколом доступа к объектам.

SOAP — это формат XML-сообщений с жёсткой структурой. Каждый запрос и ответ заворачивается в конверт (Envelope), содержит заголовок (Header) и тело (Body). Описание сервисов — в WSDL (Web Services Description Language), ещё одном XML-документе, который описывает, какие методы доступны, какие параметры принимают и что возвращают.

Как выглядел SOAP-запрос к простому сервису погоды:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken>
        <wsse:Username>alice</wsse:Username>
        <wsse:Password>secret</wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </soap:Header>
  <soap:Body>
    <GetWeather xmlns="http://example.com/weather">
      <City>Moscow</City>
    </GetWeather>
  </soap:Body>
</soap:Envelope>

Прелесть SOAP была в том, что он был стандартизирован до мельчайших деталей. WS-* — целое семейство стандартов: WS-Security, WS-Transaction, WS-ReliableMessaging, WS-Addressing. Если тебе нужно было enterprise-решение с гарантированной доставкой, транзакциями и безопасностью — SOAP давал это «из коробки».

Проблема? Он был чудовищно сложным.

  • Чтобы поднять SOAP-клиент, нужно было сгенерировать код из WSDL
  • WSDL был настолько многословным, что генерировал тысячи строк XML для простого сервиса
  • Отладка SOAP-запросов — отдельный круг ада: попробуй найти ошибку в 200 строках XML
  • Разные реализации SOAP были несовместимы: SOAP от Microsoft не всегда работал с SOAP от IBM

SOAP стал символом всего, что ненавидят разработчики в enterprise-архитектуре: бюрократия, избыточность, «стандарты ради стандартов».

#REST: бунт одиночки

2000 год. Рой Филдинг (Roy Fielding) — один из создателей протокола HTTP — пишет диссертацию, в которой описывает REST (Representational State Transfer).

Филдинг не пытался создать альтернативу SOAP. Он анализировал, почему веб работает — и формализовал принципы, которые сделали его успешным:

  1. Единый интерфейс (Uniform Interface) — все ресурсы работают одинаково. GET для чтения, POST для создания, PUT для обновления, DELETE для удаления. Не надо гадать: «этот метод вызывается через SOAP Action или через GET?»
  2. Отсутствие состояния (Stateless) — каждый запрос содержит всю информацию, необходимую для его обработки. Сервер не хранит состояние сессии.
  3. Кэшируемость (Cacheable) — ответы явно указывают, можно ли их кэшировать и как долго.
  4. Единообразие URL / Resource-oriented — каждый ресурс имеет уникальный URL.

Ирония в том, что REST не был «придуман» — он был описан. Оказалось, что веб с самого начала работал по REST-принципам. Филдинг просто объяснил, почему это хорошо.

Но массовое принятие REST произошло не в 2000 году, а позже — когда Rails и Django показали, как легко делать RESTful API на практике. Плюс JSON оказался намного легче XML.

JSON vs XML — это отдельная история победы простоты. Сравни:

SOAP (XML): 200 строк, парсинг через DOM, пространства имён, схемы валидации.

REST (JSON):

GET /weather/Moscow
Response: {"city": "Moscow", "temperature": 22, "unit": "C"}

Какая запись понятнее? Вот именно.

К 2010 году REST победил. SOAP остался в нише enterprise-интеграций, где транзакции и formal contracts действительно нужны (банки, страхование, авиаперевозки). Всё остальное перешло на REST.

#Урок из этой войны

Победа REST над SOAP — это не победа «правильной» архитектуры над «неправильной». Это победа простоты над сложностью там, где сложность не оправдана.

SOAP решал проблемы, которых у большинства разработчиков не было. Тебе не нужна WS-AtomicTransaction для блога. Тебе не нужен WS-ReliableMessaging для списка задач. Но SOAP навязывал всю эту сложность априори — до того, как ты понял, нужна она тебе или нет.

REST говорил: «Начни с простого. Усложняй, когда понадобится». Это фундаментально другой подход.

Сейчас мы наблюдаем похожую динамику с gRPC и GraphQL. gRPC — это SOAP 2.0 (Protocol Buffers вместо XML, HTTP/2 вместо HTTP/1.1, строгие контракты, генерация кода). GraphQL — это REST с хирургической точностью запросов. Каждый выбирает под свою боль.

Запомни: сложность должна быть пропорциональна проблеме, которую ты решаешь. Не больше.


#4. jQuery → SPA → SSR: маятник качается

#Эпоха jQuery: анимация методом copy-paste

2006 год. Джон Резиг (John Resig) выпускает jQuery. Веб-фронтенд тех лет — это тёмные времена. Браузеры (IE6, Firefox 2, Safari 2) реализуют JavaScript по-разному. Чтобы сделать простой AJAX-запрос, нужно писать:

var xhr;
if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
} else {
    xhr = new ActiveXObject("Microsoft.XMLHTTP"); // IE6
}
xhr.open("GET", "/data", true);
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        document.getElementById("result").innerHTML = xhr.responseText;
    }
};
xhr.send();

И это только запрос. А если нужно анимировать элемент, обработать клик, сделать слайдер? Каждый раз — велосипед с учётом браузерных костылей.

jQuery решил эту проблему одной фразой: «Write less, do more».

$("#result").load("/data");
$(".menu").fadeIn();
$("button").click(function() { alert("Clicked!"); });

jQuery абстрагировал браузерные различия. Код работал везде — IE6, Firefox, Safari, Opera. Это была killer feature.

В те годы веб был «серверным»: HTML генерировался на сервере (Rails, PHP, Django), а jQuery добавлял интерактивности. Пользователь нажимает кнопку — jQuery делает AJAX-запрос — сервер возвращает HTML — jQuery вставляет его в DOM.

Архитектурно это был серверный рендеринг с JavaScript-глазурью.

#Single Page Applications: революция Google

2010 год. Google выпускает AngularJS. Почти одновременно появляются Backbone.js, Ember.js, Knockout. Концепция Single Page Application (SPA) врывается в мейнстрим.

Идея радикальная: рендеринг полностью переезжает на клиент. Сервер отдаёт только данные (обычно JSON), а браузер сам строит HTML. Навигация между страницами — без перезагрузки. Всё, что нужно, загружается один раз.

Почему это стало возможно?

  1. Браузеры поумнели. JavaScript-движки (V8 в Chrome, SpiderMonkey в Firefox) стали быстрее на порядок. IE начал умирать. Появились стандарты ES5/ES6.
  2. AJAX стал нормой. Разработчики перестали бояться асинхронных запросов.
  3. Пользователи привыкли к «гладким» интерфейсам. Gmail, Google Maps, Twitter — все перешли на SPA-подход.

SPA изменил архитектуру. Теперь у тебя было два приложения: клиентское (Angular/Backbone) и серверное (REST API). Команда фронтенда и команда бекенда могли работать независимо — лишь бы API был согласован.

Но появились новые боли:

  • SEO. Поисковые боты не выполняют JavaScript. Твой SPA — пустая страница для Google. Пришлось изобретать prerender-сервисы и server-side rendering для SPA.
  • Первая загрузка. Клиент скачивает мегабайты JavaScript, прежде чем увидеть что-то полезное.
  • Состояние на клиенте. Теперь менеджмент состояния (Redux, MobX, Vuex) — отдельная инженерная дисциплина.
  • Сложность. SPA — это операционная система в браузере. Routing, state management, кэширование, оптимизация рендеринга.

#Маятник качается обратно: SSR, Next.js, Astro

К 2020 году стало ясно: SPA переусложнён для 80% задач. Для блога, новостного сайта, интернет-магазина не нужен клиентский роутинг и Redux-saga. Нужно быстро отдать HTML и добавить чуть-чуть интерактива.

Появились Next.js (React), Nuxt.js (Vue), SvelteKit — фреймворки, которые дают серверный рендеринг «из коробки», а SPA-фичи — опционально.

Следом — Astro, который вообще по умолчанию не отправляет JavaScript. Ноль JS, если не нужен. Никогда не загружать то, что не используется — radical idea, right?

И Qwik, который主張 (заявляет) идею resumability: приложение может быть «заморожено» на сервере и продолжено на клиенте без выполнения всего JavaScript.

Маятник качается.

Серверный рендеринг (LAMP, Rails)
    ↓
SPA (Angular, React, Vue) — весь рендеринг на клиенте
    ↓
SSR + Hydration (Next.js, Nuxt) — рендеринг на сервере, интерактив на клиенте
    ↓
Islands Architecture (Astro, Qwik) — минимум JS, только интерактивные «островки»

Куда качнётся дальше? Возможно, к чему-то вроде «серверные компоненты, которые никогда не покидают сервер» — и React уже экспериментирует с этим (React Server Components).

#Главный урок

Маятник качается между двумя полюсами: контроль и простота.

  • Серверный рендеринг даёт контроль над HTML, прост для понимания, но плох для интерактива.
  • SPA даёт богатый UX, но требует сложной архитектуры и долгой загрузки.
  • SSR с гидратацией пытается взять лучшее от обоих, но платит за это дублированием (код выполняется и на сервере, и на клиенте).
  • Islands архитектура говорит: «Не дублируй то, что не нужно» — но требует дисциплины.

Что остаётся неизменным? Архитектурное мышление. Неважно, что модно сегодня — понимание trade-off'ов между серверным и клиентским рендерингом останется с тобой навсегда.


#5. Микросервисы: золотая лихорадка и похмелье

#Amazon в 2002: рождение из боли

2002 год. Amazon — интернет-магазин с монолитной архитектурой. Бенджамин Блэк (Benjamin Black) пишет внутренний документ, в котором предлагает перейти на сервис-ориентированную архитектуру (SOA — не путать с SOAP). Идея: каждый компонент системы — отдельный сервис со своим API.

В 2002 это звучало безумно. Но Джефф Безос (Jeff Bezos) издал знаменитый мандат:

  1. Все команды отныне общаются через API. Никакого прямого доступа к базам других команд.
  2. API должен быть спроектирован так, чтобы его можно было опубликовать внешним разработчикам. Нет исключений.
  3. Любой, кто нарушает это правило, уволен. Без шуток.

Звучит жёстко? Так и было. Безос понимал: культура «зайди в базу команды Pay и сделай SELECT» не масштабируется. Когда у тебя тысяча разработчиков и миллионы строк кода, монолит становится тормозом.

Переход Amazon на микросервисы (тогда их называли SOA) занял годы. Но к середине 2000-х Amazon мог деплоить каждую команду независимо, каждая команда выбирала свой стек, и масштабировать можно было отдельные компоненты, а не всё приложение целиком.

#Netflix: микросервисы для выживания

2008 год. Netflix переживает серьёзный сбой в дата-центре — база данных падает, и три дня Netflix не работает. Компания решает: хватит. Надо переходить в облако (AWS) и перестраивать архитектуру.

К 2011 году Netflix уже полностью работает в AWS и постепенно дробит монолит на микросервисы. Каждый сервис — отдельное развертывание, отдельный стек, отдельная база.

Но Netflix столкнулся с проблемами, которые стали классическими:

  1. Как узнать, что сервис жив? — появился Hystrix (Circuit Breaker).
  2. Как маршрутизировать трафик? — Zuul (API Gateway).
  3. Как регистрировать сервисы и находить их? — Eureka (Service Discovery).
  4. Как обеспечить отказоустойчивость? — Bulkhead, Timeout, Retry паттерны.
  5. Как собирать логи со 100+ сервисов? — ELK стэк, Kafka.

Netflix открыл код этих инструментов. Так появился Netflix OSS — набор библиотек, который стал стандартом для микросервисной архитектуры на JVM. Позже Spring Cloud их завернул в «магию конфигурации», и микросервисы стали доступны массам.

#Docker и Kubernetes: инфраструктурная революция

Микросервисная идея крутилась вокруг, но был один тормоз: деплой.

У тебя 20 сервисов. Каждый написан на своём языке (Java, Go, Python, Node). У каждого свои зависимости. Как гарантировать, что окружение везде одинаковое? Как откатить один сервис, не затронув другие?

Docker (2013) решил проблему консистентности окружения. «Работает на моей машине» больше не было оправданием — контейнер включал в себя всё, включая ОС-уровень.

Kubernetes (2014, релиз 1.0 в 2015) решил проблему оркестрации. Автоматическое размещение контейнеров, масштабирование, балансировка, самовосстановление.

Docker + Kubernetes стали для микросервисов тем же, чем Apache + mod_php были для LAMP — инфраструктурой, которая делает архитектуру возможной.

И тут началась золотая лихорадка.

#Микросервисное похмелье

К 2018-2019 году микросервисы стали мейнстримом. Каждая конференция говорила о микросервисах. Каждый стартап хотел «микросервисы как в Netflix».

Началось то, что Мартин Фаулер (Martin Fowler) назвал «microservices prejudice» — люди делят монолит на микросервисы не потому, что это нужно, а потому, что «так модно».

Реальность оказалась суровее:

  • Сетевая сложность. Там, где раньше был вызов функции, теперь — HTTP-запрос с задержкой в миллисекунды. А если сервис упал? А если сеть потеряла пакет? А если таймаут?
  • Распределённые транзакции. Saga-паттерн — это красиво в теории. На практике — адская боль с компенсациями.
  • Observability. Без централизованного логирования, трейсинга и метрик ты слеп. Поднять ELK/Prometheus/Grafana/Jaeger — отдельный проект.
  • Тестирование. Интеграционные тесты между сервисами требуют целой инфраструктуры. Contract testing (Pact) помогает, но не решает всех проблем.
  • DevOps-экспертиза. Микросервисы требуют команды, которая понимает Kubernetes, CI/CD, мониторинг, управление секретами. Это не про «просто запили API».

Саймон Браун (Simon Brown) хорошо сформулировал: «Для микросервисов нужна зрелость организации, а не технологии».

#Реальность 2024: модульный монолит

К середине 2020-х наступило похмелье, но и переоценка. Микросервисы не «отменили» — они нашли своё место: это решение для зрелых продуктов с большими командами, не для стартапов.

Альтернатива, которая заняла нишу «золотой середины» — модульный монолит (modular monolith). Это монолит, но с чёткими модульными границами (похожими на bounded contexts из DDD). Каждый модуль имеет свой API, свою БД (на уровне схемы), свои тесты. Но деплоится как единое целое.

Почему это работает:

  • Простота деплоя. Один процесс, одна команда deploy.
  • Нет сетевых задержек. Вызовы между модулями — это вызовы функций, не HTTP.
  • Можно перейти к микросервисам позже. Когда и если понадобится — модуль можно вынести в отдельный сервис.

Martin Fowler: «Сделайте сначала модульный монолит. Переходите на микросервисы, когда поймёте, что монолит вам реально мешает».


#6. Serverless, Edge, JAMStack: очередной виток?

#Serverless: код без сервера

2014 год. AWS запускает Lambda. Идея: ты не думаешь о серверах. Ты пишешь функцию, загружаешь её в облако, и она выполняется в ответ на событие. Всё остальное — масштабирование, отказоустойчивость, патчи ОС — делает провайдер.

Serverless (Function-as-a-Service, FaaS) — это радикальное упрощение девопс-нагрузки. Ты вообще не думаешь об инфраструктуре. Твой фокус — только код.

Проблемы, которые решает Serverless:

  • Платишь только за использование. Нет idle-серверов. Если функцию вызвали 100 раз — заплатил за 100 вызовов.
  • Автоматическое масштабирование. Lambda может обработать 1 запрос в минуту или 100 000 в секунду — облако масштабирует само.
  • Нет операционных расходов. Не надо ставить патчи, перезагружать серверы, настраивать мониторинг.

Но появились новые боли:

  • Cold starts. Если функцию долго не вызывали, следующему запросу нужно ждать, пока загрузится runtime. Для Node.js это 100-300ms, для Java — 2-10 секунд.
  • Ограниченное время выполнения. Lambda-функции живут максимум 15 минут. Для долгих процессов не подходит.
  • Stateful логика. Serverless — stateless по определению. Где хранить состояние? Внешние сервисы (Redis, S3, DynamoDB).
  • Привязка к провайдеру. AWS Lambda несовместима с Google Cloud Functions несовместима с Azure Functions. Переносимость кода — ноль.
  • Отладка. Локально воспроизвести окружение Lambda можно, но это «close enough, not exact».

Serverless не заменил серверы. Он нашёл свою нишу: обработка событий, вебхуки, простые API, бэкенд для мобильных приложений. Для этого он идеален.

#Edge Computing: код на границе сети

Edge computing — логическое продолжение CDN. Если CDN кэширует статику на границе сети (ближе к пользователю), то edge computing выполняет код на границе сети.

Cloudflare Workers (2017), AWS Lambda@Edge (2018), Vercel Edge Functions, Deno Deploy — платформы, которые позволяют запускать код в десятках (а то и сотнях) дата-центров по всему миру.

Зачем?

  1. Латентность. Если твой сервер в Виргинии, а пользователь в Австралии — каждый запрос идёт через Тихий океан. 250ms туда, 250ms обратно. Edge сокращает это до 10-50ms.
  2. Гео-распределённые данные. Можно кэшировать данные на границе: корзина пользователя в EU, рекомендации в US, авторизация в Asia.
  3. Персонализация на границе. A/B тесты, гео-редиректы, локализация — без round-trip до origin-сервера.

Edge — это по сути возвращение к идее «сделай на сервере, отдай HTML», но сервер теперь не один — их сотни, и они рядом с каждым пользователем.

Маятник качнулся от SPA (всё на клиенте) к edge rendering (всё на сервере, но сервер рядом с пользователем). Ирония?

#JAMStack: JavaScript + API + Markup

2015 год. Мэт Билман (Matt Biilmann) и Крис Бах (Chris Bach) вводят термин JAMStack. Идея: статический сайт + динамические API.

Как это работает:

  1. Markup — HTML генерируется на этапе билда (статическая генерация, SSG). Не на сервере по запросу, а заранее.
  2. API — вся динамика вынесена в API-сервисы (аутентификация, комменты, поиск, платежи).
  3. JavaScript — склеивает markup и API на клиенте.

Звучит как шаг назад? Возвращение к статическим сайтам 1990-х? Не совсем.

JAMStack решает вполне современные проблемы:

  • Безопасность. Нет сервера, который можно взломать. Нет базы данных, в которую можно сделать SQL injection. Статические файлы на CDN — и всё.
  • Производительность. CDN отдаёт статику мгновенно. Нет серверного времени ответа, нет генерации страницы на лету.
  • Простота хостинга. Статику можно хостить на Netlify, Vercel, Cloudflare Pages, GitHub Pages — часто бесплатно.

Но JAMStack не универсален:

  • Большие сайты. Для сайта с миллионами страниц билд может длиться часами. Incremental Static Regeneration (Next.js ISR) решает эту проблему частично.
  • Часто меняющиеся данные. Если контент обновляется каждую секунду — статическая генерация не имеет смысла.
  • Динамические фичи. Реалтайм, вебсокеты, стриминг — JAMStack не про это.

JAMStack нашёл свою нишу: контентные сайты, документация, блоги, лендинги, e-commerce (с головой на API). Для всего остального есть сервер.


#Компромиссы и альтернативы: как выбирать?

Вся история веб-архитектуры — про то, что нет идеальных решений. Есть trade-off'ы.

Вот ключевые: какие выбирать — зависит от твоей ситуации.

Параметр Монолит Модульный монолит Микросервисы
Скорость старта Высокая Средняя Низкая
Простота деплоя Высокая Высокая Низкая
Масштаб команды До 5-10 чел До 20-50 чел 50+ чел
Сложность отладки Низкая Низкая Высокая
Изоляция сбоев Низкая Средняя Высокая
Performance Высокая Высокая Средняя (сеть)
DevOps нагрузка Низкая Низкая Высокая
Параметр SSR (Server) SPA (Client) SSG (Static) Edge
Первая загрузка Быстрая Медленная Мгновенная Быстрая
Интерактив Средний Высокий Низкий Средний
SEO Отличное Плохое Отличное Отличное
Сложность Низкая Высокая Низкая Средняя
Обновление контента В реальном времени Через API Требует билда Разное
Параметр REST GraphQL gRPC WebSocket
Простота Высокая Средняя Низкая Средняя
Типизация Опционально Строгая Строгая Нет
Кэширование HTTP-native Сложное Нет Нет
Streaming Нет Частично Да Да
Over-fetching Есть Нет Нет Н/Д
Инструменты отладки curl GraphiQL, Apollo grpcurl wscat

#Когда что применять: контекст решает

Вот как я вижу принятие решений в реальных проектах:

Ты в стартапе с командой из 3-5 человек. Бери монолит. Rails, Django, Laravel, Next.js — неважно. Быстро сделай MVP. Не думай о микросервисах. Когда придёт боль — тогда и будем решать.

У тебя команда из 20 разработчиков и продукт растёт. Модульный монолит — твой друг. Выдели bounded contexts, раздели на модули, каждый со своей БД. Не трогай деплой — пусть всё ещё один процесс.

У тебя 5+ команд по 5-8 человек, и каждая отвечает за свою функциональность. Вот тут микросервисы оправданы. Но только если у тебя есть SRE-команда или DevOps-культура. Иначе похмелье гарантировано.

Твоё приложение — контентный сайт / блог / документация. JAMStack. Next.js SSG, Astro, Hugo, 11ty. Статика на CDN. Если нужны динамические фичи — API-сервисы.

Твоё приложение — админка / панель управления. SPA. React + Vite + какой-нибудь UI kit. Просто, быстро, интерактивно. SEO не важен. Пользователи — внутренние.

Твоё приложение — e-commerce / SaaS с быстрым UI. SSR с гидратацией. Next.js, Nuxt, SvelteKit. Быстрая первая загрузка, хороший SEO, интерактив там, где нужен.

Тебе нужен API для мобильного приложения. REST для простых случаев. GraphQL — если клиентам нужна гибкость выборки данных. gRPC — если перформанс критичен.


#Практический кейс: эволюция архитектуры SoundCloud

SoundCloud — отличный пример того, как архитектура эволюционирует вместе с продуктом, проходя через все описанные этапы.

#2007: Ruby on Rails монолит

SoundCloud запускается как классический Rails-монолит. Одна кодовая база, одна база данных PostgreSQL, одна команда. Разработка идёт быстро, фичи выкатываются каждый день.

#2011: Выделение API

Появляется мобильное приложение. SoundCloud выделяет API из монолита — теперь внешние клиенты и внутренние сервисы общаются через REST. API становится продуктом сам по себе: SoundCloud предоставляет его сторонним разработчикам.

#2013: Дробление на микросервисы

Монолит становится слишком большим (миллионы строк кода). Одна команда не может его обслуживать. SoundCloud начинает дробить систему на микросервисы:

  • Users service — регистрация, профили, подписки
  • Tracks service — загрузка, хранение, transcoding треков
  • Playlists service — плейлисты, коллекции
  • Stream service — лента рекомендаций
  • Search service — поиск (Elasticsearch)
  • Social service — лайки, комментарии, репосты
  • Payments service — подписки, транзакции

Каждый сервис — отдельное Java-приложение, своя БД (PostgreSQL, Cassandra, Redis, Elasticsearch — что подходит задаче), свой CI/CD, свой owner в команде.

#2015-2017: Боли микросервисов

SoundCloud проходит через классические проблемы:

  • N+1 проблема в сервисах — запрос к одному сервису порождает цепочку запросов к другим
  • Data consistency — транзакции через несколько сервисов требуют Saga
  • API Gateway — как маршрутизировать запросы от клиента к нужному сервису?
  • Мониторинг — без хорошей observability команды слепы

SoundCloud инвестирует в инфраструктуру: Kafka для асинхронной коммуникации, Prometheus + Grafana для метрик, Jaeger для трассировки.

#2017: Backend Services Framework (BSF)

SoundCloud создаёт внутренний фреймворк (BSF), который стандартизирует, как пишутся микросервисы: общая конфигурация, логирование, метрики, health checks, circuit breakers. Это снижает когнитивную нагрузку на команды — они фокусируются на бизнес-логике, а не на инфраструктуре.

#2020+: Оптимизация

SoundCloud не отказывается от микросервисов, но пересматривает границы. Некоторые микросервисы объединяются обратно. Некоторые выделяются. Архитектура становится гибридной — модульные компоненты внутри крупных сервисов.

#Уроки из SoundCloud

  1. Архитектура — это не проект, а эволюция. SoundCloud не проектировал микросервисы заранее. Они выросли из боли.
  2. Каждый переход решал конкретную проблему. Монолит → API: нужна была мобилка и партнёры. API → микросервисы: монолит стал тормозом.
  3. Не все микросервисы — это микросервисы. Некоторые сервисы SoundCloud — это, по сути, монолиты, просто меньшего размера.
  4. Инфраструктура — это цена, которую ты платишь. Без хорошего мониторинга, CI/CD и культуры DevOps микросервисы убивают.

#Чек-лист для самопроверки

  • Я понимаю, какую боль решал каждый архитектурный этап (CGI → LAMP, LAMP → MVC, MVC → API, API → микросервисы)
  • Я знаю разницу между SOAP и REST и почему REST победил (простота vs enterprise-сложность)
  • Я осознаю цикличность трендов: серверный рендеринг → SPA → SSR → Edge
  • Я могу объяснить, почему микросервисы не подходят для стартапа или маленькой команды
  • Я понимаю концепцию «модульного монолита» и когда его применять
  • Я знаю Trade-off'ы между: Serverless vs Сервер, REST vs GraphQL vs gRPC, SPA vs SSR vs SSG vs Edge
  • Я могу определить, какая архитектура подходит для текущего контекста (стадия продукта, размер команды, требования)
  • Я понимаю, что каждая архитектура — это реакция на боль предыдущей
  • Я критически оцениваю новые технологии: за ними стоит чья-то нерешённая проблема. Совпадает ли она с моей?
  • Я не использую термин «серебряная пуля» — я знаю, что trade-off'ы есть всегда

#«Не делай так»: анти-паттерны

#1. «Напишем микросервисы для стартапа»

Самый дорогой анти-паттерн. Ты тратишь месяцы на инфраструктуру (Docker, Kubernetes, CI/CD, мониторинг, service mesh), а не на продукт. Твой MVP откладывается. Инвесторы нервничают. А пользователям всё равно, сколько у тебя сервисов — им нужна работающая фича.

Как надо: Монолит. Если очень хочется «на будущее» — заложи модульные границы в коде. Перейти от модульного монолита к микросервисам можно за недели. Обратно — за месяцы или никогда.

#2. «Serverless для всего»

Serverless прекрасен для определённых нагрузок. Но если у тебя высоконагруженный API с миллионами запросов — Lambda обойдётся дороже, чем выделенный сервер. Если у тебя долгие вычисления (генерация отчётов, обработка видео) — Lambda не подходит.

Как надо: Serverless — для своих задач. Event-driven, webhooks, простые API, batch processing с коротким временем жизни.

#3. «SPA для контентного сайта»

Ты делаешь блог на React с клиентским рендерингом. Результат: пустая страница для SEO, 2MB JavaScript для показа текста, плохой Core Web Vitals. Google тебя не индексирует. Пользователи уходят.

Как надо: Для контента — SSR или SSG. SPA — для приложений (админки, дашборды, инструменты).

#4. «GraphQL для всего»

GraphQL — мощный инструмент, но он добавляет сложность. У тебя появляется GraphQL-схема, резолверы, N+1 проблема (GraphQL его не решает — нужен DataLoader), кэширование (у REST оно есть на уровне HTTP, у GraphQL — нет).

Как надо: GraphQL — когда у тебя разнородные клиенты (веб, мобилка, desktop) с разными потребностями в данных. Для простого CRUD REST хватит.

#5. «Давайте перепишем всё с нуля»

Классика. Команде надоел старый проект. «Давайте возьмём новый фреймворк и перепишем». Через год: незаконченный рерайт, демотивированная команда, два параллельно поддерживаемых приложения.

Как надо: Strangler Fig паттерн — вырезай части старой системы постепенно, заменяя их новыми сервисами. Amazon, Netflix и SoundCloud именно так и делали.


#Ключевые выводы

  1. Каждый архитектурный шаг — реакция на боль предыдущего. CGI → LAMP → MVC → API → Микросервисы → Serverless — каждое решение решало конкретную проблему и создавало новую. Понимание этой динамики — твой компас.

  2. Простота побеждает сложность. REST победил SOAP, JSON победил XML, jQuery упростил JS, Rails упростил веб. Если твоя архитектура сложнее, чем проблема — ты перепроектируешь.

  3. Маятник качается, но не возвращается в ту же точку. Мы не вернулись к серверному рендерингу 2005 года — мы пришли к гибридам (SSR + hydration, islands architecture, edge rendering). История — это не круг, а спираль.

  4. Микросервисы — это organisational decision, а не технологический. Джефф Безос прав: распределённые команды = распределённый код. Но если у тебя одна команда — микросервисы создадут больше проблем, чем решат.

  5. Не существует «правильной» архитектуры — есть только подходящая для твоего контекста. Стартап с двумя разработчиками ≠ Amazon с 10 000 инженеров. Выбирай архитектуру, которая решает твою проблему, не проблему Netflix 2011 года.


Что дальше?

Что дальше? В Главе 3 мы разберём первые принципы архитектуры — то, что остаётся неизменным независимо от моды и технологий. Связанность (coupling), связность (cohesion), разделение ответственности (Separation of Concerns), и другие фундаментальные концепции, которые работают и для CGI-скрипта, и для serverless-функции.