Моноліт → мікросервіси
Міграція з моноліту на мікросервіси — це перенесення можливостей наявної системи в окремі сервіси по одній, без зупинки системи. Міграція, яка працює, — не переписування з датою переходу, а послідовність невеликих виділень за фасадом, кожне з яких обґрунтоване виміряним обмеженням і має шлях назад; найскладніше тут — дані. Сторінка починається з одного виділення як шістнадцяти кроків; далі — що виділяти першим, шість способів переносити код без зупинки, поділ даних, модель і команди, як вести міграцію й коли зупинитися, п’ять задокументованих кейсів і де все це вироджується.
- Патерн
- Strangler fig (Фаулер, 2004)
- Одиниця роботи
- Одне виділення зі шляхом назад на кожному кроці
- Найскладніше
- Поділ даних
Одне виділення від початку до кінця — шістнадцять кроків, як зрозуміти, що кожен зроблено, і шлях назад
Міграція, яка працює, — це не один великий проєкт. Це той самий невеликий проєкт, повторений багато разів: винести одну можливість із моноліту в сервіс, поки система весь час працює. Кожне повторення має ті самі шістнадцять кроків у тому самому порядку. Найважливіший стовпець — останній. Він каже, як скасувати кожен крок, і майже для всіх відповідь займає хвилини. Наступні розділи посилаються на кроки за номерами.
| # | Крок | Зроблено, коли | Шлях назад |
|---|---|---|---|
| 1 | Назвати обмеження й виміряти його — «платежі чекають 21 день на чужі релізи» | Число, ціль і дату записано | Скасовувати нічого. Якщо числа немає, зупиніться тут: робота — модулі, а не сервіси |
| 2 | Обрати можливість для виділення | Її роботу можна описати одним реченням без «і», і її викликає мало модулів | Обрати іншу — ще нічого не перенесено |
| 3 | Підняти мінімум платформи: конвеєр, трасування, логи, алерти, графік чергувань | Порожній сервіс доходить до продакшну через конвеєр і з’являється в трасі | Для користувачів нічого не змінилося |
| 4 | Поставити перед монолітом фасад, що пересилає все без змін | Увесь трафік іде через нього, а помилки й затримка не змінилися | Спрямувати трафік назад на моноліт |
| 5 | Провести межу модуля всередині моноліту: один публічний інтерфейс | Перевірка в CI падає на будь-якому новому імпорті нутрощів модуля | Revert — це досі одна одиниця деплою |
| 6 | Дати модулю власну схему: жодних JOIN і зовнішніх ключів через межу | Перевірка запитів не знаходить SQL, що перетинає межу | Revert — це досі одна база |
| 7 | Пустити кожне звернення через інтерфейс, зокрема звіти й пакетні задачі | Ніщо поза модулем не торкається його таблиць | Revert |
| 8 | Домогтися, щоб тести модуля запускалися окремо | Його тести проходять без жодного іншого завантаженого коду | Скасовувати нічого |
| 9 | Збудувати сервіс із власним сховищем; заповнити його й тримати синхронним із журналу бази моноліту | Задача звірки тиждень не показує розходжень | Вимкнути синхронізацію — копію ще ніхто не читає |
| 10 | Паралельний запуск: надсилати читання в обидва, віддавати відповідь моноліту, логувати кожне розходження | Жодного розходження за повний бізнес-цикл — тиждень, кінець місяця | Зупинити експеримент |
| 11 | Перевести читання на сервіс — 1%, 10%, 50%, 100% — під прапорцем | Помилки й затримка лишаються на базовому рівні, коли всі читання йдуть на сервіс | Перемкнути прапорець — дані моноліту досі актуальні |
| 12 | Перевести запис: сервіс стає джерелом істини й синхронізує зміни назад у старі таблиці | Кожен запис іде через сервіс, а зворотна синхронізація не показує розходжень | Перемкнути запис назад — старі таблиці актуальні, тож моноліт може знову взяти все на себе |
| 13 | Перевести кожного читача, що лишився, — звіти, експорти, інші модулі — на сервіс чи аналітичну копію | Журнали запитів місяць не показують читань старих таблиць | Повернути читача назад |
| 14 | Зупинити зворотну синхронізацію й видалити старі таблиці — після резервної копії | Таблиць немає, і нічого не зламалося | Відновити з резервної копії — перший крок без швидкого шляху назад |
| 15 | Видалити код модуля з моноліту й старий маршрут із фасаду | Коду немає | Revert і повторний деплой — уже не прапорець |
| 16 | Виміряти обмеження з кроку 1 відносно базового рівня й вирішити, що далі | Число опубліковано, і наступне виділення обрано — або міграція зупиняється | — |
До першого виділення: причина й кандидат
Більшість міграцій починається з картинки кінцевої точки — схеми з дванадцяти сервісів — і рухається назад. Саме через такий порядок так багато з них тягнуться два роки й закінчуються вісьмома сервісами, монолітом, у якому досі лежить головна логіка, і командою, повільнішою, ніж на старті. Порядок, що працює, починається з кроку 1: названого обмеження, яке можна виміряти сьогодні. Кожна причина, яку називають для міграції, або проходить цю перевірку, або вказує на дешевшу роботу, і таблиця розкладає найпоширеніші.
| Названа причина | Чи виправдовує це сервіс? | Що перевірити спершу |
|---|---|---|
| «Код — безлад» | Ні: поділ піде по зв’язності, що вже існує | Межі модулів — рядки 1–7 на сторінці про моноліт |
| «Деплої повільні й страшні» | Ні: частота деплою — не властивість архітектури | Конвеєр, менші партії, feature flags — рядки 8–12 там само |
| «Одній частині треба значно більше CPU чи пам’яті» | Іноді | Окремий пул процесів, запущений з тієї самої збірки |
| «Одній частині потрібна інша мова чи рантайм» | Так, якщо виграш виміряний, а не бажаний | Бібліотека або невеликий допоміжний процес |
| «Дані карток мають вийти з-під меж аудиту» | Так | Чи погодиться аудитор натомість на окремий процес і базу |
| «Команди володіють чистими модулями й досі блокують одна одну на релізі» | Так — саме для цього випадку стиль і існує | Розмір партій і прапорці — щоб переконатися, що очікування не зводиться до великих релізів |
| «Платформа під нами доживає віку» | Це змушує переїхати, але не диктує архітектуру | Чи не дешевше перенести все всередині моноліту |
| «У всіх уже мікросервіси» | Ні | Яке обмеження виміряно |
Крок 2 обирає першого кандидата, і це не має бути ні найскладніша частина, ні найважливіша. Перше виділення — репетиція. Його завдання — навчити команду конвеєра, трасування, чергувань і передачі даних, поки помилка ще дешева. Тож йому потрібні небагато викликачів, чітка межа й малий радіус ураження в разі збою. Таблиця оцінює звичних кандидатів.
| Кандидат | Хто викликає | Наскільки заплутані дані | Якщо зламається | Вердикт |
|---|---|---|---|---|
| Сповіщення | Багато модулів, в один бік | Власні шаблони й журнал надсилань | Повідомлення приходять пізніше | Першим — класична репетиція |
| Обробка зображень | Лише завантаження | Файли, а не таблиці | Завантаження повільніші | Першим — часто це ще й причина масштабування |
| Генерація PDF і звітів | Кілька екранів | Читає дані інших модулів | Документи приходять пізніше | Рано — щойно читає через інтерфейси |
| Пошук | Більшість екранів | Копія чужих даних, яку вже живлять зміни | Результати застарівають | Рано — це вже модель для читання |
| Автентифікація | Кожен запит | Таблиця користувачів — у центрі | Ніхто не може увійти | Пізніше — коли платформа себе довела |
| Платежі | Оформлення замовлення й повернення | Гроші зі суворою узгодженістю | Гроші загублено або списано двічі | Пізніше — часто заради відповідності вимогам |
| Замовлення, ядро предметної області | Усе | З’єднані з усім | Бізнес зупиняється | Останнім або ніколи |
Від другого виділення впорядковуйте за обмеженням із кроку 1, а не за зручністю. І тримайте кожне достатньо малим, щоб воно завершувалося за кілька тижнів. Виділення, що триває пів року, перестало бути зворотним у будь-якому практичному сенсі, бо моноліт, з якого воно вийшло, за цей час поїхав уперед.
Шість способів переносити код, не зупиняючи систему
Найвідоміший патерн має ботанічну назву. Під час відпустки в дощових лісах Квінсленду 2001 року Мартін Фаулер побачив фікуси-душителі (strangler fig). Фікус починається як насінина в заглибині дерева-господаря, росте вниз до землі й угору до світла і зрештою може стояти сам там, де було дерево. Через пару років він використав це як образ того, як його колеги замінювали застарілі системи, у короткому дописі «Strangler Application». Згодом він перейменував його на «Strangler Fig Application», бо люди запам’ятовували жорстоке слово й забували рослину. У серпні 2024 року він переписав допис, і нова версія називає чотири види діяльності, які Ян Картрайт, Роб Горн і Джеймс Льюїс визначили для будь-якої поступової заміни. Зрозуміти, яких результатів ви хочете. Вирішити, як розбити проблему на менші частини. Успішно поставити ці частини. І змінити організацію, щоб так могло тривати й далі.
Той самий допис наводить аргумент, якому команди часто опираються. Поступова заміна потребує перехідної архітектури: фасадів, задач синхронізації й перемикачів, що існують лише поки старе й нове працюють поруч, а потім їх видаляють. Це схоже на марнування. Відповідь Фаулера: менший ризик і раніша цінність переважують цю ціну. Кроки 4, 9 і 12 — саме такий код. Закладайте його в бюджет, а не обурюйтеся ним.
Фасад ставлять першим, ще до будь-якого виділення (крок 4). Із фасадом на місці перше виділення є зміною маршрутизації, і його відкат — теж. Без нього перше виділення — це зміна в кожному викликачі, а відкат — ще одна зміна в кожному викликачі. Книжка Сема Ньюмана «Monolith to Microservices» (2019) збирає strangler fig і ще п’ять споріднених патернів для перенесення коду, і кожен пасує до своєї ситуації:
| Патерн | Як працює | Коли застосовувати | Чого коштує |
|---|---|---|---|
| Strangler fig application | Фасад надсилає кожен маршрут або в моноліт, або в новий сервіс | До можливості звертаються ззовні — URL, черга, виклик API | Проксі, який треба експлуатувати; він бачить лише те, що перетинає край |
| Композиція інтерфейсу | Цілі сторінки чи частини сторінки віддає новий сервіс | Можливість має власні екрани | Два фронтенд-стеки, поки це триває |
| Branch by abstraction | Усередині моноліту інтерфейс дозволяє старій і новій реалізації співіснувати за перемикачем | Можливість викликають із глибини коду | Шар абстракції, який треба додати, а потім прибрати |
| Паралельний запуск | Обидві реалізації обробляють той самий запит; віддають стару відповідь, а дві порівнюють | Коректність важлива, а описати її важко | Подвійна робота, а побічні ефекти треба тримати поза новим шляхом |
| Decorating collaborator | Проксі передає запит монолітові без змін, а потім за відповіддю запускає нову поведінку в сервісі | Нову поведінку додають, не торкаючись моноліту, — бали за кожне замовлення | Працює лише тоді, коли відповідь несе достатньо даних |
| Change data capture | Новий сервіс реагує на зміни, прочитані з журналу бази моноліту | У моноліті нічого не можна ні змінити, ні викликати | Прив’язує сервіс до схеми моноліту |
Branch by abstraction: шов усередині коду
Фасад бачить лише те, що перетинає край системи. Багато з того, що ви хочете виділити, викликають зсередини — функцію ціноутворення, якою користуються оформлення замовлення, рахунки й адмінка. Для цього випадку є branch by abstraction. Назву техніці дав Пол Геммант, коли обстоював розробку в trunk, а саму ідею він приписує Стейсі Керлу; кроки Фаулер описав у січні 2014 року. Поставити інтерфейс перед старим кодом. Перевести на інтерфейс кожного викликача, по одному. Збудувати нову реалізацію за тим самим інтерфейсом — тут це клієнт, що викликає новий сервіс. Перемикати викликачів прапорцем. Коли старою реалізацією вже ніхто не користується, видалити її, а згодом і інтерфейс, якщо він більше не виправдовує себе. Кожен коміт на цьому шляху лишає систему такою, що збирається й готова до релізу, тож жодну довгоживучу гілку зливати не доводиться.
Паралельний запуск: довести новий код, перш ніж йому довіряти
Переписаний код вартий рівно стільки, наскільки точно він повторює стару поведінку, а її рідко хтось записував. Паралельний запуск (крок 10) вимірює її замість того, щоб вгадувати. Кожен запит або вибірка запитів іде в обидві реалізації. Користувач отримує стару відповідь. Нову порівнюють із нею, і кожну різницю записують із достатнім контекстом для налагодження. GitHub збудував для цього бібліотеку Scientist і описав, як із нею замінював код, що зливає pull requestзміна, запропонована на рев’юЗапит на злиття однієї гілки в іншу, де зміну переглядають, обговорюють і перевіряють пайплайном, перш ніж вона потрапить у головну гілку; у GitLab те саме зветься merge request. Його ціна — очікування: pull request, що днями чекає на рев’ю, і породжує довгоживучі гілки та болісні злиття.Докладніше в статті →Усі терміни. Після чотирьох днів виправлення того, що знайшов експеримент, — зокрема функції, якої в новому рушії злиття не було взагалі, — новий код 24 години обробляв 100% запитів без жодного розходження й жодного повільного випадку. До вимкнення старого шляху перевірили десятки мільйонів злиттів. Khan Academy робив те саме на краю своєї системи й називав це тестуванням пліч-о-пліч: шлюз надсилав більшість трафіку запиту в моноліт, а частину — в нові сервіси, і результати порівнювали, доки не виправили кожну різницю.
Поділ даних — сама міграція
Виділити код — тиждень роботи. Виділити дані — це і є міграція. Саме тут команди, що пропустили цей розділ, опиняються з двома сервісами на одній базі — тим результатом, якого вся вправа мала уникнути. Ньюман описує три порядки: спершу розділити базу, спершу розділити код або все одразу. Від «усе одразу» він застерігає. З його досвіду, найпоширеніший — спершу код, бо він раніше дає результат. Його звичний провал на практиці — зупинитися на цьому, коли новий сервіс досі читає таблиці моноліту.
Шістнадцять кроків оминають обидві пастки, переносячи по одній речі за раз. Поділ починається всередині моноліту, де він дешевий (кроки 6–7). Розділіть таблиці модуля логічно: та сама база, власна схема, жодних JOIN і зовнішніх ключів через межу. Уже це виявляє кожну приховану залежність, бо кожен перетин тепер валить перевірку, і кожен виправляють, поки стара система працює. Потім дані копіюють і тримають синхронними (9), переводять читання (10–11), переводять запис (12), переводять останніх читачів (13) — і лише тоді щось видаляють (14).
GitHub зробив саме це у великому масштабі між 2019 і 2021 роками — на кластері бази, де жили користувачі, репозиторії, issues і pull request. Спершу з’явилися віртуальні розділи. Таблиці, що належать разом, згрупували в домени схеми, перелічені в простому YAML-файлі в застосунку на Rails. Групи забезпечували два лінтери: один валив будь-який запит, що звертався до таблиць двох доменів, інший — будь-яку транзакцію, що їх охоплювала. Наявні порушення позначили як винятки, і це перетворило їх на видимий беклог. Домен, у якому не лишилося винятків, був готовий до переїзду. Фізичне перенесення робили через Vitess або через власне перемикання на реплікації MySQL, що переводило запис за кілька десятків мілісекунд у найтихішу годину доби; одне таке перемикання перенесло 130 найнавантаженіших таблиць одразу. 2019 року кластер відповідав на 950 000 запитів на секунду. 2021 року ті самі таблиці, розподілені між кількома кластерами, відповідали на 1 200 000 — а середнє навантаження на кожен хост зменшилося вдвічі.
Не кожна база ділиться по чистих лініях, і книжка Ньюмана називає патерни для незручних випадків. Ось сім, що трапляються майже в кожній міграції:
| Патерн | Що робить | Коли пасує | Підступ |
|---|---|---|---|
| Подання бази (database view) | Новий сервіс читає таблиці моноліту через подання, що ховає їхню структуру | Потреба лише читати, на ранньому етапі | Досі прив’язаний до бази моноліту |
| Сервіс-обгортка для бази | Тонкий сервіс перед таблицями, які ще ніхто не може розплутати | Викликачі мають перестати лізти в таблиці напряму | Безлад сховано, а не прибрано |
| Зміна власника даних | Власником стає сервіс; моноліт викликає його замість таблиць | Можливість переїхала, і її дані мають іти слідом (крок 12) | Кожен запит моноліту до цих таблиць треба переписати |
| Синхронізація даних у застосунку | Застосунок пише в обидва сховища, поки триває переїзд | Немає журналу бази, з якого можна синхронізуватися | Два записи — не транзакція: звіряти або розходитися |
| Tracer write | Нове сховище стає джерелом істини для одного зрізу даних за раз | Даних забагато або вони надто навантажені, щоб перенести за один крок | Два джерела істини, доки не переїде останній зріз |
| Поділ таблиці | Таблиця, чиї стовпці належать двом можливостям, стає двома таблицями | Одна таблиця «customers» тримає і дані акаунта, і дані доставки | Змінюється кожен запит, що читав обидві половини |
| Перенести зв’язок зовнішнього ключа в код | JOIN стає зверненням до іншого сервісу; цілісність перевіряють у коді | Кожен ключ через межу, знайдений на кроці 6 | Повільніші читання й «сироти», яким база більше не запобігає |
Саме на звітах зазвичай з’ясовується, що поділ даних не завершено (крок 13). Звіт, що поєднує таблиці шести модулів, роками працював, бо все лежало в одній базі. Після поділу чесних відповідей дві. Або звіт викликає сервіси, що володіють даними, або читає аналітичну копію, яку живлять події чи журнали бази. Ньюман радить окрему схему, збудовану для звітності, а не запити до робочих сховищ. Хай там як, знайдіть кожного читача до кроку 14. Той один щомісячний експорт, про який ніхто не пам’ятав, — класична причина відкату першого числа.
Модель і команди: прошарок проти псування й закон Конвея
Новому сервісу знадобляться дані старої системи, а модель старої системи зазвичай сформована п’ятнадцятьма роками випадковостей. Якщо новий сервіс говорить цією моделлю, він успадковує її назавжди, і виділяти його майже не було сенсу. Ерік Еванс назвав захист від цього в книжці «Domain-Driven Design» 2003 року: прошарок проти псування (anti-corruption layer). Це межа перекладу — тонкий шматок коду, що з одного боку говорить старою моделлю, а з іншого — моделлю нового сервісу, і належить новому сервісу. Він навмисно потворний, навмисно ізольований і навмисно тимчасовий. Коли старе джерело нарешті зникне, ви видалите один компонент, а не виколупуватимете модель із кожного файлу. Ось що він зазвичай перекладає:
| У старій моделі | Що робить прошарок | У новому сервісі |
|---|---|---|
Стовпець status з одинадцятьма значеннями, частину з яких ставить лише одна пакетна задача | Зіставляє кожен код з іменованим станом і відхиляє коди, яких не розуміє | Явні стани з дозволеними переходами між ними |
| Запис клієнта, що водночас є адресою доставки | Розділяє його на два поняття | Клієнт і адреси, на які він замовляє доставку |
| Час, збережений рядками в місцевому часі | Розбирає їх з урахуванням історичного часового поясу сервера | Мітки часу в UTC |
| Суми, збережені як числа з рухомою комою | Округлює один раз, явно, і логує, що саме округлило | Цілі копійки плюс код валюти |
Мелвін Конвей 1968 року помітив, що структура системи зрештою віддзеркалює структуру комунікації організації, яка її будувала. У міграції це не курйоз, а найсильніша сила в кімнаті. Якщо виділеним сервісом спільно володіють чотири команди, у ньому виростуть чотири внутрішні фракції й координаційна нарада, і він не деплоїтиметься незалежно, хоч би що казала схема. Застосована свідомо, та сама сила стає інструментом. Джонні Лерой і Метт Сімонс 2010 року назвали це зворотним маневром Конвея (inverse Conway maneuver): змініть межі команд на ті, які ви хочете бачити в сервісів, і архітектура піде слідом.
Він також задає чесну верхню межу міграції. Не можна отримати більше сервісів, ніж є команд, щоб ними володіти, а сервіс, яким не володіє ніхто, гниє в розподіленій системі швидше, ніж будь-коли гнив у моноліті. Тому крок 3 свідомо містить графік чергувань. Команда-власник експлуатує новий сервіс із дня, коли він отримав перший запит, — правило Amazon, словами Вернера Фогельса: «You build it, you run it».
Як вести міграцію: потужність, темп і коли зупинитися
Міграція конкурує з роботою над фічами за тих самих людей, і цю конкуренцію вирішує той, хто присутній у кімнаті, коли планують квартал. Міграція, яку у фоні ведуть ті, у кого є вільний час, триває три роки й не завершується ніколи. Міграція як повне заморожування фіч програє суперечку на першому ж комерційному дедлайні. Виживає заявлена частка потужності — одна команда або фіксована частка кварталу кожної команди, — де кожне виділення приземляється як видимий результат, а не як прогрес за планом.
Не давайте старому коду рости, поки ви його переносите. Правило Khan Academy під час переписування: кожну нову фічу в старому коді на Python мала особисто погодити людина, що очолювала інженерію, бо кожен новий рядок — це ще один рядок для перенесення. Той самий проєкт міряв прогрес тим, що переїхало, а не тим, що збудовано: часткою трафіку, яку обслуговує новий бекенд, кількістю готових полів APIApplication Programming Interface — контракт між програмамиПравила, за якими одна програма звертається до іншої: які запити можна надіслати, у якому вигляді й що прийде у відповідь. Для того, хто звертається, це контракт — усе, що за ним, лишається чорною скринькою, і саме тому другий бік можна переписати, не зламавши вас.Докладніше в статті →Усі терміни та обсягом, що зростав, коли роботу краще розуміли. Таблиця перелічує заміри, що відрізняють робочу міграцію від тієї, що застрягла.
| Замір | Здорова ознака | Тривожна ознака |
|---|---|---|
| Обмеження з кроку 1 | Зрушує за кілька тижнів після кожного виділення | Не змінилося після третього виділення |
| Час виконання в зоні виділення | Коротший, ніж раніше | Довший — зміни тепер чекають на два конвеєри |
| Фічі, що зачіпають моноліт і сервіс одночасно | Рідкісні й стають ще рідшими | Більшість фіч |
| Частка трафіку на новому шляху | Зростає крок за кроком | Місяцями стоїть на тій самій частці |
| Сервісів на команду-власника | Кілька | Сервісів більше, ніж людей |
| Інциденти, що перетинають межу | Меншають | Більшають із кожним виділенням |
Ніщо в цьому патерні не вимагає, щоб моноліт зник, а перетворення його зникнення на мету — це те, як міграція переживає власне обґрунтування. Кожне виділення обґрунтовувалося обмеженням. Коли решта частин нічого не блокує, за рештою виділень немає жодного аргументу, крім симетрії. Постійний гібрид — кілька сервісів навколо добре модуляризованого ядра, у якому досі лежить більша частина предметної області, — поширений і цілком гідний кінцевий стан. У більшості організацій це ще й точка найкращої віддачі: частини зі справжньою потребою в незалежності її мають, а решта не платить за мережу, яка їй ніколи не була потрібна. Інженери SoundCloud 2015 року сказали те саме: дещо з того, що робив моноліт, було таким малим і стабільним, що його, можливо, дешевше лишити там назавжди.
Міряйте міграцію обмеженням, яке назвали на кроці 1, а не кількістю сервісів. Якщо платежі тепер виходять двічі на тиждень замість разу на місяць, це виділення себе окупило, і ви можете сказати це числом. Якщо кількість сервісів зросла з одного до дев’яти, а час виконання не зрушив, дев’ять сервісів — це не поступ. Це дев’ять конвеєрів деплою.
П’ять кейсів і те, чого в кожному бракувало
П’ять кейсів охоплюють двадцять років — від міграції, що сформувала саме слово, до тієї, що завершилася 2021 року. Кожен розповідають люди, які робили цю роботу, і в кожному основну вагу несе інший крок із шістнадцяти.
| Кейс | Що сталося | Що це показує |
|---|---|---|
| Amazon, 2001–2006 | Моноліт Obidos більше не масштабувався; спільні бази не лишили ні ізоляції, ні чіткого власника | Правило, що з цього вийшло, стосувалося даних і власності, а не розміру |
| Netflix, 2008–2016 | Пошкодження бази на три дні зупинило відправлення DVD; через сім років вимкнули останні частини дата-центру | Білінг і дані клієнтів переїхали останніми, і самі вони забрали роки (кроки 12–14) |
| SoundCloud, 2012–2015 | Карта потоку цінності показала, що фічі йшли два місяці, переважно в очікуванні; нові фічі пішли в сервіси, а старі лишилися повільними | Спершу виміряти обмеження (крок 1) — і те, що нові сервіси самі старого коду не лагодять |
| GitHub, 2015–2021 | Переписаний код доводили паралельними запусками; головну базу розділили доменами схеми й лінтерами, а потім перенесли | Логічний поділ до фізичного (кроки 6, 10) |
| Khan Academy, 2019–2021 | Моноліт на Python 2 перенесли на 26 сервісів на Go; до 30 серпня 2021 року 95% трафіку йшло на новий бекенд | Вимушений переїзд, зроблений поступово, з тестуванням пліч-о-пліч і забороною на зростання старого коду |
Історія Amazon — та, з якої взявся сам словник. В інтерв’ю 2006 року Вернер Фогельс описав Obidos — застосунок, що від заснування компанії тримав усю бізнес-логіку й логіку відображення Amazon. Роками зусилля з масштабування йшли в бази за ним, доки 2001 року не перестав масштабуватися сам фронтенд. Глибша проблема була в спільності. Бази ділили багато команд і процесів, тож ніщо не мало ізоляції й ніщо не мало чіткого власника, і застрягли і фронтенд, і бекенд. Відповіддю стала сервісно-орієнтована архітектура з одним правилом над усіма іншими: дані інкапсульовано разом із логікою, що з ними працює, доступ до них — лише через опублікований інтерфейс сервісу, без прямого доступу до бази ззовні. Вона розрослася до сотень сервісів. Перечитайте це правило й зверніть увагу, про що воно мовчить. Воно нічого не каже про те, яким малим має бути сервіс. Обмеженням була власність — і виправленням теж.
Netflix почав у серпні 2008 року, коли серйозне пошкодження бази на три дні позбавило його змоги відправляти DVD. Урок, який він виніс, — піти від вертикально масштабованих єдиних точок відмови, як-от однієї реляційної бази у власному дата-центрі. Він вирішив не переносити наявні системи в хмару без змін, бо так переїхали б і всі старі проблеми. Натомість перебудував: від монолітного застосунку до сотень мікросервісів, з денормалізованою моделлю даних на NoSQLбази даних поза SQL-таблицямиПарасолькова назва для баз даних, що не зберігають реляційних таблиць із запитами мовою SQL: сховищ «ключ — значення», документних, ширококолонкових і графових. Більшість із них розміщує кожен запис на машині за ключем, обраним під час проєктування, тож запити з цим ключем дешеві за будь-якого обсягу, а більшість інших — дорогі або неможливі.Докладніше в статті →Усі терміни-сховищах. Усі системи, що працюють із клієнтами, переїхали до 2015 року. Білінг і дані клієнтів забрали час після цього, а останні частини дата-центру вимкнули на початку січня 2016 року — через сім років після старту. Найдовше тривали не сервіси. Найдовше тривали дані, до яких прив’язані найсуворіші правила.
Де це вироджується
Міграція рідко провалюється за один день. Вона провалюється, застрягаючи: половину кроків зроблено для багатьох можливостей, а всі кроки — для жодної. Майже кожна застрягла міграція показує кілька з цього:
- Повне переписування поруч із живою системою: увесь ризик приходить в один день, а стара система тим часом рухається далі.
- Немає названого обмеження, тож успіх рахують створеними сервісами (крок 1).
- Код виділено, а базу лишено спільною — розподілений моноліт із додатковими кроками (кроки 9–14 пропущено).
- Почати з найскладнішої центральної можливості — команда вчиться механіки там, де помилки коштують найдорожче (крок 2).
- Немає фасаду, тож кожне виділення й кожен відкат — це зміна в кожному викликачі (крок 4).
- Виділення, що тягнуться місяцями й перестають бути зворотними, бо моноліт під ними їде вперед.
- Подвійний запис без звірки — сховища тихо розходяться.
- Паралельний запуск, який ще й виконує побічні ефекти, подвоюючи те, що мав спостерігати (крок 10).
- Новий сервіс говорить старою моделлю даних — і завозить у себе той безлад, від якого тікав.
- Старі таблиці видалено до того, як переїхав кожен читач, — і це виявляє щомісячний звіт (кроки 13–14).
- Нові фічі досі приземляються в старому коді, тож обсяг перенесення росте швидше за саме перенесення.
Чесна межа в тому, що метод робить міграцію безпечною, а не короткою. Кожен крок на цій сторінці існує, щоб система працювала, а шлях назад лишався відкритим, і кожен коштує часу: фасад, синхронізація, паралельний запуск, місяць логів перед видаленням таблиці. Netflix знадобилося сім років. Команда, якій результат потрібен до фіксованої дати, не купить його кращим патерном — лише меншим обсягом. Здебільшого правильний обсяг менший за план: кілька виділень, кожне з яких окуповує себе, навколо моноліту, що лишається.
Практична перевірка займає пів дня. Спитайте, яке обмеження стоїть за міграцією і яке його число сьогодні. Складіть список виділень у роботі й кроку, якого досягло кожне; понад два одночасно — тривожний знак. Перевірте, чи стоїть перед монолітом фасад. Для кожного виділеного сервісу з’ясуйте, чи існують досі його старі таблиці і хто їх досі читає. Пошукайте код, що пише у два сховища поспіль. Порахуйте нові фічі, додані в моноліт за минулий квартал. І для наступного виділення запишіть шлях назад, перш ніж писати будь-який код.
Коли це застосовувати
Застосовуйте, коли
- Ви можете назвати конкретне обмеження, яке накладає нинішня форма, і виміряти його.
- Автоматизований деплой і трасування вже працюють, тож виділений сервіс можна експлуатувати з першого дня.
- Є команда, готова володіти виділеним сервісом у продакшні.
- Поставка має тривати під час міграції — фасад тримає кожен крок зворотним.
- Постійний гібрид — прийнятний кінцевий стан, а не провал.
Уникайте, коли
- Причина — заплутаний код: спершу зробіть модулі всередині моноліту; це дешевше й однаково є передумовою.
- Немає кого виділити на володіння новими сервісами — вони будуть спільними, а отже зв’язаними.
- План — переписування з датою переходу, а не послідовність зворотних кроків.
- Дані неможливо поділити, бо предметна область справді потребує транзакційної узгодженості через межу.
- Міграцію міряли б створеними сервісами, а не знятим обмеженням.
Джерела й метод
На чому стоїть ця стаття
- 14 джерел
- Перевірено 13 жовтня 2026 р.
Практики й походження термінів
strangler fig, патерни виділення і зворотний маневр Конвея- СтаттяMartin Fowler — Strangler Fig (відкривається в новій вкладці)martinfowler.com · 22.08.2024
- СтаттяMartin Fowler — BranchByAbstraction (відкривається в новій вкладці)martinfowler.com · 07.01.2014
- КнигаSam Newman — Monolith to Microservices (відкривається в новій вкладці)O’Reilly · 2019
- КнигаEric Evans — Domain-Driven Design (відкривається в новій вкладці)Addison-Wesley · 2003
- СтаттяMelvin E. Conway — How Do Committees Invent? (відкривається в новій вкладці)Datamation · 04.1968
- СтаттяJonny LeRoy, Matt Simons — Dealing with creaky legacy platformsCutter IT Journal · 12.2010
Кейси
розділ «П’ять кейсів і те, чого в кожному бракувало»- СтаттяJim Gray — A Conversation with Werner Vogels (відкривається в новій вкладці)ACM Queue · 30.06.2006
- СтаттяYury Izrailevsky, Stevan Vlaovic, Ruslan Meshenberg — Completing the Netflix Cloud Migration (відкривається в новій вкладці)Netflix · 12.02.2016
- СтаттяPhil Calçado — How we ended up with microservices (відкривається в новій вкладці)philcalcado.com · 08.09.2015
- СтаттяVicent Martí — Move Fast and Fix Things (відкривається в новій вкладці)The GitHub Blog · 15.12.2015
- СтаттяJesse Toth — Scientist: Measure Twice, Cut Once (відкривається в новій вкладці)The GitHub Blog · 03.02.2016
- СтаттяThomas Maurer — Partitioning GitHub’s relational databases to handle scale (відкривається в новій вкладці)The GitHub Blog · 27.09.2021
- СтаттяKevin Dangoor — Technical choices behind a successful rewrite project (відкривається в новій вкладці)Khan Academy Blog · 09.12.2021
- СтаттяMarta Kosarchyn — Beating the odds: Khan Academy’s successful monolith→services rewrite (відкривається в новій вкладці)Khan Academy Blog · 09.12.2021
Дослідження з Claude. Пошук і звірку першоджерел зроблено з Claude (Anthropic). Відбір, текст і перевірку фактів — редакція Anatomy of IT Product.
Було корисно?
Поділіться з тим, хто працює над тією ж задачею.