ІТ‑індустрія: брехня «переписати з нуля» — чому це провалюється і що працює

Було корисно?

Ви успадковуєте систему, скріплену cron‑джобами, племінними знаннями та схемою бази даних, яка виглядає так, ніби її проектували під час пожежної тривоги. Хтось вимовляє чарівні слова: «Давайте перепишемо її з нуля». Голови кивають. Дорожні карти оновлюються. З’являється новий репозиторій, як чистий зошит першого січня.

Потім настає продакшн. Перепис не знає ваших клієнтів, граничних випадків, експлуатаційних обмежень або «гравітації даних». І ваша ротація on‑call точно не підписувалася на «дві системи, обидві зламані, назавжди».

Брехня: чому «переписати з нуля» здається правильним

Переписи продають надію. Вони пропонують чистий розрив із накопиченим безладом: більше ніяких застарілих фреймворків, ніяких «тимчасових» хаків з 2017 року, ніяких нетестованих модулів, ніякої тієї одної збереженої процедури, до якої всі бояться торкатися. Подача емоційно правильна. Проблема в тому, що продакшн‑системи не працюють на емоціях. Вони працюють на інваріантах.

Перепис «з нуля» зазвичай продають як технічний проєкт. Насправді це організаційна ставка: що ви зможете відновити не лише код, а й поведінку, семантику даних, експлуатаційну позицію та обробку відмов — поки стара система продовжує еволюціонувати під реальним навантаженням клієнтів.

Ось частина, якої люди не згадують у презентації перепису: стара система — це копалини реальних інцидентів. Вона містить рубцеву тканину аварій, спроб шахрайства, дивних клієнтських пристроїв, часткових відмов і регуляторних сюрпризів. Ця рубцева тканина потворна. Вона також цінна.

Переписи ігнорують те, що «вимоги» не в системі тикетів. Вони в графіках продакшну, у нотатках on‑call і у тих тихих припущеннях, які тримають світло ввімкненим. Коли ви переписуєте, ви видаляєте ці припущення — а потім знову їх відкриваєте о 2:13 ранку.

Жарт №1: План перепису — це як купити нову бігову доріжку, щоб стати у формі. Купівля здається продуктивною; біг — це те, де з’являється реальність.

Чому переписи провалюються в продакшні (справжні причини)

1) Паритет функцій — це пастка, а не етап

Команди трактують «паритет функцій» як чек‑ліст. Насправді стара система має не функції, а поведінку. Поведінка включає недокументовані значення за замовчуванням, часові особливості, очікування ідемпотентності, семантику повторів і робочі процеси корекції даних, які відбуваються поза щасливим шляхом.

Коли перепис прагне паритету, він орієнтується на видиму поверхню UI/API і пропускає брудні частини, що важливі: як система поводиться, коли платіжний шлюз таймаутиться, коли бекенд повільний, коли клієнт повторює POST, коли годинники розходяться або коли потрібно перебігати день подій.

2) Дані — це продукт, і дані важкі

Більшість систем — це системи даних, що носять UI. Перепис, який не починається з семантики даних — що означають записи, як вони змінюються з часом, що можна робити з eventual consistency — відхилиться у «нову схему бази, яка приємніша», а потім вріже об реальність під час вирубки.

Міграція даних — це не проект на вихідні. Це тривала вправа на надійність з бекфілами, dual‑write (або change data capture), звірками й планами відкату. Якщо ваш план перепису не включає місяці одночасної роботи обох шляхів даних, ви не плануєте cutover — ви плануєте підкидання монети.

3) Перепис створює організацію зі «split‑brain»

Два кодові бази означають дві пріоритети, дві черги баґів, дві моделі експлуатації й одну спільну базу клієнтів, яка очікує однакового сервісу. Зазвичай старші люди витягують у перепис, лишаючи легасі зменшеним у потужності та зростаючим ризиком. Потім у легасі відбувається інцидент, і графік перепису розкрадають, щоб реагувати. Перепис гальмує. Легасі деградує. Всі програють.

4) Операційна готовність — це не «ми тепер маємо Kubernetes»

Сучасні стекі можуть погіршити ситуацію, коли їх використовують як пропуск на довіру. Заміна одного набору режимів відмов на інший — не прогрес; це просто нові способи дзвонити людям.

Операційна готовність — це: чітко визначені SLO, інструментування, якісні алерти, контрольовані релізи, моделювання ємності, управління залежностями, рунибуки та культура, яка може витримувати постійні зміни. Якщо команда перепису не може добре запускати стару систему, вона не зможе і нову — просто з більш блискучим YAML.

