Własne testy A/B w GTM i Firebase — bez dedykowanego narzędzia
- A/B testing
- GTM
- Firebase
- GA4
Dedykowane platformy do testów A/B potrafią kosztować więcej niż cała reszta stacku analitycznego. A jeśli masz porządny tracking, solidny test zrobisz samodzielnie — w Google Tag Managerze na webie i w Firebase na mobile — za darmo i pod pełną kontrolą. Jeden warunek: test jest tylko tak wiarygodny, jak dane pod nim (o tym pisałem w architekturze danych pod AI — tu obowiązuje to samo).
Uwaga: część feature’ów, które realnie testowałem, była na tyle specyficzna dla jednego biznesu, że celowo je tu uogólniam. Liczy się metoda, nie konkretny przycisk.
Własne testy A/B w GTM na first-party cookie
Sedno: first-party cookie trzyma wariant, GTM nim zarządza, GA4 go raportuje. Tag
custom-HTML przy wejściu losuje wariant, zapisuje go w ciasteczku i wysyła do dataLayer:
// GTM custom HTML — przydział wariantu
// i zapis do first-party cookie
(function () {
function getCookie(name) {
var m = document.cookie.match(
new RegExp('(?:^|; )' + name + '=([^;]*)'));
return m ? m[1] : null;
}
function setCookie(name, value, days) {
var exp = new Date(Date.now() + days * 864e5);
document.cookie = name + '=' + value +
'; expires=' + exp.toUTCString() +
'; path=/; SameSite=Lax';
}
var test = 'ab_cta'; // nazwa testu
// user ma już wariant — nie losuj ponownie
if (getCookie(test)) return;
var variant = Math.random() < 0.5 ? 'v0' : 'v1';
setCookie(test, variant, 30); // ciasteczko na 30 dni
dataLayer.push({
event: 'ab_assign',
ab_test: test,
ab_variant: variant
});
})();
Trzy rzeczy, które robią z tego system, a nie hak:
- Jeden wspólny tag odpala się na evencie
ab_assigni obsługuje wszystkie testy — każdy test pcha ten sam event z własną nazwą i wariantem. Nie mnożysz tagów. - Wariant jako parametr zdarzenia i wymiar niestandardowy. Tag GA4 na
ab_assignwysyłaab_testiab_variantjako parametry zdarzenia — z nich liczy zapytanie w BigQuery niżej. Żeby segmentować raporty w samym GA4, ustawab_varianttakże jako właściwość użytkownika (zakresu sesji GA4 nie ma). - Przydzielaj w punkcie ekspozycji. Losuj tam, gdzie user staje się uprawniony do testu — dla zmiany widocznej tylko w koszyku rób to na wejściu do koszyka. Przydzielanie na każdej stronie nie psuje proporcji 50/50 wśród wchodzących do koszyka (losowanie jest niezależne od tego, czy ktoś tam dotrze), ale rozmywa wynik: wrzucasz do analizy tłum userów, którzy zmiany nigdy nie zobaczyli, i tracisz moc statystyczną. Wcześniejszy przydział ma sens tylko wtedy, gdy wariant musi być gotowy przed daną stroną (zmiana widoczna już wcześniej albo ryzyko „mignięcia” przy decyzji na renderze) — i wtedy i tak filtruj analizę do faktycznie eksponowanych. To ta sama zasada, co activation event w Firebase.
Gdy wyłoni się zwycięzca, zmieniasz skrypt tak, by wymusić wygrany wariant u wszystkich (warunek nadpisujący ciasteczko dla pozostałych grup) i puszczasz 100% ruchu.
A/B testy w Firebase Remote Config (mobile)
Na mobile nie ma ciasteczek — jest Firebase A/B Testing na Remote Config. Konsola Firebase → A/B Testing → Create experiment → Remote config. Kilka rzeczy, które nie są oczywiste:
- Osobny eksperyment per platforma (Android i iOS). Nazwy prefiksuj
[AND]/[IOS]. - Targeting: wybierasz aplikację i ustawiasz suwak na 100% userów (lub warunki).
- Activation event — tu jest pułapka. To zdarzenie decyduje, kto wchodzi do testu
(np.
view_itemdla oglądających kartę produktu). Ale activation event musi być eventem bez parametrów. Jeśli chcesz aktywować na parze event + parametr (np. tylko karty z ofertą 3P), musisz stworzyć dedykowany event (np.view_item_3p). Ta sama zasada dotyczy metryk w Goals. - Goals: primary metric + zawsze dorzuć
purchasei przychód jako KPI kontrolne. - Variants mapują się na wartości klucza Remote Config. Jeśli klucza nie ma na liście — developerzy muszą najpierw wrzucić go na produkcję.
Klient czyta wartość RC i przełącza UI:
let mode = RemoteConfig.remoteConfig()
.configValue(forKey: "product_description_mode")
.stringValue
switch mode {
case "minimal": showMinimalDescription()
case "detailed": showDetailedDescription()
default: showClassicDescription() // kontrolny
}
Firebase liczy istotność częstościowo — pokazuje p-value i przedział ufności, tak jak klasyczny test hipotez. Co p-value mówi, a czego nie, i czym różni się od podejścia bayesowskiego, rozkładam w tekście o testach A/B dla analityków.
Analiza wyników: konwersja i przychód per wariant (GA4 → BigQuery)
Konsola GA4 wystarczy do podglądu, ale realną kontrolę daje SQL na eksporcie GA4 do BigQuery — zwłaszcza gdy chcesz policzyć konwersję i przychód dokładnie tak, jak definiujesz je w biznesie:
-- Wyniki testu A/B per wariant:
-- użytkownicy, kupujący, przychód i ARPU
-- 1. jeden wiersz na użytkownika: pierwsze
-- przypisanie w TYM teście i liczba wariantów
WITH assignment AS (
SELECT
user_pseudo_id,
MIN(event_timestamp) AS first_ts,
ANY_VALUE(variant) AS variant,
COUNT(DISTINCT variant) AS variants_seen
FROM (
SELECT
user_pseudo_id,
event_timestamp,
(SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'ab_variant') AS variant,
(SELECT value.string_value
FROM UNNEST(event_params)
WHERE key = 'ab_test') AS test
FROM `projekt.analytics_XXXXXX.events_*`
WHERE event_name = 'ab_assign'
AND _TABLE_SUFFIX
BETWEEN '20260501' AND '20260531'
)
-- wspólny event dla wszystkich testów
WHERE test = 'ab_cta' AND variant IS NOT NULL
GROUP BY user_pseudo_id
),
-- 2. zakupy w okresie testu
purchase_events AS (
SELECT
user_pseudo_id,
event_timestamp,
ecommerce.purchase_revenue AS revenue
FROM `projekt.analytics_XXXXXX.events_*`
WHERE event_name = 'purchase'
AND _TABLE_SUFFIX
BETWEEN '20260501' AND '20260531'
),
-- 3. tylko zakupy PO pierwszym przypisaniu,
-- zsumowane na użytkownika
purchases AS (
SELECT
a.user_pseudo_id,
COUNT(*) AS transactions,
SUM(p.revenue) AS revenue
FROM assignment AS a
JOIN purchase_events AS p
ON p.user_pseudo_id = a.user_pseudo_id
AND p.event_timestamp > a.first_ts
GROUP BY a.user_pseudo_id
)
SELECT
a.variant,
COUNT(*) AS users,
COUNT(p.user_pseudo_id) AS buyers,
SAFE_DIVIDE(
COUNT(p.user_pseudo_id), COUNT(*)
) AS conversion_rate,
IFNULL(SUM(p.revenue), 0) AS revenue,
SAFE_DIVIDE(
IFNULL(SUM(p.revenue), 0), COUNT(*)
) AS arpu
FROM assignment AS a
LEFT JOIN purchases AS p
USING (user_pseudo_id)
-- bez użytkowników z dwoma wariantami
WHERE a.variants_seen = 1
GROUP BY a.variant
ORDER BY a.variant;
Masz w jednej tabeli wielkość grup, współczynnik konwersji, przychód i ARPU — czyli wszystko, czego trzeba do decyzji, którą wersję wypuścić na 100%. Liczby z tej tabeli wpisz do kalkulatora istotności testu A/B — powie, czy różnica jest istotna i jak duża może naprawdę być.
Trzy miejsca, w których to zapytanie najłatwiej popsuć:
- Filtr na nazwę testu. Wspólny event
ab_assignobsługuje wszystkie testy, więc beztest = 'ab_cta'mieszasz wyniki kilku testów naraz. - Zakup po przypisaniu. Zakup sprzed pierwszego
ab_assignnie mógł zależeć od wariantu, a liczony rozmywa różnicę między grupami. - Jeden wiersz na użytkownika przed złączeniem. Ktoś z wygasłym cookie dostaje drugie
ab_assign— czasem z innym wariantem. Bez agregacji jego przychód liczy się podwójnie, a bez filtravariants_seen = 1trafia do obu grup.
Pułapki, o których łatwo zapomnieć
- Zgody. Ciasteczko testu nie jest niezbędne do działania strony, więc wymaga zgody (Prawo komunikacji elektronicznej, za dyrektywą ePrivacy) — losuj i zapisuj dopiero po niej. Consent Mode tylko przekazuje tę decyzję tagom Google.
- Safari skraca ciasteczko. Ciasteczko ustawione przez JavaScript żyje w Safari najwyżej
7 dni (ITP), niezależnie od
expires. Kto wróci po tygodniu, zostanie wylosowany ponownie — stąd w SQL filtrvariants_seen = 1i stąd długie testy tracą wiarygodność. - Jeden user = jeden wariant. Ciasteczko to pamięć przydziału; nie losuj ponownie przy każdej odsłonie.
- Firebase: bez parametrów. Activation event i metryki celu muszą być eventami bez parametrów — inaczej test nie ruszy.
- Spójność web↔mobile. To dwa osobne mechanizmy (cookie vs Remote Config) — pilnuj, by definicja wariantu i metryki były takie same po obu stronach.
Cała reszta to dyscyplina: dobra warstwa zdarzeń pod spodem, jeden wariant na użytkownika i metryka liczona tak samo dla każdej grupy. Narzędzie jest tanie — wiarygodność bierze się z trackingu.