Часть VI: Крупные проекты — архитектура на уровне гигантов
Глава 31

Эволюция архитектуры — истории рефакторинга

Реальные истории трансформации архитектуры. Без прикрас, без «и жили они долго и счастливо». Только то, что реально пошло не так, и то, что реально сработало.


#Введение: почему мы учимся только на своих ошибках

Есть старый анекдот про архитектора, который говорит джуниору: «Ты будешь делать те же ошибки, что и я. Просто будешь делать их на 50% быстрее, потому что я тебе их покажу».

С рефакторингом архитектуры — та же история. Каждое поколение разработчиков проходит через одни и те же грабли. Monolith → микросервисы → обратно в монолит. Rewrite → disaster → «надо было рефакторить». On-premise → cloud → «а почему счёт такой огромный?».

В этой главе — пять реальных историй. Имена изменены, но я ручаюсь, что каждая из них произошла в реальной компании (а часто — не в одной, а в десятках). Я сам участвовал в некоторых из них. Остальные собраны из десятков разговоров с инженерами на конференциях, в подкастах и в личной переписке.

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

Эта глава — про второе.


#История 1: Monolith → Microservices → Modular Monolith — что пошло не так и почему вернулись

#Компания N: продуктовый стартап, ставший "единорогом"

Давайте представим компанию (назовём её «N»). В 2015 году это был типичный стартап: команда из 15 разработчиков, монолит на Rails (или Django — неважно), одна база данных PostgreSQL, всё задеплоено на пару EC2-инстансов. Продукт растёт, пользователей становится всё больше, команда — тоже.

Где-то в 2017 году CTO читает Мартина Фаулера. Смотрит на Netflix. Читает Убера. И принимает решение: пора на микросервисы. Ведь «монолит не масштабируется», «мы не можем деплоить независимо», «команды слишком зависят друг от друга».

Знакомо? Поехали.

#Шаг 1: Эйфория (месяцы 1-6)

Выделяется команда «платформы». Они пилят новую архитектуру: Kubernetes, Docker, service mesh, CI/CD для каждого сервиса. Старый монолит решено «распилить» на 6-8 сервисов: Users, Orders, Payments, Notifications, Search, Analytics.

Первые сервисы выходят быстро. Users-сервис — отдельная база, отдельный деплой. Команда ликует. Графики на дашбордах красивые. На all-hands meeting CTO рассказывает, как они «перешли на современную архитектуру».

#Шаг 2: Боль (месяцы 7-18)

А потом начинается.

Проблема 1: Транзакции. Раньше заказ создавался в одной транзакции: списать товар со склада, создать заказ, списать деньги, отправить уведомление. В монолите это было BEGIN TRANSACTION ... COMMIT. Теперь это — вызов Users → Orders → Payments → Notifications через HTTP. Что если Payments упал, а Orders уже создан? Что если Notifications ответил timeout, а деньги уже списаны?

Появляется Saga. Сначала Choreography, потом Orchestration. Kafka. Dead letter queues. Retry logic with exponential backoff. За месяц команда платформы пишет больше кода для инфраструктуры, чем для бизнес-логики.

Проблема 2: Data consistency. Раньше JOIN между Users и Orders делался в один запрос. Теперь данные раскиданы по разным базам. Хочешь показать «заказы пользователя» — делай два запроса: сначала в Users (получить user_id), потом в Orders (получить заказы). А если хочешь «все заказы с именем пользователя» — это уже batch job.

Появляется CQRS. Eventual consistency. Все говорят умные слова, но пользователи жалуются, что их заказы иногда «исчезают» на пару секунд.

Проблема 3: Команды. Оказалось, что 15 разработчиков не могут поддерживать 8 микросервисов. Каждый сервис требует своего CI/CD, своего мониторинга, своего logging, своей документации. Онбординг нового разработчика вырос с 2 дней до 2 недель.

Проблема 4: Debugging. Раньше ошибка была в логах одного процесса. Теперь trace проходит через 5 сервисов, и каждый пишет в свой лог. Без distributed tracing (Jaeger/Zipkin) ты слеп. Но distributed tracing — это ещё один кусок инфраструктуры.

#Шаг 3: Прозрение (месяцы 19-24)