5) Продуктивність — це властивість, що виникає, її не можна запрограмувати в unit‑тестах

Стара система має оптимізації, що з’явилися в бою: кешування на дивних шарах, денормалізовані таблиці, попередньо обчислені агрегати, ретельно підібрані індекси, коалесценція запитів і правила «не робити цього на гарячому шляху». Перепис часто починається чистим, потім під навантаженням з’являються регресії продуктивності. Тоді додають кеші, черги й фон‑завдання, поступово відтворюючи ту саму складність — але без інституційної пам’яті.

6) Нова система правильна в малому і неправильно в великому

Код‑рев’ю ловлять локальні проблеми. Вони не ловлять поведінку системи під частковими відмовами. Переписи провалюються, бо моделюють світ як надійний і консистентний. Продакшн цим не є.

7) Безпека та відповідність не «потім»

Переписи часто відсувають контролі безпеки, журнали аудиту, правила зберігання та принцип найменших привілеїв. Потім виявляється, що «дивні» логування й доступи в легасі були через те, що колись аудитор поставив дуже конкретне питання. Ви або метушитеся, або відкладаєте cutover. Обидва варіанти дорогі.

Факти й історія: індустрія вже була тут раніше

  • 1980–1990‑ті: Великі організації неодноразово намагалися робити переписи на базі CASE‑інструментів головнихфрейм‑систем; багато проектів розвалювалися через обсяг і складність міграції даних.
  • Рік 2000 (Y2K) навчив підприємства жорсткого уроку: замінити все рідко реально; виправлення і триаж на основі ризиків часто перемагають.
  • Епоха «big bang» впроваджень ERP показала закономірність: cutover‑и провалюються, коли бізнес‑процеси не змодельовані під реальні робочі потоки і винятки.
  • Зростання сервісно‑орієнтованої архітектури (SOA) обіцяв модульність; багато проєктів дали розподілені моноліти з більшими затримками й складнішим дебагом.
  • Популярність мікросервісів (середина 2010‑х) збільшила спокусу переписати, але також підвищила витрати на операційну зрілість: трасування, мапування залежностей і локалізація відмов стали обов’язковими.
  • Інструменти change data capture (CDC) дозріли і зробили інкрементальні міграції більш практичними, змінивши економіку на користь не «big‑bang» переписів.
  • Хмарна еластичність зменшила деякі ризики ємності, але внесла нові: «шиліди шуму» (noisy neighbors), квоти сервісів і інциденти через білінг.
  • Observability як дисципліна (метрики, логи, трасування) стала мейнстримом; вона показала, що багато «легасі» аварій насправді були проблемами залежностей і ємності.

Три короткі історії з корпоративних окопів

Коротка історія 1: Інцидент через хибне припущення

Середня SaaS‑компанія переписала свій білінговий сервіс на новій мові, щоб «зробити його підтримуванішим». Команда ретельно попрацювала над unit‑тестами та контрактом API. Вони побудували чисту схему бази і випустили за feature‑флагом.

Хибне припущення було тонким: вони вважали, що всі клієнти ставитимуться до POST /charge як до неідемпотентної операції й ніколи не повторюватимуть автоматично. У легасі тихо реалізували ідемпотентність через токен, який надсилав клієнт, бо років тому мобільний клієнт повторював запити через непевні мережі.

Перепис цього не реалізував. Під час рутинних мережевих коливань між регіонами частина клієнтів повторила платежі. Новий сервіс правильно створив кілька платежів. Підтримка загорілася. Фінанси втрутилися. Інженерів заспамили сторінками за «інцидент коректності даних», що довго боліло навіть коли графіки виключилися.

Виправлення не було «додати ще тестів». Виправлення — вважати ідемпотентність і повтори вимогою першого порядку, задокументувати їх як інваріанти і побудувати інструменти звірки. Також вони додали canary, що симулював повтори і перевіряв стабільність бухгалтерії.

Мораль: якщо ви явно не змоделюєте поведінку клієнта, мережа зробить це за вас.

Коротка історія 2: Оптимізація, що відкотилася

Внутрішня платформа великого підприємства переписала пайплайн звітності, щоб знизити витрати. Вони замінили агрегаційні job‑и на стрімінговий пайплайн і агресивно налаштували батчинг, щоб мінімізувати CPU і зберігання.

Оптимізація виглядала чудово у синтетичних бенчмарках. Продакшн був іншим. Їхній батчинг збільшив end‑to‑end затримку і створив сплески навантаження на downstream‑сервіси. «Дешевий» пайплайн перетворився на генератор thundering herd. Нижчестоячі сервіси ввімкнули rate‑limit. Повтори накопичилися. Внутрішні буфери стрімінгової системи зросли, і логіка backpressure почала скидати повідомлення під тривалим навантаженням.

Тепер система мала дві проблеми замість одної: звіти запізнювалися і деякі були неправильні. Команда витратила тижні на побудову компенсуючих контролів: dead‑letter черги, інструменти для реплею і процес корекції «пізніх даних». Витрати зросли, а не впали, бо операційні витрати теж — просто оплачуються людською увагою.

Вони врешті‑решт послабили батчинг, прийняли вищі постійні витрати на обчислення і встановили суворі SLO для свіжості й коректності. «Оптимізація» оптимізувала неправильну річ: рахунок, а не продукт.

Коротка історія 3: Нудна, але правильна практика, що врятувала ситуацію

Компанія, що модернізувала стек автентифікації, втрималася від спокуси переписати і зробила боляче непоказну річ: побудувала сумісну тест‑систему на основі реального продакшн‑трафіку. Не просто unit‑тести — записані запити, крайові токени і представницькі режими відмов.

Вони розгорнули новий сервіс як shadow‑читач спочатку. Він валідовував токени й обчислював рішення, але не застосовував їх. Протягом тижнів він порівнював свої результати з рішеннями легасі і логував невідповідності з достатнім контекстом для дебагу.

Вони знайшли довгий хвіст дивностей: толерантність до зсуву годинників, старіший алгоритм підпису, який ще використовував один клієнт, і специфічний код помилки, на який покладався партнер. Жодне з цього не було в специфікації. Все це мало значення.

Коли вони нарешті переключили трафік, запуск був майже нудним. On‑call отримав кілька сторінок — здебільшого через надмірну чутливість дашбордів — а потім все стабілізувалося. «Нудна практика» означала ставлення до міграції як до збору доказів, а не як до героїчного стрибка.

Що насправді працює: патерни, що переживають зіткнення з реальністю

Почніть з інваріантів, а не з архітектури

Перш ніж сперечатися про фреймворки, запишіть інваріанти. Речі, що мають залишатися істинними навіть коли залежності відмовляють:

  • Правила ідемпотентності: які операції можна безпечно повторювати і як саме.
  • Обмеження коректності даних: що «не може трапитися» (подвійне списання, від’ємний баланс, втрачені записи аудиту).
  • Бюджети затримки і цілі доступності: що користувач потерпить.
  • Вимоги консистентності: де допустима eventual consistency, а де ні.
  • Вимоги відката: що означає скасувати деплой, міграцію або бекфіл.

Використовуйте патерн strangler fig (і робіть це серйозно)

Патерн strangler fig працює, бо поважає, що продакшн‑системи — живі екосистеми. Ви не замінюєте дерево за один день; ви вирощуєте нову систему навколо нього і поступово переводите відповідальності.

Практично це означає:

  • Поставте шар маршрутизації (API‑gateway, reverse proxy або ingress сервісної сітки) перед старою системою.
  • Переносьте по одній кінцевій точці, одному робочому процесу або одній доменній частці за раз.
  • Тримайте швидкий відкат: миттєво маршрутизувати трафік назад.
  • Використовуйте shadow‑читання та порівняння, коли це можливо.

Надавайте перевагу «заміні за інтерфейсом» над «переписати все»

Коли підсистема справді прогнила, замініть її за стабільним інтерфейсом. Збережіть контракт. Збережіть метрики. Збережіть рунибуки з експлуатації. Змініть внутрішню реалізацію. Це зменшує радіус ураження і перешкоджає командам перебудовувати весь світ лише щоб полагодити одну стіну.

Міграція даних: dual‑write або CDC, плюс звірка

Оберіть отруту обережно:

  • Dual‑write: застосунок пише в старе і нове сховища. Простішe концептуально, складніше зробити коректним при часткових відмовах.
  • CDC: трактуйте стару базу як джерело істини і стриміть зміни в нове сховище. Часто більш надійно, але вимагає уважного порядку і дисципліни еволюції схем.

У будь‑якому разі потрібна звірка: періодичні завдання, що порівнюють підрахунки, контрольні суми і інваріанти між старим і новим. Без звірки ви працюєте на відчуттях.

Побудуйте операційну паритетність перед функціональним паритетом

Операційна паритетність — це здатність запускати, відлагоджувати і відновлювати. Вона включає:

  • Дашборди, що показують насичення, помилки, затримки і здоров’я залежностей.
  • Алертинг, який дієво (pages за симптоми, а не шум).
  • Рунибуки, що передбачають часткові відмови і включають відкати.
  • Навантажувальне тестування, що відповідає формі продакшна, а не тільки обсягу.

