1. Чому релізний процес для ChatGPT App складніший за звичайне розгортання
У попередній частині модуля ми говорили про те, як стежити за якістю та стабільністю App (логи, метрики, SLO). Тепер розберімося, як вибудувати релізний процес так, щоб ці метрики не просідали під час кожного розгортання.
У звичайному вебзастосунку все більш-менш просто: ви розгорнули нову версію бекенду та фронтенду — користувач оновив сторінку й перейшов на нову версію. Якщо щось зламалося, часто достатньо просто відкотити розгортання.
У ChatGPT App стек складніший. Ви маєте щонайменше чотири шари, і кожен живе за своїми правилами:
- Маніфест і схема інструментів (MCP tools / OpenAPI);
- Інфраструктура: Next.js‑застосунок і сервер MCP/Agents/ACP;
- System‑prompt та інші промпти;
- Дані: product feed, налаштування, конфіги.
Проблема в тому, що модель живе у своєму «мисленнєвому світі». Контекст чату може тягнутися годинами й днями. Маніфест і описи tools підвантажуються й кешуються з боку OpenAI та не оновлюються миттєво в усіх активних діалогах. Якщо ви взяли й змінили сигнатуру інструмента (наприклад, видалили поле з input‑schema), то в старих діалогах модель продовжить надсилати старий payload, а ваш оновлений бекенд його відхилятиме. У підсумку — помилки 400, дивні відповіді в чаті й дуже засмучені користувачі.
Тому у світі ChatGPT App «реліз» — це не просто «розгорнути новий Docker». Це скоординована зміна кількох шарів: з акуратним керуванням версіями, feature flags і можливістю відкотитися.
2. Матриця версій: що взагалі версіонувати
Корисно дивитися не лише на «версію App 1.3», а одразу на матрицю версій за шарами. Для GiftGenius це може виглядати приблизно так:
| Шар | Що версіонуємо | Приклад значення | Де зберігати |
|---|---|---|---|
| App / Next.js | Код і збірка | |
package.json, Git‑тег |
| MCP / API інтерфейс | Набір tools, їхні схеми | |
константа в коді, анотації |
| System / prompts | System‑prompt, хелпери, приклади | |
окремі файли й метадані |
| Commerce / ACP / feed | Формат product feed і ACP‑контрактів | |
схема в репозиторії даних |
Зверніть увагу: це різні осі. Ви можете випустити версію 1.4.2 застосунку, у якій schema tools залишиться v1.7, а prompt перейде на v3.2. У логах це має бути видно — так потім легше розібратися, чому після prompt v3.2 раптом просіла конверсія в checkout.
Для коду зручно використовувати семантичне версіонування (SemVer): MAJOR.MINOR.PATCH. MAJOR — несумісна зміна, MINOR — нові можливості без поломок, PATCH — виправлення помилок. Для інтерфейсних версій (schema, prompts) логіка схожа: MAJOR сигналізує про breaking change.
Окремо варто виділити версію інтерфейсу tools. Для LLM це критично важливо: якщо ви змінюєте контракт інструмента, а модель «вважає», що контракт старий, починаються проблеми. Тому політика зазвичай така: у MINOR‑релізах ви лише додаєте нові необовʼязкові поля й не перейменовуєте та не видаляєте старі; несумісні (breaking) зміни — тільки через новий інструмент, наприклад suggest_gifts_v2.
Тепер, коли ми розділили шари за версіями, подивімося, як ці версії загалом «живуть» у релізному циклі — від dev до prod.
3. Базовий релізний потік для GiftGenius
Спершу домовімося про етапи. Найімовірніше, у вас уже є середовища (з модуля про розгортання), але тепер подивімося на них крізь призму релізів.
Зазвичай виділяють:
- dev — локальна розробка, Dev Mode у ChatGPT, тунелі;
- staging — якнайближче до prod: той самий тип бази, той самий тип MCP/ACP, але тестові дані та платежі в sandbox;
- prod — бойове середовище, до якого підʼєднано бойовий ChatGPT App / лістинг у Store.
Процес може виглядати так:
flowchart TD A[Dev: гілка feature/*] --> B[PR → main] B --> C[CI: unit + contract + lint] C --> D[Deploy to Staging] D --> E[Smoke/E2E + ручна перевірка] E --> F[Deploy to Prod] F --> G[Моніторинг за метриками та логами]
На кожному кроці ви додаєте «запобіжники». У dev‑середовищі розробник може робити що завгодно, але кожне злиття в main запускає CI, який проганяє unit/contract‑тести. Якщо все гаразд — автоматично розгортаємо на staging. На staging запускаємо короткий набір E2E/smoke‑сценаріїв: наприклад, один повний флоу підбору подарунка й «умовний» checkout у тестовому платіжному середовищі.
І лише після цього натискаємо кнопку розгортання в prod. Бажано — з позначкою версії та посиланням на CHANGELOG. Уже в prod далі стежимо за p95, error‑rate та бізнес‑метриками (конверсією з рекомендації в checkout). Якщо після релізу щось падає — у нас має бути зрозумілий план відкочування, про який поговоримо нижче.
4. Release notes і changelog: що саме записувати
Якщо у вас немає релізних нотаток, за місяць ви дивитиметеся на графік: «А чому три тижні тому в нас p95 checkout виріс у два рази?» — і відповідатимете собі: «Не знаю, але був великий реліз». Не найкраща стратегія.
Зазвичай є щонайменше два види нотаток.
Внутрішній changelog — технічний. Він потрібен розробникам, SRE і всім, хто працює з кодом. Там пишуть, які можливості зʼявилися, які помилки виправили, чи були несумісні зміни. Формат можна взяти за каноном Keep a Changelog: секції Added, Changed, Fixed, Removed.
Зовнішні release notes — зрозумілі людині нотатки для користувачів і для Store. Там не потрібно писати «migrate MCP SDK 0.4 → 0.5»; краще написати «Додали рекомендації для свята Thanksgiving», «Виправили рідкісну помилку, через яку деякі подарунки не додавалися в кошик».
Приклад фрагмента CHANGELOG.md для GiftGenius:
## [1.4.0] - 2025-11-20
### Added
- Новий tool `suggest_gifts_v2` з підтримкою тегів інтересів.
- A/B‑тест нового system-prompt (flag: GG_PROMPT_V3).
### Changed
- Покращено обробку помилок ACP checkout (дружніші повідомлення).
### Fixed
- Виправлено баг, через який товари без зображення приховувалися з рекомендацій.
Корисно десь поруч зберігати й версію промпта: навіть просто хеш коміту файла з system‑prompt, який потім можна згадати: «Ага, після промпта b3f9c2d користувачі стали рідше натискати «Купити»».
5. Міграції SDK і специфікацій: як жити у швидкозмінній екосистемі
Екосистема навколо Apps SDK, MCP і Agents розвивається швидко: виходять нові версії SDK, змінюється протокол MCP, оновлюється ACP тощо. Це добре (зʼявляються можливості), але водночас болісно: так само швидко щось може й зламатися.
Фіксація версій і «не оновлюйтеся в день релізу»
Перша проста рекомендація: фіксуйте версії залежностей. Не ^0.4.0, а 0.4.0. Для SDK, які часто ламають API (MCP/Agents/Apps SDK), це особливо критично. У дослідженнях на цю тему також описують феномен «SDK fatigue» — втому від постійних несумісних оновлень. Її особливо гостро відчувають ті, хто залишив залежності з плаваючими версіями (на кшталт ^0.4.0) і одного разу раптово отримав купу помилок після npm install.
Міні‑приклад:
// package.json (фрагмент)
{
"dependencies": {
"@modelcontextprotocol/sdk": "0.4.2",
"@openai/applications-sdk": "0.3.1"
}
}
Друга рекомендація: не поєднуйте в одному релізі бізнес‑можливості та великі міграції SDK. Якщо вам потрібно оновити MCP SDK з 0.3.x до 0.4.x, краще зробити окремий технічний реліз: спочатку оновлення SDK, тести й стабілізація, а вже потім — новий checkout‑флоу.
Стратегія міграції SDK
Зазвичай розумний план виглядає так:
У dev‑середовищі ви створюєте окрему гілку upgrade/mcp-sdk-0.4. Оновлюєте залежність, правите код, проганяєте всі unit/contract‑тести, локально проганяєте основний флоу GiftGenius через Dev Mode.
Далі розгортаєте цю гілку на окремий staging‑URL і проганяєте на ньому E2E/smoke‑тести. Можна навіть зробити невеликий навантажувальний тест: 50–100 викликів suggest_gifts поспіль.
Якщо все гаразд — зливаєте в main, розгортаєте на основний staging, ще раз проганяєте smoke, і лише потім — у prod.
Якщо ні — у вас є очевидний шлях назад: просто не зливати цю гілку або відкотити її. У цьому й сенс того, щоб розносити SDK‑міграції та продуктові релізи.
Міграції схем tools і API
Найнеприємніша частина — це інтерфейсні зміни: модель дізнається про них не миттєво. У дослідженнях на цю тему окремо наголошують на важливому правилі: «Розширюй, але не ламай» (additive‑only). Якщо вам потрібно додати в suggest_gifts новий аргумент interests: string[], зробіть його необовʼязковим, а не обовʼязковим — тоді старі сценарії продовжать працювати.
Приклад еволюції Zod‑схеми для вхідних даних:
// v1
const suggestGiftsInputV1 = z.object({
recipientName: z.string(),
budget: z.number()
});
// v1.1 (додали опціональні поля)
const suggestGiftsInputV1_1 = suggestGiftsInputV1.extend({
interests: z.array(z.string()).optional(),
occasion: z.string().optional()
});
Зверніть увагу: ви не чіпаєте наявні поля, а лише розширюєте схему.
Якщо ж потрібно справді зламати контракт (наприклад, замінити budget на minBudget + maxBudget), краще зробити новий інструмент suggest_gifts_v2 і вказати в описі, що це поліпшена версія. Старе API можна залишити як deprecated і поступово вимкнути, коли переконаєтеся, що модель і користувачі переїхали.
Міграції даних: product feed і ACP
Ми вже поговорили про міграції SDK і схем tools. Product feed — це теж контракт. Якщо ви змінюєте формат SKU, валют, локалізацій, це потрібно робити скоординовано: оновити і схему фіда, і обробку в MCP/ACP, і будь‑які препроцесори. У документації з commerce для ChatGPT App зазначають, що помилки у фіді (биті поля, дублікати, дивні ціни) можуть убити якість рекомендацій навіть за ідеального коду.
Типовий підхід:
- Спочатку додаєте нові поля в схему фіда як опціональні й навчаєте GiftGenius їх використовувати, якщо вони є.
- Потім оновлюєте пайплайн, який будує feed, щоб він почав заповнювати ці поля.
- Запускаєте валідатор фіда (contract‑тести на дані) і лише після цього починаєте залежати від цих полів у логіці.
6. Feature flags: відокремлюємо розгортання від релізу
Прапорці функцій (feature flags) — один із головних інструментів виживання у світі ChatGPT App. Основна ідея проста: ви розгортаєте код, але не обовʼязково одразу вмикаєте нову функціональність. Спочатку вона живе «під прапорцем» — увімкнена лише для розробників, або для 1 % користувачів, або взагалі вимкнена.
Це особливо важливо, коли:
- ви викочуєте новий алгоритм рекомендацій;
- ви змінюєте system‑prompt (а це може радикально змінити поведінку моделі);
- ви підʼєднуєте дорогий або повільний інструмент;
- ви експериментуєте з новим checkout‑флоу.
Простий прапорець через змінні середовища
У найпростішому варіанті прапорець функції можна зробити через змінну середовища:
// lib/features.ts
export const isNewRecoEnabled =
process.env.NEXT_PUBLIC_GG_NEW_RECO === "1";
Далі в коді MCP‑інструмента ви можете використати це так:
if (isNewRecoEnabled) {
return runNewRecoAlgorithm(input);
}
return runOldRecoAlgorithm(input);
Плюс такого підходу — простота. Мінус — перемикати прапорці під час роботи складніше: потрібно розгортати нове середовище або щонайменше переініціалізувати застосунок.
Централізований helper із контекстом
Трохи зріліший підхід — мати централізований хелпер, який враховує не лише глобальні прапорці, а й контекст користувача (тенант, сегмент, A/B‑група).
// lib/featureFlags.ts
type Feature = "new-reco" | "checkout-v2";
type Context = { userId: string; tenantId?: string };
export function isFeatureEnabled(
feature: Feature,
ctx: Context
): boolean {
// тут може бути логіка: env, БД, зовнішній сервіс прапорців
if (feature === "new-reco") {
return process.env.GG_NEW_RECO === "1";
}
return false;
}
У MCP‑handler:
const enabled = isFeatureEnabled("new-reco", { userId });
const result = enabled
? await runNewReco(input)
: await runOldReco(input);
Якщо ви колись підʼєднаєте зовнішній сервіс прапорців (LaunchDarkly, Statsig тощо), достатньо буде змінити реалізацію isFeatureEnabled, а не весь код.
Приклади сценаріїв для GiftGenius
A/B‑тест system‑prompt. Ви створюєте PROMPT_V2 і вмикаєте його лише для 10 % користувачів із tenantId у певному списку. При цьому MCP‑інструменти та віджет не змінюються, а ви порівнюєте конверсію.
Kill‑switch для дорогого інструмента. Припустімо, ви зробили інструмент, який виконує складний зовнішній запит (наприклад, до якоїсь ML‑моделі рекомендацій, що коштує дорого). Якщо цей зовнішній сервіс почне падати або різко подорожчає, ви захочете за секунду вимкнути його, не зупиняючи GiftGenius повністю. Feature flag — один із найпростіших способів.
Поступовий rollout нового checkout‑флоу. Checkout v2 ви спочатку вмикаєте лише для співробітників компанії та кількох довірених клієнтів. Якщо метрики в нормі — розширюєте аудиторію, доки не ввімкнете для всіх.
7. Відкочування: як швидко повернутися назад, коли щось пішло не так
Навіть за ідеальних тестів і прапорців щось таки зламається. Важливо, щоб у вас була зрозуміла стратегія: що ви робите в перші 5–15 хвилин після того, як побачили сплеск помилок або просідання метрик.
Швидке відкочування коду
Якщо проблема в суто технічній помилці (NPE, не та змінна, неправильний URL зовнішнього сервісу), зазвичай достатньо відкотити розгортання до попередньої версії. Наприклад, у Vercel можна миттєво повернутися до попереднього розгортання.
Ваше завдання — завжди знати, яке розгортання відповідає якій версії та як його відкотити. В ідеалі це описано в README для чергового (on‑call): «Якщо після релізу 1.4.0 все горить — відкочуєтеся на deployment X».
Другий швидкий важіль — feature flags. Якщо впала лише нова можливість (checkout-v2), простіше вимкнути її прапорцем, ніж одразу відкочувати весь реліз.
Небезпечні зміни: маніфести та схеми
З маніфестами та схемами складніше. Якщо ви залили в Store новий маніфест із неправильною схемою tools і його вже схвалив OpenAI, відкочування може зайняти дні. Причина проста: кожна зміна маніфеста теж проходить через ревʼю OpenAI. В аналітиці різних Store прямо зазначають, що зміни маніфеста й схем — «небезпечні» релізи, які потрібно готувати та тестувати особливо уважно.
Тому краще розділяти:
- безпечні релізи: зміни коду MCP/Next.js, правки промптів, нові можливості, заховані за прапорцями;
- небезпечні релізи: зміни в переліку tools, input/output‑схемах, ACP‑контрактах.
«Небезпечні» релізи варто робити окремо — з додатковими перевірками та, можливо, спочатку лише через Dev Mode та staging‑App (без публікації в Store).
Відкочування даних і фіда
З даними ситуація ще складніша (і болючіша). Якщо ви мігрували product feed на нову схему й водночас видалили старі поля, повернути все назад не завжди тривіально. Тому міграції даних мають бути ідемпотентними й оборотними: або ви зберігаєте стару копію, або робите міграцію у два кроки (спочатку дублюєте дані, потім перемикаєте читання).
Простий підхід — певний час зберігати старі й нові поля паралельно та мати змогу перемикатися між ними через feature flag. Якщо щось падає — ви просто повертаєтеся до старого читання.
8. Міні‑дизайн релізного процесу для GiftGenius
Давайте зберемо все, про що говорили вище (версії, SDK‑міграції, feature flags, відкочування), в один практичний сценарій.
Уявімо, що ви готуєте реліз 1.4.0, у якому:
- додаєте новий інструмент suggest_gifts_v2 з розширеним input;
- вмикаєте новий system‑prompt для частини користувачів;
- оновлюєте MCP SDK з 0.3 → 0.4;
- змінюєте формат product feed (додаєте поле tags).
Розумний план виглядав би так.
Спочатку — окремий технічний реліз 1.3.1: оновлення MCP SDK плюс мінімальні правки коду, без змін схем і можливостей. Проганяєте CI, staging, smoke‑тести. Якщо все стабільно — працюєте з цим кілька днів.
Далі — гілка feature/reco-v2. Там ви додаєте suggest_gifts_v2 як новий інструмент (старий suggest_gifts лишається). Його input‑схема розширюється лише в бік додавання нових опціональних полів. Підготовляєте новий system‑prompt, але ховаєте його за прапорцем GG_PROMPT_V3. В ACP/фіді додаєте нове поле tags як необовʼязкове, а читання будуєте так, щоб за його відсутності все продовжувало працювати.
У CI додаєте кілька нових contract‑тестів: що suggest_gifts_v2 приймає старий і новий payload, що feed з tags валідний, але старі записи без tags теж не ламають сервер.
Після злиття в main:
- CI проганяє unit/contract;
- на staging запускаються 1–2 E2E‑сценарії через новий tool;
- вмикаєте новий prompt і tool лише для тестового тенанта через feature flag.
Стежите за метриками: p95 інструмента, error‑rate, конверсією в checkout. Якщо все гаразд — розширюєте прапорець на більшу кількість користувачів. І лише потім, коли переконалися у стабільності, оновлюєте маніфест для Store (якщо потрібно) та промо‑опис застосунку.
Якщо десь дорогою щось ламається, ви знаєте, як повернутися назад: або вручну вимикаєте прапорець, або відкочуєте розгортання, або, у крайньому разі, відкочуєте версію маніфеста (але це той випадок, якого краще взагалі не допускати).
9. Типові помилки в релізному процесі ChatGPT App
Помилка № 1: одна абстрактна «версія App» замість матриці версій.
Коли у вас є лише «GiftGenius v1.4», але ніде не зафіксовано версії tools‑схеми, промптів і product feed, ви не зможете потім відповісти на запитання: «Після якої саме зміни у нас упав checkout?» Розносіть версії за шарами й логуйте їх у структурованих логах.
Помилка № 2: несумісні зміни в tools без нової назви/версії.
Найболючіша штука: ви взяли й перейменували поле в input‑схемі або видалили його, не змінюючи імʼя інструмента. У старих чатах модель продовжує надсилати старий payload, бекенд повертає 400, GPT починає «галюцинувати» у відповідь, користувачі нічого не розуміють. Будь‑яку несумісну зміну робіть через новий інструмент (foo_v2) або через нову версію API, а старий інтерфейс залишайте на перехідний період.
Помилка № 3: оновлення SDK «заодно» з можливістю.
Класика: ви додаєте нову бізнес‑можливість і водночас оновлюєте @modelcontextprotocol/sdk з 0.3 до 0.5 — «щоб усе було найсвіжіше». У підсумку, якщо щось ламається, незрозуміло, винен новий код, нова схема чи новий SDK. Міграції SDK краще робити окремими технічними релізами — з чітким планом тестів і можливістю відкотитися.
Помилка № 4: відсутність прапорців функцій і миттєвих kill‑switchʼів.
Розгорнути новий алгоритм рекомендацій одразу на 100 % користувачів — це ризиковано, доки він не почне давати дивні результати або не паралізує зовнішній сервіс. Без feature flag ваш єдиний важіль — повний відкат релізу, який може зачепити не лише нову можливість, а й десяток нешкідливих поліпшень. Зробіть хоча б прості прапорці через env або невеликий конфіг.
Помилка № 5: надія на «миттєве» оновлення маніфеста.
Поширене хибне уявлення — думати, що щойно ви змінили tools або openapi.yaml, модель одразу дізнається про нову схему. На практиці маніфест і описи tools кешуються, а в уже відкритих чатах можуть жити довго. Ігнорування цього факту веде до неочевидних помилок: у нових чатах усе працює, у старих — падає. Плануйте зміни схем з урахуванням цієї поведінки й тестуйте їх через Dev Mode та staging перед викочуванням у Store.
Помилка № 6: відсутність зрозумілого плану відкочування та документації щодо релізів.
Якщо у вашій команді ніхто не може відповісти на запитання «Як відкотити реліз за 5 хвилин?», «Яку версію ми відкочуємо?», «Як повернутися до старої схеми фіда?» — вважайте, що плану відкочування у вас немає. Для чергового (on‑call) має бути короткий, але конкретний сценарій: які кнопки натискати, які змінні змінювати і де дивитися, що відкочування спрацювало.
Помилка № 7: «німі» релізи без release notes і привʼязки до метрик.
Викочувати релізи без бодай якогось changelogʼа — означає за кілька місяців жити в режимі ворожіння. Коли p95 раптово виріс, а конверсія впала, ви гадатимете: «А що взагалі тоді змінювалося?» Звичка писати хоча б мінімальні релізні нотатки й привʼязувати їх до дат розгортання та версій полегшує не лише аудит якості, а й життя всієї команди.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