Через полтора года команда «N» смотрит на то, что они построили. У них не микросервисы. У них распределённый монолит (distributed monolith).

Симптомы классические:

  • Изменение в Orders требует деплоя Payments и Notifications
  • Shared database между сервисами (потому что «временное решение»)
  • Синхронные HTTP-цепочки: check-order → check-inventory → check-payment
  • Покрытие тестами упало, потому что интеграционные тесты между сервисами — это ад

Самое обидное: скорость разработки не выросла. Она упала. Фичи, которые в монолите делались за 2 дня, теперь делаются за неделю.

#Шаг 4: Возвращение (месяцы 25-36)

Решение, к которому приходит команда — модульный монолит (modular monolith). Они объединяют код обратно в один процесс, но сохраняют доменные границы (bounded contexts), которые проработали во время микросервисной эпопеи.

Как это выглядит:

  • Один процесс, один деплой
  • Чёткие модули (в Java — modules, в Node.js — packages с явными зависимостями)
  • Модули общаются через вызовы в памяти, не через HTTP
  • Чёткие контракты между модулями
  • Одна база данных, но модули не имеют прямого доступа к таблицам друг друга

Результат:

  • Скорость разработки вернулась на уровень «до микросервисов»
  • Онбординг — 3 дня вместо 2 недель
  • Тестирование снова стало простым
  • Инфраструктурная сложность упала на 70%

#Что на самом деле произошло?

Компания N не зря прошла этот путь. Микросервисы заставили их:

  1. Чётко определить границы доменов (bounded contexts)
  2. Научиться управлять контрактами между модулями
  3. Понять, что не всё нужно делать распределённо

Парадокс: они стали архитектурно грамотнее, хотя технологически вернулись туда, откуда начали.

Когда это нормально? Когда у вас одна команда (или несколько колокейтированных команд) и вы не имеете проблем с масштабированием на уровне кода. Модульный монолит — отличное решение для 90% случаев.

Когда микросервисы всё-таки нужны? Когда у вас есть реальная проблема, которую решает микросервисная архитектура:

  • Разные команды должны деплоить независимо (по-настоящему независимо)
  • Разные части системы имеют радикально разные требования к масштабированию
  • Вы используете разные технологические стэки для разных задач (и это оправдано)

Но не делайте ошибку «N»: микросервисы — это решение про команды и организацию, а не про масштабирование.


#История 2: Startup → IPO → Enterprise — как архитектура растёт с компанией

#Компания S: от гаража до публичной компании

Компания «S» запускается в 2012 году. Два основателя, один контрактник, PHP + MySQL на shared hosting. Код — спагетти, но он работает. Продукт набирает пользователей.

2014 год: раунд A, 15 человек. Код переезжает на выделенные серверы. Появляется первый DevOps. База шардируется. Добавляется Redis для кэша.

2016 год: раунд C, 80 человек. Начинаются проблемы.

#Проблемы роста (3 стадии)

Стадия 1: «А король-то голый» (15-50 человек)

На этой стадии компания осознаёт, что их архитектура не выдерживает критики.

Кодовая база — Big Ball of Mud. Одна модель User содержит 80 полей, потому что все фичи добавляли поля в одну таблицу. Один контроллер обрабатывает и аутентификацию, и платёжные уведомления, и админку.

Типичный день разработчика: «Я добавил поле в модель User, и упал тест в модуле Payments. Почему? Потому что Payments использовал User.email из той же модели, а я поменял формат email на username. Как они связаны? Не знаю. Теперь сижу и разбираюсь.»

Что обычно делают: начинают наводить порядок. Выделяют отдельные модули. Вводят code review. Перестают «коммитить в master сразу». Появляется первый архитектор (обычно это самый опытный разработчик, которого повысили «за заслуги»).

Стадия 2: «Мы выросли из штанишек» (50-200 человек)

Продукт стабильно растёт. Команда разделяется на несколько: Platform, Payments, Mobile, Web. Каждая команда хочет двигаться с разной скоростью.

Проблема в том, что код всё ещё общий. Web-команда не может релизить, потому что Platform-команда сломала API. Platform-команда не может рефакторить, потому что все зависят от их кода.

