Deco
Pt

A/B testing e redirects

withABTesting + padrão SITES_KV; loadRedirects de blocks; padrão CSV-redirects.

Esta página cobre dois temas operacionais: A/B testing (servir variants alternativos) e redirects (mover usuários de URLs antigas para novas). Ambos vivem na camada do Worker, antes do framework ver o request.

A/B testing

Quando usar withABTesting

withABTesting é para A/B no nível de versão de site — comparar codebases inteiros (canary deploys, experimentos full-stack). Para A/B no nível de conteúdo (mudar uma section), use matchers do CMS.

Cenário Use
Mudar texto/imagem de um Hero Matcher CMS + multivariate block
Testar nova nav inteira contra a velha withABTesting
Migração gradual v1 → v2 com 10% do tráfego withABTesting
Targeting de feature flag (logado vs deslogado) Matcher CMS

Setup

 // src/worker-entry.ts
import { createDecoWorkerEntry } from "@decocms/start/sdk/workerEntry";
import { withABTesting } from "@decocms/start/sdk/abTesting";

const decoWorker = createDecoWorkerEntry(serverEntry, { admin: { /* ... */ } });

export default withABTesting(decoWorker, {
  kvBinding: "SITES_KV",
  preHandler: (request, url) => null,
}); 

KV namespace

withABTesting lê variants do KV. Crie e bind:

 npx wrangler kv:namespace create SITES_KV 

Adicione no wrangler.jsonc :

 {
  "kv_namespaces": [
    { "binding": "SITES_KV", "id": "abc123..." }
  ]
} 

Schema de variants

KV holds entries chaveados por host:

 www.minha-loja.com → { variants: [{ name: "v1", weight: 90 }, { name: "v2", weight: 10 }] } 

Quando um request chega:

  1. withABTesting lê entry do KV.
  2. Determina o variant via cookie + buckets ponderados.
  3. Posta para o Worker variant target (ou aceita “default” e roda o Worker corrente).

Cookie _deco_variant=<name> faz o variant ser sticky para a sessão.

Padrões de deploy

Canary deploy:

 Worker A (production current) — peso 90
Worker B (next version)        — peso 10 

Bumpa para 50/50 quando Worker B passar. Promove para 100/0 cortando A.

Migração gradual:

 Worker v1-fresh (legacy)    — peso 80
Worker v2-tanstack (new)    — peso 20 

Aumenta peso v2 conforme métricas saem boas.

preHandler

Roda antes do A/B routing. Use para shortcut em redirects ou bypass de A/B em paths:

 preHandler: (request, url) => {
  if (url.pathname.startsWith("/admin")) {
    return new Response("Not allowed", { status: 403 });
  }
  return null;
}, 

Devolva Response para shortcut, null para continuar para A/B routing.

Tracing variants

Toda variação assigned é loggada. Filtre wrangler tail :

 npx wrangler tail --search "_deco_variant=v2" 

Adicione _deco_variant aos events do seu coletor de analytics para análise downstream.

Redirects

loadRedirects (driven pelo CMS)

Padrão preferido. Edita redirects no admin como blocks; framework carrega:

 // src/worker-entry.ts
import { loadBlocks } from "@decocms/start/cms";
import { loadRedirects, matchRedirect } from "@decocms/start/sdk/redirects";

const cmsRedirects = loadRedirects(loadBlocks());

const proxyHandler = (request: Request, url: URL) => {
  const redirect = matchRedirect(url.pathname, cmsRedirects);
  if (redirect) {
    const target = url.search ? `${redirect.to}${url.search}` : redirect.to;
    return new Response(null, {
      status: redirect.status,
      headers: { Location: target },
    });
  }
  return null;
};

const decoWorker = createDecoWorkerEntry(serverEntry, {
  admin: { /* ... */ },
  proxyHandler,
}); 

Block:

 {
  "__resolveType": "$live/redirects/Redirect.ts",
  "from": "/old-product",
  "to": "/p/new-product",
  "status": 301
} 

OK para até ~1000 redirects. Para larger, prefira CSV.

Padrão CSV-redirects

Para 10.000+ redirects (típico de sites pré-migração com URLs legacy), loadRedirects é lento — todo decofile resolve. Use CSV em vez disso.

Um padrão comum é armazenar redirects num CSV:

 // src/middleware/redirects.ts
import redirects from "../../data/redirects.csv?raw";

const map = new Map<string, { to: string; status: number }>();
for (const line of redirects.split("\n").slice(1)) {
  const [from, to, status] = line.split(",");
  if (from && to) map.set(from.trim(), { to: to.trim(), status: Number(status) || 301 });
}

export const matchCsvRedirect = (pathname: string) => map.get(pathname) ?? null; 

?raw em Vite inlina o conteúdo do CSV em build time. Sem leitura runtime; lookup O(1).

Conecte no proxyHandler igual ao CMS-driven.

Combo CMS + CSV

CMS para redirects de longa cauda editáveis (poucos, mexidos por content team), CSV para legacy bulk (muitos, raramente tocados):

 const proxyHandler = (request: Request, url: URL) => {
  const cms = matchRedirect(url.pathname, cmsRedirects);
  if (cms) return redirectResponse(cms);

  const csv = matchCsvRedirect(url.pathname);
  if (csv) return redirectResponse(csv);

  return null;
}; 

CMS ganha porque é o que o time edita ativamente.

Cache de redirects

Redirects são respostas Cache-Control = bypass . Cloudflare ainda cacheia o redirect em si (HTTP 301 são cacheable por default), então depois do primeiro hit é instantâneo.

Para invalidar quando você atualizar o mapa de redirects, faça deploy — bundle redeploy gera novo Worker e reset cache.

301 vs 302

  • 301 = permanente. Browsers cache forever. SEO transfere link equity.
  • 302 = temporário. Sem cache; re-checa em todo hit.

Use 301 para URLs legacy (preferido). Use 302 para redirects de campanha ( /black-friday-2024 /c/promocao durante campanha; depois apaga).

Veja também

Found an error or want to improve this page?

Edit this page