Цитата, яку варто приклеїти до монітора

Надія — це не стратегія. — Джеймс Кемерон

Операції не цинічні; вони алергічні до магічного мислення. Плануйте режими відмов, які у вас точно будуть.

Тримайте стару систему здоровою під час міграції

Тут керівництво має дозріти. Якщо ви голодуєте легасі під час побудови заміни, легасі впаде і поглине команду, що робить заміну. Виділіть явну потужність на роботу з надійністю легасі під час міграції. Розглядайте це як зниження ризику, а не «витрачені зусилля».

Жарт №2: Єдина річ гірша за одну крихку систему — це дві крихкі системи, які сперечаються, чия це провина.

Практичні завдання: команди, виводи, що це означає, і що ви вирішуєте

Ось завдання, які ви реально виконуєте, коли хтось каже «перепис вирішить продуктивність/надійність». Ви не сперечаєтеся. Ви вимірюєте. Кожне завдання нижче містить реалістичну команду, типовий вивід, що означає цей вивід і рішення, яке з нього випливає.

1) Визначити насичення CPU проти скарг на затримки

cr0x@server:~$ uptime
 14:22:01 up 37 days,  4:11,  2 users,  load average: 18.42, 17.96, 16.88

Що це означає: Середнє навантаження значно вище за кількість ядер (треба знати cores) — це свідчить про конкуренцію за CPU або накопичення runnable‑черги, можливo також I/O‑wait залежно від навантаження.

Рішення: Не починайте перепис через «повільно». Спочатку визначте, чи ви CPU‑bound, I/O‑bound або lock‑bound. Далі: перевірте розподіл CPU і чергу виконуваних процесів.

2) Перевірити використання CPU по ядрах та iowait

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (prod-app-01)  02/04/2026  _x86_64_  (16 CPU)

22:22:10     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
22:22:11     all   62.11    0.00   11.83   18.44    0.00    0.52    0.00    0.00    0.00    7.10
22:22:11       0   71.00    0.00   12.00   10.00    0.00    0.00    0.00    0.00    0.00    7.00

Що це означає: Високий %iowait вказує, що CPU чекає диск/мережеве сховище. Високий %usr — завантаження обчисленнями. Тут обидва: CPU зайнятий і чекає на I/O.

Рішення: Дослідіть затримки сховища та бази даних перед переписом логіки застосунку. Перепис не змінить ваші диски.

3) Перевірити тиск пам’яті і свопінг

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            62Gi        54Gi       1.1Gi       1.8Gi       6.9Gi       4.2Gi
Swap:          8.0Gi       2.7Gi       5.3Gi

Що це означає: Використання swap‑у — нетривіальне. Якщо система активно свопиться, хвостова затримка різко зросте.

Рішення: Перед переписом вирішіть питання розміру пам’яті, витоків або лімітів контейнерів. Якщо ви не можете запускати поточний сервіс без свопінгу, новий, ймовірно, зробить це теж — просто швидше.

4) Перевірити активний свопінг і major‑faults

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 8  2 2795520 1187328  9120 6852140   0  64  1220  1780 8210 9230 61 11  7 21  0
 7  1 2795584 1169000  9120 6854100   0 128  1100  1650 8030 9012 60 12  8 20  0

Що це означає: so (swap out) вказує на активний свопінг. wa також високий, що узгоджується з I/O‑wait.

Рішення: Розглядайте це як опс‑інцидент, а не як можливість у дорожній карті. Зменшіть пам’ять, виправте «шумних сусідів» або масштабуйте. Перепис цього не вилікує.

5) Знайти головних споживачів CPU

cr0x@server:~$ ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head
  PID COMMAND         %CPU %MEM
 8123 java            345.2 18.4
 9001 redis-server     72.1  3.2
 7442 nginx            38.0  0.8

Що це означає: Один процес, що споживає кілька ядер, може бути очікуваним, але підтвердіть, чи це узгоджується з пропускною здатністю. Якщо CPU високий, а пропускна здатність низька — ви крутитеся в циклі або є contention.

Рішення: Профілюйте гарячий процес і перевірте контенцію потоків. Якщо аргумент перепису «нова мова буде швидшою», вимагайте доказів у вигляді flame‑graph спочатку.

6) Швидко ідентифікувати вузькі місця диска

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (prod-db-01)  02/04/2026  _x86_64_  (16 CPU)

Device            r/s     w/s   rMB/s   wMB/s  await  svctm  %util
nvme0n1         220.0   410.0    35.2    88.1  18.40   0.90  92.50

Що це означає: Високий %util і зростаючий await означають, що пристрій насичений або черга зростає. Низький svctm при високому await вказує на проблему з чергою/латентністю, а не на сиру повільність пристрою.

Рішення: Потрібна оптимізація запитів, зміни індексів або розподіл I/O. Перепис, що зберігає ті самі шаблони доступу, натрапить на ту саму стіну.

7) Перевірити ємність файлової системи та виснаження inode

cr0x@server:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  900G  855G   45G  96% /
cr0x@server:~$ df -i
Filesystem       Inodes   IUsed    IFree IUse% Mounted on
/dev/nvme0n1p2  5900000 5892000     8000  100% /

Що це означає: Майже повний диск — погано; виснаження inode — підступніше і може зламати деплої, логування і тимчасові файли.

Рішення: Зупиніться. Очистіть. Додайте політики зберігання. Якщо ваш перепис «бо деплої падають», а причина — inode, вам не потрібна нова кодова база — потрібне прибирання.

8) Перевірити помилки мережі і повторні передачі

cr0x@server:~$ netstat -s | egrep -i 'retrans|listen|listenoverflows|packet receive errors' | head
    12455 segments retransmitted
    37 packet receive errors

Що це означає: Повторні передачі і помилки прийому можуть давати «випадкові затримки». Ваш застосунок може бути невинним.

Рішення: Дослідіть NIC, невідповідності MTU, перевантажені балансувальники навантаження або проблеми між зонами доступності перед переписом шару застосунку.

9) Інспектувати стани TCP‑з’єднань (витоки або повільні клієнти)

cr0x@server:~$ ss -s
Total: 14021
TCP:   10234 (estab 812, closed 9132, orphaned 5, timewait 7210)

Transport Total     IP        IPv6
RAW       0         0         0
UDP       29        25        4
TCP       1102      1011      91
INET      1131      1036      95
FRAG      0         0         0

Що це означає: Надмірний timewait може вказувати на короткоживучі з’єднання без keep‑alive або агресивну поведінку клієнтів з повтореннями.

Рішення: Налаштуйте повторне використання з’єднань, параметри балансувальника навантаження та поведінку клієнтів. Перепис не змінить фізику TCP.

10) Перевірити тротлінг контейнерів (Kubernetes CPU limits)

cr0x@server:~$ kubectl -n payments top pods | head
NAME                           CPU(cores)   MEMORY(bytes)
payments-api-6d8d6c6b6c-2qz7m  980m         740Mi
payments-api-6d8d6c6b6c-pk9h4  995m         755Mi
cr0x@server:~$ kubectl -n payments describe pod payments-api-6d8d6c6b6c-2qz7m | egrep -i 'Limits|Requests|throttl' -n | head -n 20
118:    Limits:
119:      cpu:     1
120:      memory:  1Gi

Що це означає: Поди, що тримаються на межі ліміту CPU, ймовірно, зазнають тротлінгу, що викликає сплески затримки, які виглядають як «новий сервіс повільніший».

Рішення: Перегляньте CPU limits/requests і політики HPA. Якщо ви переписуєте на Kubernetes без розуміння тротлінгу, ви просто перенесли проблему у YAML.

11) Виявити lock‑контенцію в базі даних (класична сліпота при переписі)

cr0x@server:~$ psql -U postgres -d appdb -c "select pid, wait_event_type, wait_event, state, query from pg_stat_activity where wait_event_type is not null order by pid limit 5;"
 pid  | wait_event_type |   wait_event   | state  |                  query
------+-----------------+----------------+--------+------------------------------------------
 4142 | Lock            | transactionid  | active | UPDATE invoices SET status='paid' ...
 4221 | Lock            | relation       | active | ALTER TABLE ledger ADD COLUMN ...

Що це означає: Запити блокуються через локи. Проблеми з продуктивністю можуть бути через DDL‑міграції, а не через якість коду застосунку.

Рішення: Плануйте важкі міграції, зменшуйте обсяги блокувань, використовуйте онлайн‑зміни схем. Не переписуйте через «Postgres повільний», коли ви тримаєте локи.

12) Перевірити повільні запити і знайти топ‑винуватців

cr0x@server:~$ psql -U postgres -d appdb -c "select calls, mean_exec_time, rows, left(query,120) as q from pg_stat_statements order by mean_exec_time desc limit 5;"
 calls | mean_exec_time | rows | q
-------+----------------+------+------------------------------------------------------------
   412 |         982.14 |   12 | SELECT * FROM orders WHERE customer_id = $1 ORDER BY created_at DESC LIMIT 50
   201 |         744.33 |    1 | SELECT balance FROM accounts WHERE id = $1 FOR UPDATE

Що це означає: У вас є конкретні цілі: додати індекси, змінити форму запиту, зменшити блокування. Зазвичай це дешевше, ніж перепис.

Рішення: Виправте «гарячі» запити спочатку. Якщо ви все ще хочете перепис, принаймні перенесіть уроки про запити, щоб нова система не повторила помилки.

13) Перевірити лаг реплікації перед cutover

cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep -i 'Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running'
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 43

Що це означає: Лаг реплікації означає, що «читання з нової системи» може бути застарілим. Це може порушити очікування користувачів під час міграції.

Рішення: Або прийміть застарілість явно (і спроектуйте для неї), або не переключайте читання, поки лаг не буде стабільно низьким.

14) Виявити зміну частоти помилок під час canary‑релізу

cr0x@server:~$ kubectl -n payments logs deploy/payments-api --since=5m | egrep -c " 5[0-9][0-9] "
27

Що це означає: Зростаюча кількість 5xx після деплою — це канар‑фейл поки не доведено протилежне.

Рішення: Откатуйте швидко, потім відлагоджуйте з трасуванням і перевірками залежностей. Не «протискайте» через реліз, бо дорожня карта перепису цього вимагає.

15) Підтвердити достатню кількість дескрипторів файлів під навантаженням

cr0x@server:~$ ulimit -n
1024
cr0x@server:~$ cat /proc/$(pgrep -n nginx)/limits | egrep -i "open files"
Max open files            1024                 1024                 files

Що це означає: 1024 FD часто замало для навантажених проксі/сервісів. Ви можете отримувати помилки з’єднань, що виглядають як «новий ап flaky».

Рішення: Підніміть ліміти, перевірте налаштування рантайму контейнера і повторно протестуйте. Знову: виправте фундаментальні речі перш ніж перебудовувати всесвіт.

Швидкий план діагностики: що перевіряти перше/друге/третє

Це процедура, коли хтось каже «легасі — вузьке місце» або «перепис буде швидшим». Ви можете виконати це за годину на живому інциденті (акуратно) або в стенді з продакшн‑подібним навантаженням.

Перше: Насичення, помилки чи затримка залежностей?

  • Перевірити рівень помилок (сплески 5xx/4xx, таймаути). Якщо помилки стрибнули, продуктивність може бути симптомом часткової відмови.
  • Перевірити насичення: CPU iowait, disk await, мережеві retransmits, з’єднання DB, пул потоків.
  • Перевірити здоров’я залежностей: БД, кеш, брокер повідомлень, зовнішні API, DNS, термін дії сертифікатів.

Мета: класифікувати проблему: compute‑bound, I/O‑bound, lock‑bound, network‑bound або відмова залежності.

Друге: Знайдіть тісний цикл у шляху запиту

  • Виберіть одну кінцеву точку, що впливає на користувача (найбільший трафік або найвища затримка).
  • Протрейсіть її наскрізь (distributed tracing, якщо є; інакше — кореляційні ID у логах).
  • Виміряйте час у: CPU застосунку, запитах БД, кеші, апстримі, серіалізації, повторних спробах.

Мета: з’ясувати, куди йде час, а не куди здається, що йде.

Третє: Підтвердіть контрольованим експериментом

  • Зробіть одну зміну (індекс, TTL кешу, розмір pool‑у з’єднань, ліміт CPU).
  • Запустіть її як canary на невеликій частці трафіку.
  • Порівняйте: latency percentiles, error rates, метрики насичення.

Мета: рішення на підставі доказів. Якщо пропозиція перепису не витримує такого рівня перевірки, це проект моралі, а не інженерний проєкт.

Поширені помилки: симптоми → корінь → виправлення

«Ми переписали, а затримка погіршилася»

Симптоми: p95/p99 затримки вгору, CPU в нормі, дашборди показують більше мережевих викликів.

Корінь: Ви розклали в процесі виклики на RPC‑ланцюжки без бюджету затримки, перетворивши in‑process виклики в RPC‑ланцюги. Ви побудували розподілений моноліт.

Виправлення: Згорніть балакучі межі, пакетизуйте виклики, введіть локальний кеш і вимагайте бюджети на кожен хоп. Надавайте перевагу грубозернистим API перед «чистими» межами мікросервісів.

«Cutover спрацював, потім з’явився дрейф даних»

Симптоми: Звіти не співпадають, баланси різняться, клієнти бачать неконсистентні стани через дні.

Корінь: Dual‑write без semantics exactly‑once; відсутня звірка; події приходять у різному порядку; різні правила часових зон/округлень.

Виправлення: Реалізуйте завдання звірки та перевірки інваріантів; використовуйте ключі ідемпотентності; визначте авторитетне джерело для полів; за можливості прийміть CDC з гарантіями порядку.

«Нова система стабільна, але on‑call гірший»

Симптоми: Більше алертів, складніший дебаг, більше невідомих невідомих.

Корінь: Відтермінували observability і рунибуки; алерти побудовані на сирих метриках замість сигналів впливу на користувача; немає трасування.

Виправлення: Інструментувати «золоті» сигнали (latency, traffic, errors, saturation). Додати трасування. Переписати алерти під симптоми і пов’язати з SLO.

«Ми не можемо відпустити, бо женемося за паритетом назавжди»

Симптоми: Проєкт перепису тягнеться квартали/роки, бізнес додає фічі в легасі, перепис не наздоганяє.

Корінь: Мислення big‑bang; відсутні інкрементальні cutover‑и; команда перепису ізольована від реальних продуктових пріоритетів.

Виправлення: Патерн strangler з тонкими вертикальними зрізами. Переносьте один робочий процес наскрізь. Заморозьте певні легасі‑фічі або перенаправляйте нові фічі лише в новий шлях.

«Ми замінили схему бази даних і все болить»

Симптоми: Повільні запити, contention по локу, розширення вікон міграцій, ризикові відкати.

Корінь: Перепроект схеми ігнорував шаблони доступу й експлуатаційні обмеження; відсутні індекси; незмірені міграції.

Виправлення: Почніть з профілювання запитів і стратегії індексів. Використовуйте онлайн‑міграції, бекфіли і фазовані обмеження. Тримайте стару схему як адаптер, коли потрібно.

«Ми переписали заради безпеки і ввели нові дірки»

Симптоми: Відсутні журнали аудиту, слабші перевірки авторизації, розростання секретів.

Корінь: Контролі безпеки були імпліцитні в легасі і не змоделовані; новий стек випущено без threat‑modeling.

Виправлення: Інвентаризуйте інваріанти безпеки (правила authz, логування, зберігання). Додайте автоматичні перевірки в CI. Використовуйте принцип найменших привілеїв і централізоване управління секретами з першого дня.

Чек‑листи / покроковий план

Чек‑лист рішення: чи варто взагалі переписувати?

  1. Чи можете назвати вузьке місце? Якщо ні — спочатку вимірювання (див. завдання й план діагностики).
  2. Проблема в підтримуваності коду чи в поведінці системи? Якщо інциденти здебільшого через ємність/залежності, перепис коду не допоможе.
  3. Чи є стабільний контракт? Якщо інтерфейс нестабільний, зафіксуйте його перед зміною інтернів.
  4. Є план щодо даних? Якщо ви не можете описати dual‑write/CDC, звірку й відкат — ви не готові.
  5. Чи маєте опс‑зрілість? Дашборди, алерти, трасування, рунибуки, поетапні релізи. Якщо ні — побудуйте це спочатку.
  6. Чи можете закрити дві системи ресурсами? Якщо ні — робіть поступову заміну, а не паралельні переписи.

Безпечніший план модернізації (працює навіть при обмеженому часі)

  1. Інвентар інваріантів: ідемпотентність, правила коректності, зберігання, авторизація, коди помилок, ліміти швидкості.
  2. Інструментувати легасі, якщо воно сліпе: додайте request‑ID, гістограми затримок, таксономію помилок.
  3. Поставте шар маршрутизації: gateway/proxy, що може розділяти трафік і миттєво відкатувати.
  4. Виберіть один вертикальний зріз: один робочий процес, що дає реальну цінність і зачіпає реальні залежності.
  5. Shadow спочатку: нова система обчислює відповіді і логгує невідповідності, але не віддає їх.
  6. Canary: 1% трафіку, потім 5%, потім 25%, вимірюючи SLO і інваріанти.
  7. Обережно переключайте шляхи читання: застарілі читання видимі користувачу. Використовуйте бюджети консистентності і явну поведінку.
  8. Перемикайте шляхи запису останніми: переконайтеся, що ідемпотентність, повтори і звірка доведені.
  9. Деактивуйте по частинах: видаляйте легасі‑endpoint‑и коли трафік для них сходить до нуля; зберігайте шляхи архівації для аудиту.