Что делает компания S: в 2017 году они принимают болезненное решение — выделять сервисы. Не потому что «микросервисы модно», а потому что организационно не могут иначе. Закон Конвея бьёт в полную силу.

Выделение идёт постепенно:

  1. Сначала отделяют Payments (регуляторные требования — отдельная база)
  2. Потом Auth (все сервисы нуждаются в авторизации)
  3. Потом Search (отдельный Elasticsearch кластер)
  4. Потом Analytics (отдельный data pipeline)

Ключевое отличие от Истории 1: они не делают «big bang» миграцию. Они выделяют сервисы по одному, когда это реально необходимо.

Стадия 3: «Мы больше не стартап» (200-1000+ человек)

2020 год. Компания S готовится к IPO. Теперь они — enterprise.

Новые проблемы:

  • Compliance. SOX, SOC2, GDPR. Каждое архитектурное решение теперь проверяется аудиторами.
  • Multi-team dependencies. 15 команд, каждая со своим роадмапом. API-контракты согласовываются неделями.
  • Legacy. Код 2012 года всё ещё работает. Никто не хочет его трогать, но он тянет всю архитектуру назад.

#Что реально сработало

1. API Platform как внутренний продукт

Компания S создала внутреннюю платформу API Gateway, которая стала единой точкой входа для всех сервисов. Это не было технически гениальным — это было организационно необходимо. Когда 15 команд общаются через HTTP, нужен единый стандарт: аутентификация, rate limiting, версионирование, документация.

2. Architecture Decision Records (ADR)

Каждое архитектурное решение — от «почему мы взяли PostgreSQL, а не MySQL для нового сервиса» до «почему мы не используем Kafka для этого сценария» — документируется в ADR. Не ради процесса, а ради ответа на вопрос «почему мы сделали это так» через полгода.

3. Feature flags как способ раскатки

Компания S вложилась в feature flag систему (сначала самописную, потом на LaunchDarkly). Это позволило раскатывать фичи без блокировок: включил флаг → тестируем → выключил → фиксим → включил снова. Без этого релизы превратились бы в ад из-за cross-team dependencies.

4. Принцип «you build it, you run it»

Каждая команда владеет своими сервисами энд-ту-энд. Если Payments упал в 3 часа ночи — Payment team чинит. Это болезненно, но это единственный способ не дать командам генерировать «брошенный код».

#Что не сработало

Микроменеджмент архитектуры. На волне enterprise-роста компания наняла «архитектурный комитет», который утверждал каждое технологическое решение. Бюрократия убила скорость. Через год комитет распустили, оставив только рекомендательные архитектурные гайдлайны.

#Урок для тех, кто проходит этот путь

Архитектура стартапа, которая привела к IPO, — это не «правильная» архитектура. Это архитектура, которая достаточно хороша на каждом этапе.

Если бы компания S в 2012 году начала проектировать enterprise-архитектуру для 1000 человек, они бы никогда не запустились. Если бы они в 2020 году продолжали писать спагетти-код, как в 2012, — они бы утонули.

Секрет: рефакторить архитектуру нужно постоянно, но только под текущие боли, не под будущие страхи.


#История 3: The Rewrite — уроки Joel Spolsky и что реально работает

#Теорема Джоэла (весна 2000)

В апреле 2000 года Джоэл Спольски (Joel Spolsky) написал эссе «Things You Should Never Do, Part I» [Anecdotal]. Основная мысль:

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

Спольски был прав в главном: rewrite — это почти всегда катастрофа. Но давайте разберёмся, почему, и — что важнее — когда rewrite всё-таки работает.

#История 3a: Rewrite Netscape (1998) — подтверждение теоремы

Netscape решил переписать браузер с нуля. Netscape Communicator 4.0 был «не very good», как выразился Спольски. Баги, медленный, глючный. Команда сказала: «Давайте напишем заново!».

Результат: пять лет разработки, пропущенный рынок, увольнение 70% инженеров, продажа компании AOL, и в итоге — Mozilla, которая появилась из руин, но когда Интернет Эксплорер уже занял 90% рынка.

Урок: rewrite убивает компании. Не потому что rewrite технически невозможен. А потому что время, которое вы тратите на rewrite, ваши конкуренты тратят на фичи.

