Середовища, деплой і релізи
Зміст
Між «код замерджено» і «користувач бачить фічу» лежить ціла інженерна дисципліна: код треба зібрати, доставити на середовище, безпечно перемкнути на нього трафік, переконатися, що нічого не впало, — і мати план відкату, якщо впало. Для senior QA це не «не моя зона». Саме тут вирішується, чи ловиться регресія до того, як її побачать усі користувачі, чи smoke після деплою відрізнить «викотилось нормально» від «викотилось, але база не змігрувала», і чи можна вимкнути зламану фічу за секунди без нового релізу.
На співбесіді цю тему питають, щоб перевірити не термінологію, а системне мислення: чому blue-green дає миттєвий відкат, а rolling — ні; чому feature flag і деплой — різні події; як не зіпсувати метрики A/B власними тестовими прогонами. Вага середня, але провал одразу опускає грейд: хто не бачить різниці між «задеплоїли» і «зарелізили», не тягне на senior.
Середовища: dev, staging, prod
Середовище (environment) — це окремий екземпляр застосунку з власною інфраструктурою, базою даних і конфігурацією. Код той самий, а от дані, інтеграції й налаштування — різні. Класична драбина складається щонайменше з трьох сходинок.
| Середовище | Призначення | Дані | Стабільність |
|---|---|---|---|
| dev | Розробник перевіряє свій код | Синтетичні, часто локальні | Нестабільне, ламається постійно |
| staging (stage, pre-prod) | Фінальний гейт перед продом, максимально схожий на прод | Prod-like або анонімізований зліпок | Умовно стабільне |
| prod (production) | Живий застосунок для користувачів | Реальні дані користувачів | Має бути завжди зелене |
Часто драбина довша: окремий QA/test-стенд, UAT (user acceptance testing) для замовника, sandbox для інтеграцій. Але суть однакова — код рухається знизу вгору, і кожна сходинка ловить свій клас проблем.
Ключове поняття для QA — паритет середовищ (environment parity). В ідеалі staging відрізняється від prod лише даними й масштабом. На практиці розходяться версії залежностей, конфіги, фіче-флаги, версії БД, наявність кешу/CDN — і це джерело найпідступніших багів: «на staging працює, на prod падає». Конфігурація під кожне середовище (base URL, ключі інтеграцій, ліміти) живе не в коді, а в змінних середовища й секретах — глава Секрети та конфігурація в CI; prod-подібний стенд локально часто піднімають через Docker.
Окрема дисципліна: на staging/prod з реальними інтеграціями працюють обережно. Тест, який справді списує гроші чи шле листи живим людям, — це інцидент, а не тест. Тому платежі, пошту й сторонні API навіть на prod-подібних середовищах мокають або переводять у sandbox.
Smoke після деплою
Розгортання (deploy) саме по собі нічого не гарантує: артефакт скопіювався, процес перезапустився — і все. Чи піднявся застосунок насправді, чи доступна база, чи не забули змінну середовища — це окрема перевірка. Post-deploy smoke (перевірка розгортання, deployment verification) — це короткий набір перевірок критичного шляху одразу після викату, який відповідає на одне питання: «релізу можна довіряти чи негайно відкочувати?».
Це не та сама регресія, що ганяється на PR. Різниця в цілі й адресаті:
- регресія на PR перевіряє код до злиття, ганяється проти тестового білда;
- post-deploy smoke перевіряє конкретне розгортання на конкретному середовищі — уже задеплоєний застосунок з його реальною конфігурацією, БД і мережею.
Мінімум для smoke: health-ендпоінт відповідає 200, головна сторінка рендериться, логін працює, один-два наскрізні критичні сценарії (для маркетплейсу — «пошук → картка товару → додати в кошик») проходять. Він має бути швидким (хвилини, не години) і стабільним, бо блокує промоушен релізу. Різницю між smoke, sanity й регресією як видами тестування розбирають у розділі «Основи тестування»; тут важливий саме кут «перевірка після деплою».
// Post-deploy smoke: ганяється проти реального URL середовища, не проти білда
test('критичний шлях доступний після деплою', async ({ page, request }) => {
// 1. Інфраструктура жива
const health = await request.get(`${process.env.BASE_URL}/health`);
expect(health.status()).toBe(200);
// 2. Критичний користувацький сценарій працює
await page.goto('/');
await page.getByRole('searchbox').fill('ноутбук');
await page.getByRole('button', { name: 'Знайти' }).click();
await expect(page.getByRole('article').first()).toBeVisible();
});
У зрілому пайплайні smoke — це окрема стадія після deploy-джоба, і її провал автоматично тригерить відкат. Механіка стадій і джобів — у главі Будова пайплайна; стабільність автотестів у CI — у Автотести в CI.
Стратегії розгортання: rolling, blue-green, canary
Коли на проді сотні тисяч користувачів, «зупинити застосунок, підмінити файли, запустити» — не варіант: це простій і ризик. Тому реліз викочують так, щоб мінімізувати ризик і мати шлях назад. Три базові стратегії, які senior QA має розрізняти.
Rolling (поступова заміна). Інстанси застосунку оновлюють по черзі: вимкнули один, підняли на новій версії, перевели трафік, узялися за наступний. Плюс — не потрібна подвійна інфраструктура. Мінус, критичний для QA: під час викату дві версії обслуговують трафік одночасно. Користувач одним запитом може потрапити на стару версію, наступним — на нову. Це вимагає зворотної сумісності: новий код мусить розуміти старі дані/запити, а API — старих клієнтів. Відкат теж поступовий і повільний.
Blue-green (синьо-зелений). Тримають два ідентичні середовища: «синє» (поточне, на бойовому трафіку) і «зелене» (нова версія). Розгортаємо на зелене, ганяємо smoke на ньому без користувачів, а тоді балансувальник перемикає весь трафік із синього на зелене однією операцією. Головна перевага — миттєвий відкат: якщо щось пішло не так, перемикаємо трафік назад на синє. Мінус — потрібна подвійна інфраструктура і акуратність зі спільною базою (обидва кольори ходять в одну БД).
Canary (канарковий реліз). Нову версію отримує спершу маленька частка трафіку (умовно 1–5%), решта лишається на старій. Метрики нової версії — помилки, латентність, бізнес-показники — порівнюють зі старою. Якщо здорові — частку поступово нарощують до 100%; якщо деградація — відкочують, зачепивши мінімум користувачів. Назва — від канарки в шахті. Canary дає найкращий контроль ризику й природно поєднується з автоматичним відкотом за метриками.
Роль QA у всіх трьох — не «пройти клац-тест після викату», а закласти механіку безпеки на етапі планування:
- визначити критерії просування й відкату: які smoke-перевірки й пороги метрик (error rate, p95-латентність) вважаємо здоровими — інакше рішення «промоутити чи ні» ухвалюють на око;
- перевірити зворотну сумісність для rolling/canary: змішані версії не мають ламати одна одну. Класична пастка — несумісна міграція БД; безпечний патерн — expand-contract (спершу додати нове, не ламаючи старе, прибрати старе лише після повного викату). Механіка міграцій — у розділі «Бази даних і SQL»;
- прогнати smoke на «зеленому»/канарці до живого трафіку;
- домовитися про спостережуваність: без метрик і логів canary — це просто повільніший спосіб зламати прод (глава Логи, моніторинг і спостережуваність).
Feature flags: відокремити деплой від релізу
Feature flag (фіче-флаг, feature toggle) — це умовний перемикач у коді, який вмикає чи вимикає гілку функціональності без нового деплою. Головна ідея, яку часто недооцінюють: флаг розриває зв'язок між деплоєм і релізом. Код нової фічі може вже бути на проді (задеплоєний), але вимкнений флагом (не зарелізений). Реліз стає зміною конфігурації, а не викатом коду.
Навіщо це QA і команді:
- Kill switch. Фіча зламалася на проді — вимикаємо флаг за секунди, без rollback і без нового білда. Це найшвидший «відкат», який існує.
- Поступове відкриття. Флаг можна вмикати для когорти: спершу внутрішнім співробітникам, потім 10% користувачів, потім усім. По суті це canary на рівні фічі, а не інстансів.
- Тестування на проді. Фічу можна ввімкнути лише для тестових акаунтів на живому середовищі.
Що це означає для тестування:
- Тест має знати стан флага. Той самий білд поводиться по-різному залежно від флага, тож у налаштуванні тесту стан флага треба задавати явно (через API конфігурації чи cookie/заголовок оверрайду), а не сподіватися на дефолт. Інакше тест «іноді зелений» — класичний флак, тільки причина не в синхронізації, а в конфігурації.
- Комбінаторний вибух. N незалежних флагів — це
2^Nконфігурацій. Усі не перекриєш; покривай релевантні комбінації, а не декартів добуток — та сама логіка, що й у попарному тестуванні з розділу «Тест-дизайн». - Обидві гілки живі. Поки флаг існує, стара й нова поведінка обидві в проді — тестувати треба і
on, іoff. - Flag debt. Фіча давно розкотилася на 100%, а мертвий флаг і його
else-гілка живуть у коді роками. Прибирання відпрацьованих флагів — частина гігієни.
// Стан флага задаємо явно в налаштуванні, а не покладаємось на дефолт середовища
test.describe('новий чекаут', () => {
test.use({ featureFlags: { 'new-checkout': true } });
test('оплата проходить у новому флоу', async ({ page }) => {
// ... перевіряємо гілку on
});
});
A/B-експерименти: варіанти, когорти, чистота метрик
A/B-тест — це не про якість коду, а про продуктову гіпотезу: «нова кнопка збільшить конверсію?». Користувачів випадково ділять на варіанти (variants): контрольний (стара версія) і один чи кілька експериментальних (treatment). Далі порівнюють цільову метрику й вирішують, чи різниця статистично значуща. Технічно варіант часто розводять тим самим фіче-флагом, але ціль інша — не безпека релізу, а вимір ефекту.
Три речі, за які відповідає QA і на яких валяться експерименти:
Розподіл і когорти. Одиниця рандомізації (найчастіше користувач або сесія) має розподілятися випадково й стабільно: один і той самий користувач у межах експерименту завжди бачить той самий варіант (sticky assignment). Якщо варіант «мерехтить» між візитами — досвід зламаний, а дані брудні. Перевір: чи призначення липке; чи фактичний спліт відповідає заявленому (50/50 має бути ~50/50). Різке відхилення від очікуваного співвідношення — sample ratio mismatch (SRM), сигнал, що з експериментом щось не так (баг у сплітері, фільтрація ботів зачепила один варіант), і його результатам довіряти не можна.
Чистота метрик. Метрику дуже легко забруднити. Головні джерела бруду:
- внутрішній трафік — співробітники, QA-прогони, автотести, боти. Якщо твої 500 smoke-прогонів на день падають у той самий бакет, що й реальні користувачі, метрика зсунеться. Внутрішній трафік має виключатися з аналітики або йти в окремий сегмент;
- некоректний трекінг — подія конверсії не спрацювала, спрацювала двічі чи приписалася не тому варіанту. Перевірити, що аналітичні події летять правильно й рівно один раз, — прямий обов'язок QA;
- cross-contamination — користувач побачив обидва варіанти (різні пристрої, скинутий cookie).
Guardrail-метрики. Крім цільової метрики стежать за «захисними»: не зросли помилки, не впала швидкість, не просіла інша воронка. Позитивний ефект на одну метрику ціною регресії деінде — це не перемога.
Головна теза для співбесіди: QA в A/B — це не «клацнув обидва варіанти», а гарант валідності експерименту: коректний і липкий розподіл, чистий трафік, правильний трекінг подій.
Rollback: як відкотитися
Rollback (відкат) — повернення системи до попередньої робочої версії після невдалого релізу. Альтернатива — roll-forward (швидкий фікс новим релізом уперед); вибір залежить від того, що швидше й безпечніше. Способи відкату прямо залежать від того, як ти деплоїш:
- blue-green — перемкнути балансувальник назад на «синє»; майже миттєво;
- canary/feature flag — вимкнути флаг або відвести трафік від канарки; теж секунди, без нового білда;
- rolling — викотити попередню версію тим самим поступовим механізмом; повільно;
- завжди можна redeploy попереднього артефакту — тому артефакти релізів і теги зберігають (див. нижче про S3 і теги).
Найпідступніше в rollback — база даних. Код відкотити легко, дані — ні. Якщо реліз накотив несумісну міграцію (перейменував колонку, видалив поле), відкат коду впаде на новій схемі. Тому міграції проєктують зворотно сумісними: expand-contract дозволяє відкотити код, не чіпаючи БД. Питання «а як ви відкочуєте міграцію БД?» — улюблене на senior-співбесідах: на ньому видно, хто реально релізив.
Роль QA: ще до релізу переконатися, що план відкату існує й перевірений — «відкотимося, якщо що» без відрепетируваної процедури не спрацьовує. Оракулом «чи вже відкотилось / чи та версія» служить маркер версії застосунку (той самий принцип, що й фінгерпринтинг бандлів у главі Кешування).
Семантичне версіонування
Семантичне версіонування (semantic versioning, SemVer) — угода, за якою номер версії несе зміст, а не просто зростає. Формат — MAJOR.MINOR.PATCH (наприклад, 2.4.1), і кожна частина має чітке правило (специфікація SemVer 2.0.0):
- MAJOR — несумісні зміни (breaking changes): те, що зламає наявних споживачів.
1.x.x→2.0.0; - MINOR — нова функціональність, зворотно сумісна.
2.4.x→2.5.0; - PATCH — виправлення багів без зміни контракту.
2.4.1→2.4.2.
Є ще pre-release та build-мітки: 2.5.0-rc.1, 2.5.0-beta+build.42. Версії 0.y.z — фаза початкової розробки, де ламати можна будь-коли.
Чому це важливо QA:
- Версія — це очікування. MAJOR-бамп у залежності — плануєш регрес на breaking changes; PATCH — очікуєш незмінний контракт і фокусуєшся на фіксі. Це прямо годує контрактне й API-тестування (розділ «API-тестування»).
- Діапазони версій. Записи
^2.4.0(дозволяє MINOR/PATCH) чи~2.4.0(лише PATCH) уpackage.jsonозначають, щоnpm installможе підтягнути іншу версію, ніж була вчора, — джерело «нічого не міняв, а зламалось» (механіка діапазонів і lock-файлів — розділ «JavaScript/TypeScript для AQA»). - Реліз-версії зазвичай фіксують git-тегом (Git для AQA: bisect, blame, хуки й теги).
Застереження чесності: SemVer поважають не всі. Багато продуктів релізяться за датою (CalVer, 2026.07), за номером білда чи просто git-хешем. SemVer — конвенція для версійованих контрактів (бібліотеки, API), а не універсальний закон.
Testing in production
Довго вважалося, що тестувати на проді — соромно. Насправді частину проблем інакше не спіймати: обсяг і форму реальних даних, реальний патерн трафіку, поведінку сторонніх інтеграцій, справжню інфраструктуру. Staging завжди трохи бреше, тож зрілі команди свідомо роблять частину перевірок на проді — контрольовано.
Техніки testing in production:
- Canary + feature flags — нова поведінка в проді, але для мікрочастки чи лише тестових акаунтів.
- Synthetic monitoring — автотести критичного шляху, що ганяються проти prod за розкладом і алертять, коли ключовий флоу відвалився. Це постійне продовження post-deploy smoke.
- Shadow / dark traffic (дзеркалювання трафіку) — копію реального трафіку шлють на нову версію паралельно, а її відповіді відкидають: навантажуємо новий код бойовими запитами, не впливаючи на користувача.
- A/B і RUM (real user monitoring) — вимір поведінки й продуктивності на живих користувачах.
Залізні запобіжники (без них це не «testing in production», а «інцидент in production»):
- не псувати реальні дані — тестові акаунти, ізольовані сутності, sandbox для платежів/пошти;
- обмежувати радіус ураження — фіче-флаги й canary, щоб проблема зачепила мінімум людей;
- спостережуваність і швидкий kill switch — бачити наслідки в реальному часі й уміти вимкнути за секунди.
Головна теза: testing in production — це доповнення, а не заміна тестуванню до прода. Воно ловить те, що не відтворюється на staging, але не скасовує smoke, регресію й перевірки на нижчих середовищах.
Хмари й де живуть середовища (оглядово)
Раніше середовища були фізичними серверами в серверній. Сьогодні dev/staging/prod найчастіше живуть у хмарі (AWS, Google Cloud, Azure) або в приватному кластері. QA не мусить бути хмарним інженером, але корисно розуміти, де виконується застосунок і звідки брати артефакти й логи. Оглядово, застосунок виконується як одне з:
- віртуальні машини (AWS EC2 тощо) — «сервер в оренду»;
- контейнери й оркестрація (Kubernetes, ECS) — застосунок пакується в образ і масштабується репліками; звідси й rolling/blue-green/canary як штатні можливості оркестратора;
- керовані сервіси й serverless — інфраструктуру тримає провайдер.
Окремо для QA важливе об'єктне сховище, канонічний приклад — Amazon S3 (Simple Storage Service). Це «нескінченний диск» для файлів, і артефакти зберігають саме там: зібрані білди, а головне для QA — звіти прогонів, скриншоти, відео й трейси впалих тестів, JUnit-XML. Коли нічний прогін впав о третій ночі, ти йдеш не на агента (його вже нема), а за посиланням на артефакт у сховищі. Механіка артефактів — глава Будова пайплайна; ретенція артефактів зазвичай обмежена в часі, тож критичні докази варто зберігати окремо, а не покладатися на дефолт CI.
Типові помилки
- «Задеплоїли = зарелізили». Виглядає як одне, а насправді два: з фіче-флагами код може місяць лежати на проді вимкненим. Плутанина ламає планування релізу й тестування.
- «Пройшло на staging — пройде на prod». Виглядає як гарантія, а насправді паритет середовищ ніколи не ідеальний: інші дані, конфіг, версії, CDN. Це окремий клас багів, а не випадковість.
- «Деплой пройшов, отже все ок». Виглядає як успіх, а насправді deploy лише скопіював артефакт. Без post-deploy smoke ти не знаєш, чи піднявся застосунок і чи змігрувала база.
- «Ми зробимо rollback, якщо що». Виглядає як план, а насправді це наміри. Невідрепетируваний відкат (особливо з міграцією БД) у момент інциденту не спрацьовує.
- «A/B показав ріст конверсії». Виглядає як перемога, а насправді метрику могли забруднити внутрішній трафік, зламаний трекінг чи SRM. Без перевірки чистоти даних цифрам вірити не можна.
Підсумок
- Деплой і реліз — різні події: деплой ставить код на середовище, реліз відкриває його користувачам; фіче-флаги роблять цей розрив явним і керованим.
- Post-deploy smoke перевіряє конкретне розгортання на конкретному середовищі — це не та сама регресія, що на PR, і його провал має тригерити відкат.
- Стратегії розгортання торгують ризиком за інфраструктуру: rolling дешевий, але живе зі змішаними версіями; blue-green дає миттєвий відкат; canary — найкращий контроль ризику за метриками. Роль QA — задати критерії просування/відкату й зворотну сумісність.
- У A/B-експерименті QA відповідає за валідність: липкий і коректний розподіл, чистий від внутрішнього трафіку сегмент, правильний трекінг подій.
- SemVer (
MAJOR.MINOR.PATCH, спец 2.0.0) кодує сумісність: MAJOR — breaking, MINOR — нова сумісна фіча, PATCH — фікс; але поважають його не всюди.
Що питають на співбесіді
- «Чим відрізняються blue-green, canary і rolling? Коли що обереш?» Інтерв'юер дивиться, чи розумієш ти механіку (одночасність версій, спосіб і швидкість відкату), а не завчені означення. Сильна відповідь прив'язує вибір до ризику й наявної інфраструктури.
- «Яка різниця між деплоєм і релізом?» Перевіряють, чи бачиш ти роль фіче-флагів і decoupling. Слабкий кандидат вважає це синонімами.
- «Як тестуєш feature flag? Скільки конфігурацій?» Дивляться, чи згадаєш ти обидві гілки, явне задання стану флага в тесті й комбінаторний вибух (
2^N), а не «поклацаю з увімкненим». - «Як відкотити реліз, який зачепив схему БД?» Класичний фільтр на senior: очікують згадку про зворотно сумісні міграції (expand-contract) і про те, що код відкотити легко, а дані — ні.
- «A/B-тест показав ефект — як переконаєшся, що йому можна вірити?» Шукають розуміння чистоти метрик: виключення внутрішнього трафіку, перевірку трекінгу, SRM, guardrail-метрики.
- «Що таке smoke після деплою і чим він відрізняється від регресії?» Перевіряють, чи розділяєш ти «перевірити код» і «перевірити конкретне розгортання».
- «Поясни
^1.2.0у package.json» — точковий на розуміння SemVer-діапазонів і чому збірка може «поплисти» без змін у коді.
Джерела
- ISTQB Certified Tester Foundation Level (CTFL) v4.0 — базова термінологія й рівні тестування (test levels), контекст тестування.
- Semantic Versioning 2.0.0 — канонічна специфікація SemVer.
- Feature Toggles (Pete Hodgson, martinfowler.com) — таксономія й практика фіче-флагів.
- Blue Green Deployment (Martin Fowler) — механіка blue-green і роль перемикання трафіку.
- Canary Release (Danilo Sato, martinfowler.com) — канарковий реліз і поступове нарощування трафіку.
Що таке середовище і навіщо тримати окремі dev, staging і prod?
Середовище (environment) — це самостійний екземпляр застосунку зі своєю інфраструктурою, базою даних і конфігурацією; код усюди однаковий, а дані, інтеграції й налаштування різні. Драбина потрібна, бо кожна сходинка ловить свій клас проблем: на dev розробник перевіряє сирий код і не боїться його зламати, staging — останній гейт, максимально схожий на бойовий, а prod має бути завжди зеленим для реальних користувачів. Часто драбина довша — окремий QA-стенд, UAT для замовника, sandbox для інтеграцій, — але напрямок один: код рухається знизу вгору. Для QA це означає, що баг, спійманий на staging, коштує на порядки дешевше за той самий баг на prod. Плутати середовища небезпечно: перевірка, що реально знімає гроші чи розсилає пошту справжнім користувачам, перетворюється на аварію, а не на тест.
Що таке паритет середовищ і чому «на staging працює, а на prod падає»?
Паритет середовищ (environment parity) — це ступінь, до якого staging збігається з prod у всьому, крім даних і масштабу. Ідеал недосяжний на практиці: різняться версії залежностей і бази, конфіги, набір увімкнених флагів, присутність кешу чи CDN. Саме ці розбіжності породжують найпідступніший клас багів, коли на передрелізному стенді все зелено, а на бойовому щось відвалюється. Тому «пройшло на staging» — не гарантія, а лише сильний сигнал; частина перевірок усе одно лишається на prod. Для QA практичний висновок: коли баг відтворюється тільки на одному середовищі, перше питання — чим це середовище відрізняється конфігурацією, а не «де в коді помилка».
Яка різниця між деплоєм і релізом?
Це дві різні події, які слабкий кандидат вважає синонімами. Деплой (deploy) — це доставка й запуск коду на середовищі: артефакт скопійовано, процес перезапущено. Реліз (release) — це момент, коли фіча стає доступною користувачам. Розводить їх фіче-флаг: код може місяць лежати на проді задеплоєним, але вимкненим, і тоді реліз — це просто зміна конфігурації, а не новий викат. Плутанина ламає і планування, і тестування: якщо не бачиш цього розриву, ти не розумієш, як фічу можна безпечно тримати в проді до її відкриття або миттєво прибрати без нового білда.
Що таке post-deploy smoke і чим він відрізняється від регресії на PR?
Post-deploy smoke (перевірка розгортання) — це короткий набір перевірок критичного шляху одразу після викату, який відповідає на одне питання: релізу можна довіряти чи негайно відкочувати. Різниця з регресією на PR — в об'єкті й адресаті: регресія перевіряє код до злиття, ганяючись проти тестового білда, а smoke перевіряє конкретне розгортання на конкретному середовищі — уже піднятий застосунок з його реальною конфігурацією, базою й мережею. Деплой сам по собі нічого не гарантує: він лише скопіював артефакт, а чи піднявся процес, чи доступна БД, чи не забули змінну — це окрема перевірка. Smoke має бути швидким (хвилини) і стабільним, бо блокує промоушен релізу. У зрілому пайплайні його провал автоматично тригерить відкат.
Що покласти в мінімальний smoke після деплою?
Мінімум — це перевірка, що інфраструктура жива і критичний шлях працює, а не повне покриття функціоналу. Типовий набір: health-ендпоінт відповідає 200, головна сторінка рендериться, логін проходить, один-два наскрізні критичні сценарії відпрацьовують (для маркетплейсу — «пошук → картка товару → додати в кошик»). Ганяти smoke треба проти реального URL середовища, а не проти білда: тільки так перевіряється саме це розгортання з його конфігурацією. Важлива стриманість: смок роздувати не можна, бо він блокує реліз — усе інше йде в повну регресію. Якщо смок падає, промоутити не можна навіть за «зелену» регресію на PR, бо це різні перевірки.
Чим відрізняються rolling, blue-green і canary, і коли що обрати?
Три стратегії торгують ризиком за інфраструктуру. Rolling оновлює інстанси по черзі — дешево, подвійна інфраструктура не потрібна, але під час викату дві версії обслуговують трафік одночасно, а відкат повільний. Blue-green тримає два ідентичні середовища й перемикає весь трафік із поточного на нове однією операцією балансувальника — головний плюс миттєвий відкат назад, ціна — подвійна інфраструктура. Canary віддає новій версії маленьку частку трафіку (умовно 1–5%), стежить за метриками й нарощує до 100% лише за здорових показників — найкращий контроль ризику, але вимагає зрілої спостережуваності. Сильна відповідь не переказує означення, а прив'язує вибір до ризику й наявної інфраструктури: критичний сервіс із жорсткою вимогою до відкату — blue-green чи canary; проста стабільна система з обмеженими ресурсами — rolling.
Чому rolling вимагає зворотної сумісності?
Бо під час поступової заміни інстансів стара й нова версії якийсь час працюють пліч-о-пліч і обслуговують той самий трафік. Один запит користувача може потрапити на старий інстанс, наступний — на новий, тож новий код мусить розуміти старі дані й запити, а API — старих клієнтів. Класична пастка тут — несумісна міграція БД: якщо новий реліз перейменував чи видалив колонку, старі інстанси, які ще живі, впадуть на зміненій схемі. Безпечний патерн — expand-contract: спершу додати нове, не ламаючи старого, а прибрати старе лише після повного викату. Та сама логіка стосується canary, де версії теж співіснують; blue-green від цього частково рятує, але спільна база все одно вимагає обережності.
Як працює canary і чому він дає найкращий контроль ризику?
Canary (канарковий реліз, назва — від канарки в шахті) спершу пускає нову версію лише на маленьку частку трафіку, а решта користувачів лишається на старій. Метрики нової версії — частота помилок, латентність, бізнес-показники — порівнюють зі старою в реальному часі. Якщо показники здорові, частку поступово нарощують (умовно 5 → 25 → 50 → 100%); якщо видно деградацію, відкочують, зачепивши мінімум людей. Контроль ризику найкращий саме тому, що проблему видно на крихітній вибірці до масового викату, і рішення про просування чи відкат приймається за живими даними. Але без метрик і логів canary перетворюється на повільніший спосіб зламати прод — тому роль QA тут почасти в тому, щоб заздалегідь задати пороги (наприклад, error rate і p95-латентність), за якими викат зупиняється.
Що таке feature flag і як він розриває зв'язок між деплоєм і релізом?
Feature flag (фіче-флаг, feature toggle) — умовний перемикач у коді, який дозволяє вмикати й вимикати шматок функціональності, не викочуючи новий деплой. Головна ідея: код фічі може бути вже на проді, але вимкнений флагом, тож деплой (код на середовищі) і реліз (фіча доступна) стають окремими подіями, і відкриття фічі — це зміна конфігурації. Практична цінність для команди велика: kill switch дозволяє вимкнути зламану фічу за секунди без rollback і білда — це найшвидший відкат, що існує; поступове відкриття по когортах працює як canary на рівні фічі; а тестові акаунти можуть бачити фічу на живому середовищі, поки для решти вона закрита. Тому «задеплоїли» і «зарелізили» — не одне й те саме, і флаг робить цей розрив явним і керованим.
Як тестувати feature flag і чому важливо задавати його стан явно?
Залежно від стану флага один і той самий білд дає різну поведінку, тому в setup тесту цей стан виставляють явно — через API конфігурації, cookie чи заголовок оверрайду, — а не покладаються на дефолт середовища. Інакше тест «іноді зелений»: це той самий флак, тільки причина не в синхронізації, а в конфігурації. Тестувати треба обидві гілки, поки флаг живий: і on, і off присутні в проді одночасно, тож стара й нова поведінка обидві мають бути перевірені. Щодо кількості конфігурацій: N незалежних флагів дають 2^N комбінацій, тож увесь декартів добуток не перекриєш — покривай релевантні комбінації за тією ж логікою, що й попарне тестування. Слабка відповідь звучить як «поклацаю з увімкненим флагом» і пропускає і другу гілку, і явне задання стану.
Що таке flag debt і чому це проблема для тестування?
Flag debt — це мертві фіче-флаги, які лишилися в коді після того, як фіча вже розкотилася на 100% користувачів. Флаг і його else-гілка живуть роками, хоча ніколи більше не вимикаються, і це не косметична проблема. По-перше, кожен живий флаг формально подвоює простір конфігурацій (2^N), а отже й обсяг того, що теоретично треба перевіряти. По-друге, мертва гілка коду вводить в оману: незрозуміло, чи вона ще потрібна, і чи покривати її тестами. Прибирання відпрацьованих флагів — частина інженерної гігієни, і QA має право наполягати на ньому, коли бачить, що флаг давно у стабільному стані. Інакше комбінаторний вибух росте на порожньому місці.
За що відповідає QA в A/B-експерименті і що таке sticky assignment?
A/B-тест перевіряє продуктову гіпотезу, а не якість коду: користувачів випадково ділять на варіанти — контрольний і один чи кілька експериментальних, — і порівнюють цільову метрику. QA тут гарант валідності експерименту, а не «людина, що клацнула обидва варіанти». Ключова річ — sticky assignment (липке призначення): одиниця рандомізації (найчастіше користувач або сесія) має стабільно бачити той самий варіант протягом усього експерименту. Якщо варіант мерехтить між візитами, і досвід зламаний, і дані брудні. Перевіряють три речі: стабільність призначення, відповідність реального розподілу заявленому (обіцяні 50/50 мають на ділі дати приблизно рівні групи) і коректність трекінгу подій — кожна рівно раз. Різке відхилення фактичного розподілу від очікуваного — окремий сигнал тривоги.
Що таке sample ratio mismatch і про що він сигналить?
Sample ratio mismatch (SRM) — це коли фактичне співвідношення користувачів між варіантами A/B помітно відхиляється від заявленого: наприклад, замість очікуваних 50/50 виходить 54/46. Це не дрібниця, а червоний прапор: він означає, що з експериментом щось технічно не так — баг у сплітері, який ділить трафік нерівномірно, або фільтрація ботів чи внутрішнього трафіку зачепила лише один варіант. Головний наслідок: за наявності SRM результатам експерименту довіряти не можна, навіть якщо цільова метрика показує «красивий» ефект, бо самі групи вже незіставні. Тому перевірка спліту — базова гігієна перед тим, як узагалі дивитися на конверсію. Для QA це прямий обов'язок: зловити SRM до того, як команда почне святкувати неіснуючий приріст.
Як відкотити реліз, який зачепив схему БД?
Це класичний senior-фільтр, бо тут видно, хто реально релізив. Код відкотити легко, дані — ні: якщо реліз накотив несумісну міграцію (перейменував колонку, видалив поле), простий відкат коду впаде на вже зміненій схемі. Тому міграції проєктують зворотно сумісними за патерном expand-contract: спершу тільки додаємо нове, не ламаючи старого (expand), код обох версій якийсь час працює з розширеною схемою, і лише після повного викату прибираємо старе (contract). За такого підходу код можна відкотити, не чіпаючи БД. Спосіб відкату залежить і від стратегії деплою: blue-green — перемкнути балансувальник назад, canary чи флаг — вимкнути за секунди, rolling — викотити попередню версію повільним механізмом; завжди лишається redeploy збереженого артефакту. Головне — щоб план відкату існував і був відрепетируваний заздалегідь, бо «відкотимося, якщо що» без перевіреної процедури в момент інциденту не спрацьовує.
Що таке SemVer і що означають його частини?
Семантичне версіонування (semantic versioning, SemVer) — домовленість, за якою кожна цифра версії щось означає, а не просто інкрементується; версія складається з трьох чисел MAJOR.MINOR.PATCH, скажімо 2.4.1. MAJOR-бамп означає несумісні зміни (breaking changes), які зламають наявних споживачів: 1.x.x → 2.0.0. MINOR — нова можливість, що не ламає сумісності: 2.4.x → 2.5.0. PATCH — лагодження без зміни контракту: 2.4.1 → 2.4.2. Є ще pre-release і build-мітки на кшталт 2.5.0-rc.1, а версії 0.y.z — рання фаза, де ламати можна будь-коли. Для QA версія — це очікування: MAJOR у залежності означає планувати регрес на breaking changes, PATCH — очікувати незмінний контракт. Чесне застереження: SemVer поважають не всюди — багато продуктів релізяться за датою (CalVer, 2026.07), номером білда чи git-хешем, тож це конвенція для версійованих контрактів, а не універсальний закон.
Поясни різницю між ^1.2.0 і ~1.2.0 у package.json.
Обидва записи — це діапазони версій, які дозволяють npm install підтягнути не рівно 1.2.0, а сумісну новішу. Карет ^1.2.0 дозволяє будь-яке оновлення, що не міняє MAJOR, тобто MINOR і PATCH: підійде 1.3.0 чи 1.2.5, але не 2.0.0. Тильда ~1.2.0 вужча — дозволяє лише PATCH у межах указаного MINOR: 1.2.5 так, а 1.3.0 уже ні. Практичний наслідок, важливий для QA: через ці діапазони збірка може «поплисти» без жодних змін у твоєму коді — учора підтягнулась одна версія залежності, сьогодні інша, і звідси «нічого не міняв, а зламалось». Саме тому існує lock-файл, який фіксує точні версії; без нього діапазони роблять білд невідтворюваним. Розуміння цього прямо годує діагностику раптових падінь після чистого npm install.
Що таке testing in production і які запобіжники обов'язкові?
Testing in production — це свідома, контрольована перевірка частини сценаріїв на бойовому середовищі, бо деякі проблеми інакше не спіймати: реальний обсяг і форму даних, справжній патерн трафіку, поведінку сторонніх інтеграцій. Staging завжди трохи бреше, тож зрілі команди доповнюють (не заміняють) ним тестування до прода. Техніки: canary й фіче-флаги (нова поведінка лише для мікрочастки чи тестових акаунтів), synthetic monitoring (автотести критичного шляху проти prod за розкладом з алертами — постійне продовження smoke), shadow traffic (копію реальних запитів шлють на нову версію, а відповіді відкидають), A/B і RUM. Без запобіжників це не тестування, а інцидент: не псувати реальні дані (тестові акаунти, sandbox для платежів і пошти), обмежувати радіус ураження (флаги, canary) і мати спостережуваність плюс швидкий kill switch, щоб бачити наслідки й вимкнути за секунди. Головна теза: воно виявляє те, чого не відтворити на staging, проте не відміняє ані smoke, ані регресію на нижчих середовищах.
Три кейси, де тема «середовища й релізи» перетворюється на конкретні рішення QA: як обрати стратегію розгортання під ризик і не проґавити пастку зі змішаними версіями, як написати post-deploy smoke проти реального середовища й тест фіче-флага, що перевіряє обидві гілки, і як перевірити валідність A/B-експерименту, перш ніж вірити його цифрам. Скрізь — що дивитися і чому.
Кейс 1. Вибір стратегії розгортання під конкретний ризик
Стратегію викату обирають не за модою, а за співвідношенням «ризик проти інфраструктури». Таблиця розкладає три базові варіанти за осями, які реально впливають на роботу QA.
| Ось | Rolling | Blue-green | Canary |
|---|---|---|---|
| Одночасність версій | Дві версії разом під час викату | Одна версія в кожен момент | Дві версії разом (стара + мала частка) |
| Швидкість відкату | Повільна, поступова | Миттєва (перемикання балансувальника) | Секунди (відвести трафік) |
| Потреба в інфраструктурі | Мінімальна | Подвійна | Помірна + зріла спостережуваність |
| Що обов'язково перевірити QA | Зворотну сумісність коду й даних | Спільну БД для обох кольорів | Пороги метрик і зворотну сумісність |
Як цим користуватися на практиці. Питання інтерв'юера чи тимліда «яку стратегію візьмемо?» — це насправді питання «який у нас ризик і що ми готові за нього заплатити». Критичний платіжний сервіс, де відкат має бути миттєвим, штовхає до blue-green або canary; проста стабільна внутрішня система з обмеженим бюджетом інфраструктури — до rolling.
Міні-розбір інциденту. Команда викотила rolling-релізом зміну, що перейменувала колонку в БД. Поки нові інстанси піднімалися, старі — ще живі — почали віддавати 500, бо їхній код звертався до колонки, якої вже немає. Це не «випадковість деплою», а прямий наслідок того, що під час rolling дві версії обслуговують трафік разом, а міграцію зробили несумісною. Правильний патерн — expand-contract: спершу тільки додати нову колонку (expand), навчити обидві версії коду з нею жити, викотити код повністю, і лише потім прибрати стару (contract). QA мав спіймати це ще на плануванні, спитавши «чи переживе стара версія цю міграцію під час викату?».
Кейс 2. Playwright: post-deploy smoke і тест фіче-флага в обох гілках
Post-deploy smoke виконують проти живого URL середовища (а не проти зібраного білда), і він відповідає на одне питання: релізу можна довіряти чи відкочувати. Базовий набір — інфраструктура жива плюс один критичний наскрізний сценарій.
import { test, expect } from '@playwright/test';
test('логін і кошик живі після деплою', async ({ page, request }) => {
// 1) інфраструктура відповідає: health на реальному середовищі
const health = await request.get(`${process.env.BASE_URL}/health`);
expect(health.status()).toBe(200);
// 2) один критичний наскрізний сценарій: увійти й відкрити кошик
await page.goto('/login');
await page.getByLabel('Email').fill(process.env.SMOKE_USER!);
await page.getByLabel('Пароль').fill(process.env.SMOKE_PASS!);
await page.getByRole('button', { name: 'Увійти' }).click();
await page.goto('/cart');
await expect(page.getByTestId('cart-page')).toBeVisible();
});
Фіче-флаг вимагає окремої дисципліни: той самий білд поводиться по-різному залежно від стану флага, тож стан треба задавати явно, а перевіряти обидві гілки — і on, і off.
test.describe('чекаут за фіче-флагом', () => {
test.describe('флаг увімкнено', () => {
test.use({ featureFlags: { 'new-checkout': true } });
test('оплата йде новим флоу', async ({ page }) => {
await page.goto('/checkout');
await expect(page.getByTestId('checkout-v2')).toBeVisible();
});
});
test.describe('флаг вимкнено', () => {
test.use({ featureFlags: { 'new-checkout': false } });
test('оплата йде старим флоу', async ({ page }) => {
await page.goto('/checkout');
await expect(page.getByTestId('checkout-v1')).toBeVisible();
});
});
});
Що дивитися і чому:
- Smoke стукає в живий URL, а не в білд.
process.env.BASE_URLвказує на конкретне середовище з його БД, конфігом і мережею. Саме тому smoke ловить те, що зелена регресія на PR пропускає: незмігровану базу, забуту змінну середовища, непіднятий процес. - Стан флага задано явно, а не дефолтом. Якщо покладатися на дефолт середовища, той самий тест дає різний результат на різних стендах — це флак, тільки причина в конфігурації, а не в синхронізації. Явне
featureFlagsробить тест детермінованим. - Перевіряємо обидві гілки, поки флаг живий. Стара й нова поведінка обидві в проді, доки флаг існує, тож
off-гілку не можна лишати без тесту — саме на неї впаде kill switch, коли нову фічу доведеться вимкнути. - N флагів — це
2^Nконфігурацій. Увесь декартів добуток не перекриєш; бери релевантні комбінації за логікою попарного тестування, а мертві флаги (flag debt) вимагай прибирати, щоб простір не роздувався даремно.
Кейс 3. Перевірка валідності A/B перед тим, як вірити цифрам
Продакт каже: «варіант B підняв конверсію на 4%, розкочуємо». Перш ніж радіти, QA має підтвердити, що експерименту взагалі можна вірити. Три перевірки, на яких валяться A/B.
Перша — sample ratio mismatch: чи фактичний спліт відповідає заявленому. Заявлено 50/50, а прийшло помітно інакше — сплітер чи фільтрація зламані, і групи вже незіставні.
test('спліт відповідає заявленому 50/50 (немає SRM)', async ({ request }) => {
const res = await request.get(`${process.env.BASE_URL}/api/experiments/checkout-ab/stats`);
const { control, treatment } = await res.json(); // кількість користувачів у групах
const total = control + treatment;
const share = control / total;
// груба межа для очевидного перекосу; для рішення потрібен статкритерій, це лише сигнал
expect(Math.abs(share - 0.5)).toBeLessThan(0.02);
});
Друга — чистота трафіку: внутрішні прогони не мають падати в ту саму вибірку, що й реальні користувачі. Якщо сотні smoke-прогонів на день трекаються як конверсії, метрика зсунеться.
test('внутрішній трафік виключено з аналітики', async ({ page }) => {
// тестовий акаунт має позначатися так, щоб аналітика його відфільтрувала
const events: Array<Record<string, unknown>> = [];
await page.route('**/analytics/collect', (route) => {
events.push(route.request().postDataJSON());
route.fulfill({ status: 204, body: '' });
});
await loginAsTestUser(page); // тестовий акаунт
await page.goto('/checkout');
await page.getByRole('button', { name: 'Оплатити' }).click();
// подія конверсії має нести ознаку внутрішнього трафіку
const conversion = events.find((e) => e.name === 'purchase_completed');
expect(conversion?.internal).toBe(true);
});
Третя — коректність трекінгу: подія конверсії має спрацювати рівно один раз і приписатися правильному варіанту. Двійне спрацювання чи приписка не тому варіанту фальсифікують результат тихо.
Що дивитися і чому:
- SRM — це стоп-сигнал, а не косметика. Помітний перекіс спліту означає, що з експериментом технічно щось не так (баг сплітера, фільтрація ботів зачепила один варіант). За наявності SRM «красивому» приросту конверсії вірити не можна — групи вже нерівнозначні. Точний вердикт дає статистичний критерій; тест вище лише ловить очевидний перекіс як ранній сигнал.
- Внутрішній трафік забруднює метрику найпростіше. QA-прогони, автотести, боти, співробітники мусять або виключатися, або йти в окремий сегмент. Ознака
internalу події — саме те, за що чіпляється фільтр аналітики. - Липке призначення (sticky assignment) — окрема перевірка. Один користувач має бачити той самий варіант між візитами; якщо варіант мерехтить, досвід зламаний і дані брудні. Це перевіряють стабільністю призначення по одному й тому самому ідентифікатору.
- Guardrail-метрики не забуваємо. Навіть валідний приріст цільової метрики — не перемога, якщо поряд зросли помилки чи просіла швидкість. QA дивиться не лише на «цифру, яку хотіли підняти», а й на захисні показники.
Середовища й паритет
- Можу пояснити, що середовище — це окремий екземпляр застосунку з власною інфраструктурою, БД і конфігурацією, а не інша копія коду.
- Знаю призначення драбини dev → staging → prod і що кожна сходинка ловить свій клас проблем.
- Розумію поняття паритету середовищ і чому staging завжди трохи бреше (версії залежностей, конфіги, флаги, CDN).
Деплой, реліз і smoke
- Можу чітко розвести деплой (код на середовищі) і реліз (фіча доступна користувачам) і пояснити, що їх розводить фіче-флаг.
- Знаю, що post-deploy smoke націлений на конкретне розгортання на живому середовищі, а регресія на PR — на код до злиття проти тестового білда.
- Можу назвати мінімум для smoke: health
200, головна рендериться, логін працює, один-два критичні наскрізні сценарії. - Розумію, чому smoke має бути швидким і стабільним і чому в зрілому пайплайні його провал тригерить автоматичний відкат.
Стратегії розгортання
- Знаю різницю rolling vs blue-green vs canary за трьома осями: одночасність версій, спосіб і швидкість відкату, потреба в інфраструктурі.
- Розумію, чому blue-green дає миттєвий відкат (перемикання балансувальника) ціною подвійної інфраструктури.
- Можу пояснити, чому canary дає найкращий контроль ризику й навіщо йому пороги метрик (error rate, p95-латентність).
- Знаю, що роль QA — задати критерії просування/відкату й перевірити зворотну сумісність (expand-contract), а не «клацнути після викату».
Feature flags і A/B
- Можу пояснити, як фіче-флаг розриває зв'язок деплой/реліз і чому kill switch — найшвидший відкат.
- Знаю, що стан флага в тесті треба задавати явно, і що інакше тест «іноді зелений» — це флак від конфігурації.
- Розумію комбінаторний вибух
2^Nі що покривати треба релевантні комбінації, а не декартів добуток; знаю, що таке flag debt. - Знаю, що таке SRM (sample ratio mismatch) і чому за нього результатам експерименту вірити не можна; можу назвати guardrail-метрики.
Rollback і версіонування
- Можу пояснити, чому код відкотити легко, а дані ні, і як expand-contract дозволяє відкотити код без зміни схеми БД.
- Можу розшифрувати SemVer
MAJOR.MINOR.PATCH: MAJOR — breaking, MINOR — нова сумісна фіча, PATCH — фікс. - Розумію
^1.2.0vs~1.2.0і чому діапазони версій дають «нічого не міняв, а зламалось»; знаю роль lock-файлу.
Testing in production
- Можу назвати техніки testing in production: canary + флаги, synthetic monitoring, shadow traffic, A/B і RUM.
- Знаю три залізні запобіжники: не псувати реальні дані, обмежувати радіус ураження, мати спостережуваність і швидкий kill switch.
Яка різниця між деплоєм і релізом?
Питання
Що таке середовище (environment)?