Чек‑лист релізу для мігрованого компонента

  • SLO визначений; дашборди показують золоті сигнали.
  • Алертинг налаштований; on‑call має рунибуки і інструкції відкату.
  • Тестовано ємність під навантаженням, що імітує форму продакшна.
  • Таймаути залежностей і повтори налаштовані (з бюджетами).
  • Ідемпотентність реалізована для небезпечних операцій.
  • Завдання звірки даних на місці; процес триажу невідповідностей визначений.
  • Контролі безпеки перевірені: паритет authz, журнали аудиту, зберігання.
  • Проведено game‑day: відмова залежності, повільна БД, частковий деплой, відкат.

Часті питання

1) Коли перепис насправді виправданий?

Коли поточну систему неможливо розвивати безпечно: неподтримуваний рантайм з неможливістю патчів через ризик безпеки, ліцензійні обмеження або архітектура, що блокує критичні бізнес‑вимоги. Навіть тоді надавайте перевагу інкрементальній заміні за стабільними інтерфейсами.

2) Хіба інкрементальна міграція не повільніша за перепис?

Інкрементальна здається повільнішою, бо чесна щодо одночасного існування двох реальностей. Великі переписи здаються швидкими, поки ви не зіштовхнетеся з інтеграцією, даними та операціями — тоді час розростається. Інкрементальна міграція перемагає, бо доставляє цінність раніше і знижує екзистенційний ризик.

3) У нас жахлива якість коду. Хіба це не вимагає перепису?

Погана якість коду вимагає меж, тестів навколо інваріантів і експлуатаційної видимості. Часто можна ізолювати найгірші модулі і замінити їх за інтерфейсом. Повний перепис скидає якість коду до «невідомої», що не обов’язково краще.

4) Як уникнути «дві системи назавжди»?

Міграцією зрізами, які повністю виводять з обігу відповідальність легасі. Не будьте тим, хто будує паралельну систему, що дублює все перед релізом. Маршрутизуйте трафік, переключіть зріз, потім видаліть старий зріз. Видалення — це віхa.

5) Який найбільший прихований ризик у переписах?

Семантичне відхилення: нова система поводиться інакше при повторних спробах, часткових відмовах і дивному вводі. Користувачі не пишуть тикети про «семантичне відхилення». Вони пишуть тикети про зниклі гроші, неправильні дані і «ваш API ненадійний».

6) Чи потребує мікросервісна архітектура перепису?

Ні. Ви можете витягувати сервіси з моноліту поступово. Перший крок часто — створити внутрішні модульні межі і витягти одну доменну частку з чіткою відповідальністю і контрактами даних.

7) Як виконати міграцію даних без простою?

Використовуйте CDC або dual‑write, потім звіряйте. Переключайте читання, коли застарілість прийнятна або її пом’якшено; переключайте записи останніми з сильною ідемпотентністю. Завжди майте маршрут відкату і план бекфілів.

8) Що має вимірювати керівництво, щоб знати, що міграція здорова?

Не story points. Вимірюйте досягнення SLO, частоту інцидентів, частоту відкатів, час до виявлення/відновлення і прогрес міграції у вигляді вилученої легасі‑поверхні (видалені endpoints/робочі процеси).

9) Як зупинити інженерів від «закипання океану»?

Визначте тонкий вертикальний зріз, що доходить до продакшна, і вимагайте, щоб кожне розширення включало план виходу для рівнозначної легасі‑шляху. Заохочуйте видалення й експлуатаційну стабільність, а не новації.

Наступні кроки, які можна відправити цього кварталу

Якщо ви сидите на зустрічі, де хтось пропонує перепис як панацею, ось що робити натомість — практично, без драм:

  1. Запустіть швидкий план діагностики і опублікуйте класифікацію вузького місця. Виведіть дебати з площини естетики.
  2. Запишіть інваріанти (ідемпотентність, коректність, бюджети затримки, правила авторизації). Зробіть їх доступними для рев’ю і тестування.
  3. Виберіть один робочий процес і мігруйте його з маршрутизацією + канарі + відкат. Доведіть, що можете переміщувати зрізи безпечно.
  4. Інвестуйте в операційну паритетність: дашборди, трасування, гігієна алертів, рунибуки. Зробіть підтримку систем простішою, ніж сперечання про них.
  5. Зробіть звірку даних продуктною фічею, а не побічним квестом. Якщо ви не можете довести коректність даних, у вас немає коректності.

Брехня «переписати з нуля» живе доти, доки пропонує історію, де складність зникає. У реальних систем складність не зникає; вона переміщується. Ваше завдання — перемістити її в місця, де її можна виміряти, контролювати і зробити нудною. Нудне недооцінене. Нудне відправляється.

← Попередня
Панель керування NVIDIA відсутня: поверніть її без здогадок
Наступна →
WSL2 + Kubernetes: налаштування, яке не перегріває ваш ноутбук

Залишити коментар