#История 3b: Rewrite FreshBooks (2017) — когда теорема работает в обратную сторону

А теперь — история, где rewrite сработал. FreshBooks, канадская платформа для бухгалтерии, в 2017 году объявила, что переписывает свой продукт с нуля. И это было правильное решение.

Почему их случай отличался от Netscape?

  1. Бизнес не стоял на месте. Старый продукт продолжал продаваться и приносить деньги. Rewrite не остановил разработку фич — параллельно велись оба трека.
  2. Не все в одном релизе. FreshBooks не делал «big bang» — они мигрировали пользователей постепенно, фича за фичей.
  3. У них был чёткий критерий готовности. Не «когда всё будет идеально», а «когда новая платформа покроет 80% сценариев старых пользователей».

Результат: прощай старый код, здравствуй новая архитектура. Да, было больно. Да, заняло три года. Но бизнес выжил, продукт стал лучше.

#Когда rewrite реален (честный чек-лист)

Rewrite срабатывает, когда выполнены все эти условия:

  • Старый код невозможно поддерживать. Не «неудобно» или «хочется красивее», а реально невозможно. Пример: код на Cobol, на котором некому писать. Или код, который использует библиотеку с CVE, не имеющую апдейта.
  • У вас есть ресурс. Выделенная команда на 1-3 года, которая может работать параллельно с продуктовой разработкой. Если ваш rewrite — это «та же команда делает то же самое, только с нуля» — вы в зоне риска.
  • Вы можете мигрировать инкрементально. Никакого big bang. Каждая фича мигрируется и тестируется отдельно. Пользователь не должен заметить переключения.
  • Бизнес готов ждать. Rewrite не принесёт новых фич в ближайшие 6-12 месяцев. Если конкуренты дышат в спину — вам не до rewrite.
  • Вы понимаете, что новый код будет не идеален. Вы не пишете идеальную систему — вы пишете систему, которая будет легче поддерживать следующие 5 лет.

#Что реально работает вместо rewrite

В 90% случаев rewrite не нужен. Нужен стратегический рефакторинг. Вот проверенный подход:

#The Strangler Fig Pattern

Мартин Фаулер популяризовал паттерн «Strangler Fig» (удушающий фикус) — постепенно замещать куски старой системы новой реализацией, пока от старой не останется ничего.

Как это выглядит:

  1. Пишете API Gateway (или reverse proxy), который направляет запросы
  2. Каждую новую фичу реализуете на новой архитектуре
  3. Каждый старый модуль, который требует изменений, переписываете и переключаете трафик
  4. Через 2-3 года старая архитектура умирает естественной смертью

Это не быстро. Но это надёжно. И бизнес продолжает работать, пока вы рефакторите.

#Анти-паттерн: Golden Hammer

Не делайте ошибку — не используйте rewrite как решение для проблем, которые решаются проще. Если у вас медленная база — не переписывайте приложение, настройте индексы и кэши.

#Принцип Спольски на практике

Главный урок rewritten — не «никогда не делайте rewrite» (хотя в большинстве случаев это правда). Главный урок: осознавайте цену перезапуска.

Прежде чем сказать «давайте перепишем с нуля», ответьте на вопросы:

  • Сколько накопленных баг-фиксов мы теряем?
  • Сколько лет кода, в который вложены человеко-годы бизнес-логики?
  • Сколько пользовательских сценариев (включая те, о которых мы забыли) работает в старом коде?
  • Какой процент старого кода — реально важная логика, а какой — boilerplate, который легко воспроизвести?

И только если цена перезапуска меньше цены поддержки старого кода — rewrite оправдан.


#История 4: Database migration — MySQL → PostgreSQL, 150TB, 0 downtime, 6 месяцев

#Контекст

Представьте: у вас 150 терабайт данных в MySQL. Это не просто много данных — это core business. Платёжные транзакции, история заказов за 8 лет, аналитические данные. Более 5000 приложений и сервисов читают и пишут в эту базу.

И вам нужно переехать на PostgreSQL.

Зачем? Причины могут быть разными:

  • MySQL перестал справляться с определённым типом запросов (например, оконные функции или CTE)
  • Проблемы с репликацией в конкретной версии
  • Стратегическое решение: PostgreSQL open source, а не Oracle под капотом MySQL
  • Нужен более зрелый экстеншен для геоданных (PostGIS) или full-text search

Причина вторична. Важно то, что переезд базы данных — одна из самых рискованных операций в разработке. И это история о том, как это сделать правильно.

#Стратегия: Double Write + Incremental Migration

Команда (назовём её «М» из крупной европейской e-commerce компании) разработала стратегию, которая состоит из трёх фаз.

#Фаза 1: Подготовка (2 месяца)

Шаг 1: Аудит схемы. MySQL и PostgreSQL несовместимы на уровне типов данных, синтаксиса, и поведения.

  • TINYINT(1) → BOOLEAN или SMALLINT
  • VARCHAR — в целом совместимо, но кодировка может отличаться
  • AUTO_INCREMENT → SERIAL или IDENTITY
  • ENGINE=InnoDB → просто таблица
  • MySQL GROUP BY менее строгий, чем PostgreSQL — всё, что не агрегировано, должно быть в GROUP BY. Миллион запросов сломается (TODO: это реальная проблема).

Шаг 2: Конвертация типов. Каждый тип данных MySQL нужно сопоставить с PostgreSQL. Команда «М» написала автоматический анализатор, который просканировал все схемы (150TB!) и выдал отчёт о несовместимостях.

Шаг 3: Schema dump без данных. Запустили пустую PostgreSQL базу со сконвертированной схемой. Проверили, что приложение может с ней соединиться.

Шаг 4: Инструмент репликации. Сделали кастомный CDC (Change Data Capture) между MySQL и PostgreSQL. Это критический компонент: пока вы мигрируете данные, старые записи меняются. Нужно ловить эти изменения и применять их к новой базе.

Использовали комбинацию Debezium (для чтения MySQL binlog) и Kafka (для буферизации изменений). Не идеально, но работало.

#Фаза 2: Миграция данных (3 месяца)

Шаг 1: Полный dump и restore. Первая загрузка всех 150TB из MySQL в PostgreSQL.

Детали, которые имеют значение:

  • Использовали pgloader — инструмент, который умеет конвертировать схемы на лету и делать bulk insert
  • Dump делали с read replica, чтобы не нагружать master
  • Bulk insert в PostgreSQL делали с COPY — это на порядок быстрее INSERT
  • Отключили индексы и constraints на время загрузки (создали их потом)
  • Использовали параллельные потоки — не один процесс, а 16 одновременных transfer-воркеров

Шаг 2: Continuous sync. Debezium читает MySQL binlog и отправляет изменения в Kafka. Специальный consumer применяет эти изменения к PostgreSQL.

Здесь ключевой момент: синхронизация должна быть идемпотентной — если одно изменение применится дважды, данные не должны испортиться.

Шаг 3: Валидация. Каждую ночь — автоматическая сверка данных: случайная выборка записей из MySQL и PostgreSQL сравниваются по всем полям. Расхождения логируются и исследуются.

За два месяца команда «М» нашла:

  • 3 бага в конвертере типов (MySQL TIME с отрицательными значениями)
  • 2 особенности в поведении GROUP BY (которые сломали бы продакшен)
  • 1 кейс, где MySQL позволял вставить дубликат в unique constraint (а PostgreSQL — нет)

#Фаза 3: Переключение (1 месяц)

Шаг 1: Double Write. Приложения начинают писать в обе базы одновременно. Код меняется так:

# Было:
db.execute("INSERT INTO orders ...")

# Стало:
db_mysql.execute("INSERT INTO orders ...")
db_pg.execute("INSERT INTO orders ...")

Но это хитрый момент: гарантировать консистентность между двумя записями нельзя. Если MySQL INSERT прошёл, а PostgreSQL упал — что делать? Команда «М» решила: писать в обе базы, но считать истиной MySQL (old source of truth), пока не завершится миграция. PostgreSQL может проседать — потом нагонит через CDC.

Шаг 2: Read test. Аналитики переключаются на чтение из PostgreSQL. Если падают запросы — возвращаются на MySQL. Это длится 2 недели.

В результате:

  • 12% аналитических запросов упали (разный синтаксис, несовместимость функций)
  • 3% данных имели расхождения (исправлены вручную через скрипты)
  • 1 критический баг: PostgreSQL требует более строгих прав для DELETE без WHERE

Шаг 3: Cutover. Выбирается окно в 4 часа утра воскресенья (самый низкий трафик). Все приложения переключаются на PostgreSQL как на primary. MySQL остаётся как read-only (на случай отката).

Шаг 4: Monitoring. Первые 48 часов — полный мониторинг каждого запроса. Любая аномалия — и команда откатывается на MySQL.

Команда «М» провела 4 сухих запуска (dry-run) перед реальным cutover. На реальном — всё прошло за 2 часа.

#Почему это заняло 6 месяцев, а не 2 недели

Потому что 90% времени — это не переливание данных. Это:

  • Тестирование совместимости (2 месяца)
  • Валидация (3 месяца непрерывной сверки)
  • Координация с 5000 приложений (каждое нужно проверить)
  • Психологическая готовность команды (это реально страшно)

#Что пошло не так (честно)

  • CDC lag. На пике нагрузки Debezium отставал на 15 минут. Пришлось тюнить Kafka и консьюмер-группы.
  • Schema drift. Пока команда мигрировала, разработчики меняли схему в MySQL. Каждое изменение нужно было зеркалить в PostgreSQL. Пропустили два alter table — пришлось делать ручную синхронизацию.
  • Разное поведение в edge cases. MySQL позволяет вставить '0000-00-00' как дату. PostgreSQL — нет. 150 тысяч записей с такой датой пришлось чистить перед миграцией.
  • Performance regression. Некоторые запросы, которые летали в MySQL (с его query optimizer), в PostgreSQL работали в 10 раз медленнее. Пришлось переписывать запросы (а это значило — менять код приложений). На это ушёл ещё месяц.

#Чек-лист для миграции базы данных

  • Проверить все типы данных и их совместимость
  • Написать CDC/replication между старой и новой базой
  • Сделать полный dry-run хотя бы дважды
  • Настроить непрерывную валидацию данных (automated comparison)
  • Подготовить rollback план (MySQL остаётся read-only)
  • Заранее оптимизировать тяжёлые запросы на новой базе
  • Проверить edge cases: пустые строки, null, special characters, даты
  • Написать скрипт для schema drift sync
  • Провести нагрузочное тестирование (старая vs новая)
  • Быть готовым, что 5-10% запросов придётся переписать

#История 5: Cloud migration — on-premise → AWS/GCP/Azure

#Компания L: банк, который решил переехать в облако

Компания «L» — крупный европейский банк, 20+ лет истории. Дата-центры: три, разбросаны по Европе. Железо: HP, Dell, Cisco. Сеть: MPLS с пропускной способностью 40G между ЦОДами. Команда: сотни DevOps, системных администраторов, DBA.

В 2018 году правление принимает стратегическое решение: «Мы переезжаем в облако». AWS как primary provider, Azure как backup.

Причины:

  1. CapEx vs OpEx — банку надоело покупать железо на 3 года вперёд
  2. Скорость — выделить новый сервер в ЦОДе занимало 3 недели. В облаке — 3 минуты
  3. Compliance — регуляторы требуют disaster recovery с RPO < 15 минут (сделать это в ЦОДе дорого)

#Наивный план

«Берём все виртуалки, конвертируем в AMI, заливаем в AWS, Profit!»

Первый месяц: команда конвертирует 1500 VM в AMI (через AWS Import/Export). Ещё 500 VM — через CloudEndure (continuous replication). Результат: инфраструктура переехала, но всё работает ровно так же, как работало в ЦОДе. Просто теперь это дорогие виртуалки в AWS вместо дешёвых в ЦОДе.

Облачная экономика не работает на lift-and-shift.

#Фаза 1: Lift and Shift (месяцы 1-6)

3000 VM перенесены в AWS. Результаты:

  • Счёт за инфраструктуру вырос на 40% (AWS дороже, чем собственное железо)
  • Никакого выигрыша в скорости разработки
  • Операционные проблемы: настройка VPC, security groups, IAM — всё новое
  • Команда в шоке: «Мы переехали в облако, а оно дороже и медленнее»

