CodeGym /Kursy /ChatGPT Apps /Proces release — wersje, notatki, migracje SDK/spec, feat...

Proces release — wersje, notatki, migracje SDK/spec, feature flags, rollback

ChatGPT Apps
Poziom 17 , Lekcja 3
Dostępny

1. Dlaczego proces release dla ChatGPT App jest trudniejszy niż zwykły deploy

W poprzedniej części modułu mówiliśmy o obserwowaniu jakości i stabilności aplikacji (logi, metryki, SLO). Teraz zobaczmy, jak ułożyć sam proces release tak, aby te metryki nie sypały się przy każdym deployu.

W zwykłej aplikacji webowej wszystko jest w miarę proste: wdrażacie nową wersję backendu i frontendu — użytkownik odświeża stronę i działa na nowej wersji. Jeśli coś się zepsuło, często wystarczy po prostu cofnąć deploy.

W ChatGPT App stos jest sprytniejszy. Macie co najmniej cztery warstwy, które żyją własnym życiem:

  • Manifest i schemat narzędzi (MCP tools / OpenAPI);
  • Infrastruktura: aplikacja Next.js oraz serwer MCP/Agents/ACP;
  • System‑prompt i inne prompty;
  • Dane: product feed, ustawienia, konfiguracje.

Problem w tym, że model żyje we własnym „mentalnym świecie”. Kontekst czatu może ciągnąć się godzinami i dniami. Manifest i opisy tools są ładowane i cache’owane po stronie OpenAI i nie aktualizują się natychmiast we wszystkich istniejących dialogach. Jeśli ot tak zmienicie sygnaturę narzędzia (np. usuniecie pole z input‑schema), to w starych dialogach model nadal będzie wysyłał stary payload, a wasz nowy backend będzie go odrzucał. Efekt — błędy 400, dziwne odpowiedzi w czacie i bardzo smutni użytkownicy.

Dlatego w świecie ChatGPT App „release” to nie tylko „zdeployować nowy Docker”. To skoordynowana zmiana kilku warstw, z uważnym zarządzaniem wersjami, feature flagami i możliwością szybkiego wycofania.

2. Macierz wersji: co w ogóle wersjonować

Warto patrzeć nie tylko na „wersję aplikacji 1.3”, ale od razu na macierz wersji według warstw. Dla GiftGenius wygląda to mniej więcej tak.

Warstwa Co wersjonujemy Przykładowa wartość Gdzie przechowywać
App / Next.js Kod i build
giftgenius-app 1.4.2
package.json, tag w Git
MCP / interfejs API Zestaw tools i ich schematy
tools-schema v1.7
stała w kodzie, adnotacje
System / prompts System‑prompt, helpery, przykłady
prompt v3.1
oddzielne pliki + metadane
Commerce / ACP / feed Format product feed i kontrakty ACP
feed v2, acp v1.3
schemat w repozytorium danych

Zwróć uwagę: to różne osie. Możesz wydać wersję aplikacji 1.4.2, w której schema tools pozostanie na v1.7, a prompt przejdzie na v3.2. Powinno to być widoczne w logach; wtedy łatwiej dociec, dlaczego po prompt v3.2 nagle spadła konwersja checkout.

Dla kodu wygodnie stosować wersjonowanie semantyczne (SemVer): MAJOR.MINOR.PATCH. MAJOR — zmiana łamiąca zgodność, MINOR — nowe funkcje bez łamania zgodności, PATCH — poprawki błędów. Dla wersji interfejsowych (schema, prompts) logika jest podobna: MAJOR sygnalizuje breaking‑change.

Warto osobno wyróżnić wersję interfejsu tools. Dla LLM to krytycznie ważne: jeśli zmieniasz kontrakt narzędzia, a model „myśli”, że kontrakt jest stary, zaczyna się zabawa. Polityka jest zwykle taka: w wydaniach MINOR dodajemy tylko nowe opcjonalne pola i nie zmieniamy nazw/nie usuwamy starych; zmiany łamiące zgodność — tylko przez nowe narzędzie, np. suggest_gifts_v2.