Ошибка: банк перенёс не только приложения, но и архитектурные ограничения on-premise. В ЦОДе у вас есть «виртуалка на 32 CPU и 256GB RAM». В AWS у вас тоже может быть такая виртуалка. Но смысл облака — не в виртуалках.

#Фаза 2: Реархитектура (месяцы 7-18)

Команда осознаёт: облако — это не «ЦОД как сервис». Облако — это другой способ мышления.

Что изменили:

  1. Stateless everywhere. Все приложения переведены на stateless архитектуру. Данные — в RDS, ElastiCache, S3. Сами приложения — в Auto Scaling Groups или ECS. Если одна инстанция умирает — новая поднимается и подхватывает трафик.

  2. Managed services вместо самописного зоопарка. Вместо собственного Kafka кластера — Amazon MSK. Вместо своего RabbitMQ — Amazon MQ. Вместо своего Elasticsearch — Amazon OpenSearch. Да, это дороже, но не надо держать команду, которая чинит Kafka в 3 часа ночи.

  3. Immutable infrastructure. Вместо «зайти на сервер по SSH и поправить конфиг» — Packer + Terraform. Сервер пересоздаётся, не патчится.

  4. Disaster recovery. Раньше DR означало «второй ЦОД с таким же железом». Теперь — Terraform apply в другом регионе. Стоимость DR упала в 10 раз.

Что не изменили:

  • Не все legacy-монолиты удалось распилить. Некоторые так и живут на больших EC2 инстанциях.
  • Mainframe (IBM z/OS) не переехал в облако. Остался в ЦОДе. AWS не умеет запускать мейнфреймы.

#Фаза 3: Оптимизация (месяцы 19-36)

Через полтора года банк начинает реально экономить:

  1. Reserved Instances. Купили RI на 3 года для базовых нагрузок. Счёт упал на 40% относительно on-demand.
  2. Spot instances. Для нечувствительных ко времени задач (data processing, batch jobs) — spot. Экономия ещё 60% на этих задачах.
  3. S3 lifecycle. Данные старше 30 дней — в S3 Infrequent Access. Старше года — в S3 Glacier. Старше 7 лет — delete.
  4. Right-sizing. AWS Trusted Advisor показал, что 30% виртуалок переразмерены. Уменьшили. Счёт ещё -15%.

#Что пошло не так

1. Hidden costs

Банк не учёл стоимость data transfer. Когда 200 микросервисов в разных availability zones общаются друг с другом — это терабайты трафика. AZ-to-AZ трафик в AWS платный. Счёт за data transfer оказался вторым по величине после compute.

Решение: размещать связанные сервисы в одной AZ (или хотя бы осознавать цену cross-AZ трафика).

2. Security group hell

3000 VM × 5 security groups каждая = 15000 security group rules. Это не управляемо.

Решение: централизованная система управления security groups через Terraform + CI/CD. Любое изменение — через Pull Request.

3. Compliance audit shock

Регулятор (ECB — European Central Bank) пришёл с аудитом. Первый вопрос: «Где физически находятся ваши данные?» AWS не даёт клиентам выбирать конкретный стойку в дата-центре — только регион. Для банка это была проблема: по регуляторным требованиям, данные должны быть в ЕС, на территории конкретной страны.

Решение: AWS Outposts (физические стойки AWS в ЦОДе банка) для критических данных + стандартный AWS для всего остального.

4. «Мы переехали, но ничего не изменилось»

Самый болезненный урок. Банк потратил 18 месяцев на переезд, но разработчики продолжали писать код, как для on-premise. Stateless? «Нет, мы храним сессии на сервере». Автомасштабирование? «Нет, у нас фиксированные инстанции». Managed services? «Нет, мы поднимаем свой Kafka, как раньше».

Культурная трансформация заняла ещё 12 месяцев. И это нормально: облако — это не технологии, это культура.

#Ключевые показатели после 3 лет

Метрика On-premise AWS (год 1) AWS (год 3)
Время выделения сервера 3 недели 3 минуты 3 минуты
CapEx/год на инфраструктуру €12M €0 €0
OpEx/год €8M (электричество, охлаждение, персонал) €22M €18M
DR готовность 8 часов RTO 4 часа RTO 1 час RTO
Время релиза новой фичи 2 недели 1 неделя 3 дня
Команда инфраструктуры 45 человек 50 человек 30 человек

Да, облако обошлось дороже, чем on-premise (если считать только инфраструктуру). Но если считать скорость доставки ценности — облако выиграло.

#Итог для тех, кто планирует cloud migration

  1. Не делайте lift-and-shift. Это дороже on-premise. Облачная экономика работает, только если вы меняете архитектуру.
  2. Готовьтесь к hidden costs. Data transfer, NAT Gateway, ALB — каждая мелочь влетает в копеечку. Считайте заранее.
  3. Облако не убирает необходимость в DevOps. Оно меняет DevOps. Вместо управления серверами — управление Terraform стейтами и IAM политиками.
  4. Культура важнее технологии. Если команда продолжает писать stateful код на фиксированных инстанциях — облако не поможет.
  5. Гибрид — это нормально. Не всё должно быть в облаке. Mainframe, legacy COBOL, real-time trading системы могут остаться on-premise.

#А что в итоге?

Пять историй — пять разных уроков. Давайте соберём общее.

#Главные выводы

  1. Микросервисы не лечат проблемы скорости. Они часто их усугубляют. Если вы не имеете организационной потребности в независимом деплое — модульный монолит работает не хуже, а проще.

  2. Архитектура должна расти с компанией. То, что работало для 15 человек, сломается на 200. То, что нужно 200 — убьёт стартап на 15. Рефакторьте под текущие боли, не под будущие страхи.

  3. Rewrite — это почти всегда плохая идея, но не абсолютно. Strangler Fig Pattern работает в 9 из 10 случаев. Полный rewrite — когда старый код нельзя поддерживать, и у вас есть ресурс на 1-3 года параллельной разработки.

  4. Миграция базы данных — это 90% тестирования и 10% переливания. Не недооценивайте CDC, валидацию, и edge cases. План отката — обязателен.

  5. Облако — это не ЦОД как сервис. Без архитектурных изменений облако будет дороже on-premise. Экономия начинается, когда вы переходите на stateless + managed services.

#Анти-паттерны (ещё раз, кратко)

  • Big Bang. Переключать всё сразу — гарантированный способ получить P0 инцидент. Мигрируйте инкрементально.
  • Rewrite с теми же людьми без остановки разработки. Spoiler: ни то, ни другое не будет сделано хорошо.
  • Lift-and-shift в облако. Вы заплатите больше и получите те же проблемы.
  • Микросервисы без организационной готовности. Distributed monolith — худшее из двух миров.
  • «Сделаем идеально». Архитектура — это компромиссы. Идеальной не будет.

#Чек-лист для любой архитектурной трансформации

  • Есть ли у вас чёткий критерий успеха («что считается законченным»)?
  • Есть ли план отката (rollback)?
  • Знаете ли вы, какие компромиссы вы принимаете?
  • Бизнес понимает, что вы делаете и сколько это займёт?
  • Команда обучена новому стэку?
  • Можете ли вы мигрировать инкрементально (а не big bang)?
  • Есть ли у вас автотесты для нового кода?
  • Есть ли у вас мониторинг для обнаружения проблем после миграции?
  • Как быстро вы узнаете, что что-то пошло не так? (observability)
  • Готовы ли вы признать ошибку и откатиться, если что-то пошло не так?

#Ключевые тезисы

  1. Архитектурная трансформация — это про культуру и организацию, а не про технологии. Если не изменить то, как команда принимает решения, технологический переезд не даст результата.

  2. Нет единственно правильного пути. Monolith, microservices, modular monolith — всё может быть правильным в разное время в одной и той же компании.

  3. Рефакторинг никогда не заканчивается. Как только вы закончили одну миграцию, готовьтесь к следующей. Архитектура — это непрерывный процесс принятия решений, а не проект с датой завершения.

  4. Цена ошибки при миграции — время, а не код. Код можно переписать. Время, упущенное перед конкурентами, и доверие пользователей — нет.

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

Forward reference: AI-assisted рефакторинг — одна из самых эффективных областей применения AI-агентов (Claude Code показывает 85-95% success на multi-file refactor). Как AI меняет подход к миграции legacy-кода и рефакторингу — в Главе 34 «Как AI трансформирует SDLC».