Teraz, gdy rozdzieliliśmy warstwy według wersji, zobaczmy, jak te wersje „żyją” w cyklu release — od dev do prod.

3. Podstawowy flow release dla GiftGenius

Najpierw ustalmy etapy. Najpewniej macie już środowiska (z modułu o deployu), ale teraz spojrzymy na nie przez pryzmat wydań.

Zwykle wyróżnia się:

  • dev — lokalny rozwój, Dev Mode w ChatGPT, tunele;
  • staging — maksymalnie podobne do prod: ten sam typ bazy, ten sam typ MCP/ACP, ale dane testowe i płatności w sandboxie;
  • prod — środowisko produkcyjne, to, do którego podłączona jest produkcyjna ChatGPT App / listing w Store.

Proces może wyglądać tak:

flowchart TD
  A[Dev: gałąź feature/*] --> B[PR → main]
  B --> C[CI: unit + contract + lint]
  C --> D[Deploy to Staging]
  D --> E[Smoke/E2E + ręczna weryfikacja]
  E --> F[Deploy to Prod]
  F --> G[Monitoring metryk i logów]

Na każdym kroku dodajemy „bezpieczniki”. W środowisku dev deweloper może robić, co chce, ale każde zmergowanie do main wyzwala CI, które uruchamia testy unit/contract. Jeśli wszystko jest OK — automatycznie wdrażamy na staging. Na stagingu uruchamiamy krótki zestaw scenariuszy E2E/smoke: np. jeden pełny flow doboru prezentu i „fałszywy” checkout w testowej bramce płatności.

I dopiero po tym wciskamy przycisk deploy na prod. Najlepiej — z oznaczeniem wersji i linkiem do CHANGELOG. Już na produkcji nadal obserwujemy p95, error‑rate i metryki biznesowe (konwersja z rekomendacji do checkoutu). Jeśli po release coś spada — musimy mieć jasny plan rollback, o którym poniżej.

4. Release notes i changelog: co dokładnie zapisywać

Jeśli nie macie release notes, za miesiąc spojrzycie na wykres: „dlaczego 3 tygodnie temu p95 checkout wzrosło 2 razy?” i odpowiecie sobie „nie mam pojęcia, ale mieliśmy duże wydanie”. Słaba strategia.

Zwykle są co najmniej dwa rodzaje notatek.

Wewnętrzny changelog — techniczny. Potrzebny deweloperom, SRE i wszystkim, którzy grzebią w kodzie. Zapisuje się tam, jakie funkcje doszły, jakie błędy poprawiono, czy były breaking changes. Format można wziąć z Keep a Changelog: sekcje Added, Changed, Fixed, Removed.

Zewnętrzne release notes — notatki zrozumiałe dla użytkowników i dla Store. Nie trzeba pisać „migrate MCP SDK 0.40.5”; lepiej napisać „Dodaliśmy rekomendacje na święto Thanksgiving”, „Naprawiliśmy rzadki błąd, przez który niektóre prezenty nie dodawały się do koszyka”.

Przykładowy fragment CHANGELOG.md dla GiftGenius:

## [1.4.0] - 2025-11-20
### Added
- Nowe narzędzie `suggest_gifts_v2` z obsługą tagów zainteresowań.
- Test A/B nowego system-prompt (flaga: GG_PROMPT_V3).

### Changed
- Ulepszono obsługę błędów ACP checkout (bardziej przyjazne komunikaty).

### Fixed
- Naprawiono błąd, przez który produkty bez obrazka były ukrywane w rekomendacjach.

Dobrze trzymać gdzieś obok także wersję prompta: choćby hash commita pliku z system‑prompt, który potem pozwoli sobie przypomnieć: „aha, po promcie b3f9c2d użytkownicy rzadziej klikali „Kup””.

5. Migracje SDK i specyfikacji: jak żyć w szybko zmieniającym się ekosystemie

Ekosystem wokół Apps SDK, MCP i Agents rozwija się szybko: nowe wersje SDK, zmiany protokołu MCP, aktualizacje ACP itd. To dobrze (pojawiają się możliwości), ale i boleśnie: łatwo też coś złamać.

Pinning wersji i „nie aktualizujemy w dniu release’u”

Pierwsza prosta rekomendacja: przypinajcie wersje zależności. Nie ^0.4.0, tylko 0.4.0. Dla SDK, które często łamią API (MCP/Agents/Apps SDK), to szczególnie krytyczne. W badaniach na ten temat osobno wskazuje się zjawisko „SDK fatigue” — zmęczenia ciągłymi niekompatybilnymi aktualizacjami, które szczególnie dotyka tych, którzy zostawili zależności z pływającymi wersjami (np. ^0.4.0) i pewnego dnia złapali masę błędów po npm install.

Mini‑przykład:

// package.json (fragment)
{
  "dependencies": {
    "@modelcontextprotocol/sdk": "0.4.2",
    "@openai/applications-sdk": "0.3.1"
  }
}

Druga rekomendacja: nie łączcie w jednym wydaniu zarówno funkcji biznesowych, jak i dużych migracji SDK. Jeśli trzeba zaktualizować MCP SDK z 0.3.x do 0.4.x, zróbcie oddzielny techniczny release: najpierw aktualizacja SDK, testy i stabilizacja, a dopiero potem — nowy flow checkout.

Strategia migracji SDK

Zwykle rozsądny plan wygląda tak:

W środowisku dev tworzycie osobną gałąź upgrade/mcp-sdk-0.4. Aktualizujecie zależność, poprawiacie kod, odpalacie wszystkie testy unit/contract, lokalnie uruchamiacie główny flow GiftGenius przez Dev Mode.

Następnie wdrażacie tę gałąź na osobny staging‑URL i odpalacie na nim testy E2E/smoke. Można nawet zrobić mały test obciążeniowy: 50100 wywołań suggest_gifts pod rząd.

Jeśli wszystko jest OK — mergujecie do main, wdrażacie na główny staging, jeszcze raz uruchamiacie smoke i dopiero potem — na produkcję.

Jeśli nie — macie oczywisty rollback: po prostu nie mergować tej gałęzi albo ją cofnąć. Na tym polega sens rozdzielenia migracji SDK i wydań produktowych.

Migracje schematów tools i API

Najbardziej nieprzyjemna część to zmiany interfejsowe: model nie dowiaduje się o nich natychmiast. Badania na ten temat szczególnie podkreślają ważną zasadę: „Rozszerzaj, nie łam” (additive‑only). Jeśli trzeba dodać do suggest_gifts nowy argument interests: string[], zróbcie go opcjonalnym, a nie obowiązkowym; stare scenariusze nadal będą działać.

Przykład ewolucji schematu Zod dla danych wejściowych:

// v1
const suggestGiftsInputV1 = z.object({
  recipientName: z.string(),
  budget: z.number()
});

// v1.1 (dodano opcjonalne pola)
const suggestGiftsInputV1_1 = suggestGiftsInputV1.extend({
  interests: z.array(z.string()).optional(),
  occasion: z.string().optional()
});

Zwróć uwagę: nie dotykamy istniejących pól, tylko rozszerzamy.

Jeśli natomiast naprawdę trzeba złamać kontrakt (np. zastąpić budget przez minBudget + maxBudget), lepiej dodać nowe narzędzie suggest_gifts_v2 i w opisie wskazać, że to ulepszona wersja. Stare API można zostawić jako deprecated i stopniowo je wyłączyć, gdy upewnicie się, że model i użytkownicy przeszli na nowe.

Migracje danych: product feed i ACP

Omówiliśmy już migracje SDK i schematów tools. Product feed to też kontrakt. Jeśli zmieniacie format SKU, walut, lokalizacji, trzeba to robić skoordynowanie: zaktualizować i schemat feedu, i obsługę w MCP/ACP, i wszelkie preprocesory. W dokumentacji commerce dla ChatGPT App podkreśla się, że błędy w feedzie (uszkodzone pola, duplikaty, dziwne ceny) mogą zabić jakość rekomendacji nawet przy idealnym kodzie.

Typowe podejście:

  1. Najpierw dodajecie nowe pola do schematu feedu jako opcjonalne i uczycie GiftGenius z nich korzystać, jeśli są.
  2. Potem aktualizujecie pipeline, który buduje feed, aby zaczął wypełniać te pola.
  3. Uruchamiacie walidator feedu (testy kontraktowe na dane) i dopiero wtedy zaczynacie zależeć od tych pól w logice.

6. Feature flags: rozdzielamy deploy od release

Feature flagi to jeden z głównych narzędzi przetrwania w świecie ChatGPT App. Główna idea jest prosta: wdrażacie kod, ale niekoniecznie od razu włączacie nową funkcjonalność. Najpierw żyje „pod flagą” — włączona tylko dla deweloperów, albo dla 1 % użytkowników, albo w ogóle wyłączona.

To szczególnie ważne, gdy:

  • wypuszczacie nowy algorytm rekomendacji;
  • zmieniacie system‑prompt (a to może radykalnie zmienić zachowanie modelu);
  • podłączacie drogie lub wolne narzędzie;
  • eksperymentujecie z nowym flow checkout.

Prosta flaga przez zmienne środowiskowe

W wersji minimalnej feature flag można zrobić przez zmienną środowiskową:

// lib/features.ts
export const isNewRecoEnabled =
  process.env.NEXT_PUBLIC_GG_NEW_RECO === "1";

Potem w kodzie narzędzia MCP można użyć tego tak:

if (isNewRecoEnabled) {
  return runNewRecoAlgorithm(input);
}
return runOldRecoAlgorithm(input);

Plus takiego podejścia — prostota. Minus — przełączanie flag w runtime jest trudniejsze: trzeba wdrażać nowe środowisko albo przynajmniej przeinicjalizować.

Scentralizowany helper z kontekstem

Nieco dojrzalsze podejście — mieć scentralizowanego helpera, który uwzględnia nie tylko globalne flagi, ale i kontekst użytkownika (tenant, segment, grupa 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 {
  // tu może być logika: env, baza danych, zewnętrzny serwis flag
  if (feature === "new-reco") {
    return process.env.GG_NEW_RECO === "1";
  }
  return false;
}

W handlerze MCP:

const enabled = isFeatureEnabled("new-reco", { userId });
const result = enabled
  ? await runNewReco(input)
  : await runOldReco(input);

Jeśli kiedyś podłączycie zewnętrzny serwis flag (LaunchDarkly, Statsig itd.), wystarczy zmienić implementację isFeatureEnabled, a nie cały kod.

Przykładowe scenariusze dla GiftGenius

Test A/B system‑prompt. Tworzycie PROMPT_V2 i włączacie go tylko dla 10 % użytkowników z tenantId na określonej liście. Przy tym narzędzia MCP i widget się nie zmieniają, a wy porównujecie konwersję.

Kill‑switch dla drogiego narzędzia. Załóżmy, że zrobiliście narzędzie, które wykonuje złożone zewnętrzne zapytanie (np. do jakiegoś modelu ML rekomendacji, który jest drogi). Jeśli ten zewnętrzny serwis zacznie padać albo nagle podrożeje, chcecie w sekundę go wyłączyć, nie zatrzymując całego GiftGenius. Feature flag — najprostszy sposób.

Stopniowy rollout nowego flow checkout. Checkout v2 najpierw włączacie tylko dla pracowników firmy i kilku zaufanych klientów. Jeśli metryki są OK — rozszerzacie audytorium, aż włączycie dla wszystkich.

7. Rollback: jak szybko się cofać, gdy wszystko płonie

Nawet przy idealnych testach i flagach coś się zepsuje. Ważne, by mieć jasną strategię: co robicie w pierwsze 515 minut po zauważeniu skoku błędów lub spadku metryk.

Szybki rollback kodu

Jeśli problem to czysto techniczny błąd (NPE, nie ta zmienna, zły URL usługi zewnętrznej), zwykle wystarczy cofnąć deploy do poprzedniej wersji. Na Vercel jest natychmiastowy rollback do poprzedniego deploymentu.

Wasze zadanie — zawsze wiedzieć, który deployment odpowiada której wersji i jak go cofnąć. W idealnym świecie jest to opisane w README dla on‑call: „jeśli po release 1.4.0 wszystko płonie, cofnij do deploymentu X”.

Druga dźwignia — feature flagi. Jeśli padła tylko nowa funkcja (checkout-v2), prościej wyłączyć ją flagą, niż od razu cofać całe wydanie.

Niebezpieczne zmiany: manifesty i schematy

Z manifestami i schematami jest trudniej. Jeśli wrzuciliście do Store nowy manifest z błędnym schematem tools i OpenAI już go zatwierdziło, rollback może zająć dni. Powód jest prosty: każda zmiana manifestu też przechodzi przegląd OpenAI. W analizach różnych Store wprost mówi się, że zmiany manifestu i schematów to „niebezpieczne” wydania, które trzeba przygotowywać i testować wyjątkowo uważnie.

Dlatego lepiej rozdzielać:

  • bezpieczne wydania: zmiany kodu MCP/Next.js, poprawki promptów, nowe funkcje schowane za flagami;
  • niebezpieczne wydania: zmiany w liście tools, schematach input/output, kontraktach ACP.

„Niebezpieczne” wydania warto wdrażać osobno, z dodatkowymi sprawdzeniami i, być może, najpierw tylko przez Dev Mode i stagingową aplikację (bez publikacji w Store).

Rollback danych i feedu

Z danymi jest jeszcze ciężej (i boleśniej). Jeśli zrobiliście migrację product feedu na nowy schemat i po drodze usunęliście stare pola, powrót nie zawsze jest trywialny. Dlatego migracje danych muszą być idempotentne i odwracalne: albo zachowujecie starą kopię, albo robicie migrację w dwóch krokach (najpierw duplikujecie dane, potem przełączacie odczyt).

Proste podejście — przez jakiś czas trzymać stare i nowe pola równolegle i dać sobie możliwość przełączania się między nimi przez feature flag. Jeśli coś pada — po prostu wracacie do starego odczytu.

8. Mini‑projekt procesu release dla GiftGenius

Zbierzmy wszystko, o czym była mowa (wersje, migracje SDK, feature flagi, rollback), w jeden praktyczny scenariusz.

Załóżmy, że przygotowujecie wydanie 1.4.0, w którym:

  • dodajecie nowe narzędzie suggest_gifts_v2 z rozszerzonym wejściem;
  • włączacie nowy system‑prompt dla części użytkowników;
  • aktualizujecie MCP SDK z 0.30.4;
  • zmieniacie format product feed (dodajecie pole tags).

Rozsądny plan wyglądałby tak.

Najpierw oddzielny techniczny release 1.3.1: aktualizacja MCP SDK + minimalne poprawki kodu, bez zmian schematów i funkcji. Odpalacie CI, staging, smoke‑testy. Jeśli wszystko stabilne — żyjecie z tym parę dni.

Potem gałąź feature/reco-v2. Tam dodajecie suggest_gifts_v2 jako nowe narzędzie (stare suggest_gifts zostaje). Jego schema wejściowa rozszerza się tylko przez dodanie nowych opcjonalnych pól. Przygotowujecie nowy system‑prompt, ale owijacie go flagą GG_PROMPT_V3. W ACP/feedzie dodajecie nowe pole tags jako nieobowiązkowe, a odczyt robicie tak, by przy jego braku wszystko nadal działało.

W CI dodajecie kilka nowych testów kontraktowych: że suggest_gifts_v2 przyjmuje stary i nowy payload, że feed z tags jest poprawny, ale stare rekordy bez tags też nie wywracają serwera.

Po merge do main:

  • CI uruchamia unit/contract;
  • na stagingu startują 12 scenariusze E2E przez nowe narzędzie;
  • włączacie nowy prompt i tool tylko dla testowego tenanta przez feature flag.

Obserwujecie metryki: p95 narzędzia, error‑rate, konwersję do checkoutu. Jeśli wszystko OK — rozszerzacie flagę na większą grupę użytkowników. I dopiero potem, gdy upewnicie się w stabilności, aktualizujecie manifest dla Store (jeśli trzeba) i opis promocyjny aplikacji.

Jeśli po drodze coś się łamie — wiecie, jak się cofnąć: albo ręcznie wyłączacie flagę, albo cofacie deployment, albo, w skrajnym przypadku, przywracacie poprzednią wersję manifestu (ale to przypadek, którego lepiej w ogóle nie dopuszczać).

9. Typowe błędy w procesie release ChatGPT App

Błąd nr 1: jedna abstrakcyjna „wersja App” zamiast macierzy wersji.
Gdy macie tylko „GiftGenius v1.4”, ale nigdzie nie utrwalono wersji schematu tools, promptów i product feed, nie odpowiecie potem na pytanie: „po jakiej dokładnie zmianie spadł nam checkout?” Rozdzielajcie wersje według warstw i logujcie je w logach ustrukturyzowanych.

Błąd nr 2: zmiany łamiące zgodność w tools bez nowej nazwy/wersji.
Najbardziej bolesna rzecz: ot tak zmieniliście nazwę pola w schemacie wejściowym albo je usunęliście, nie zmieniając nazwy narzędzia. W starych czatach model nadal wysyła stary payload, backend zwraca 400, GPT zaczyna „halucynować” w odpowiedzi, użytkownicy nic nie rozumieją. Każdą zmianę łamiącą zgodność róbcie przez nowe narzędzie (foo_v2) albo nową wersję API, a stary interfejs zostawcie na okres przejściowy.

Błąd nr 3: aktualizacja SDK „po drodze” do funkcji.
Klasyka: dodajecie nową funkcję biznesową i przy okazji aktualizujecie @modelcontextprotocol/sdk z 0.3 do 0.5 „żeby było świeżo”. W efekcie, gdy coś się psuje, nie wiadomo, czy winny jest nowy kod, nowy schemat czy nowe SDK. Migracje SDK lepiej robić oddzielnymi wydaniami technicznymi, z jasnym planem testów i możliwością rollbacku.

Błąd nr 4: brak feature flagów i natychmiastowych kill‑switchy.
Wypuścić nowy algorytm rekomendacji od razu na 100 % użytkowników — to fajne, dopóki nie zacznie dawać dziwnych wyników albo nie położy zewnętrznej usługi. Bez feature flag waszą jedyną dźwignią jest pełny rollback wydania, który może dotknąć nie tylko nową funkcję, ale i dziesięć niewinnych ulepszeń. Zróbcie choćby proste flagi przez env albo małą konfigurację.

Błąd nr 5: nadzieja na „natychmiastową” aktualizację manifestu.
Częste złudzenie — myśleć, że gdy tylko zmienicie tools lub openapi.yaml, model od razu pozna nowy schemat. W praktyce manifest i opisy tools są cache’owane, a w już otwartych czatach mogą żyć długo. Ignorowanie tego prowadzi do nieoczywistych błędów: w nowych czatach wszystko działa, w starych — się wywraca. Planujcie zmiany schematów z uwzględnieniem tego zachowania i testujcie je przez Dev Mode i staging przed publikacją w Store.

Błąd nr 6: brak jasnego planu rollbacku i dokumentacji wydań.
Jeśli w waszym zespole nikt nie potrafi odpowiedzieć na pytania „jak cofnąć wydanie w 5 minut?”, „do której wersji się cofamy?”, „jak wrócić do starego schematu feedu?” — to znaczy, że nie macie rollbacku. On‑call powinien mieć krótki, ale konkretny scenariusz: które przyciski naciskać, które zmienne zmieniać i gdzie sprawdzić, że cofnięcie zadziałało.

Błąd nr 7: „nieme” wydania bez release notes i powiązania z metrykami.
Wypuszczanie wydań bez choćby szczątkowego changeloga oznacza, że za parę miesięcy będziecie zgadywać. Gdy p95 nagle wzrośnie, a konwersja spadnie, będziecie się zastanawiać: „co właściwie wtedy się zmieniło?” Nawyk pisania choćby minimalnych release notes i wiązania ich z datami deployu oraz wersjami ułatwia nie tylko audyt jakości, ale i życie całego zespołu.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION