Pular para o conteúdo principal

Anti-patterns

Lista consolidada do que NÃO fazer no setup de marketing. Cada item bate com erro real visto em produção da Cactus ou de plataformas similares.

Atribuição

❌ Header tem <a href="/?utm_source=header_btn">Início</a>.

Por quê é ruim: sobrescreve atribuição real do user. Canal "header_btn" infla até virar #1 em todos os relatórios; canais reais (Meta, Google) somem.

✅ Use data-* attributes pra rastrear UI ou eventos GTM custom (select_item).

2. Esquecer set completo de UTMs

❌ Link de campanha só com utm_campaign=cyber-monday.

Por quê é ruim: last-touch herda utm_source da campanha anterior. User que veio do Google primeiro e clicou em link de e-mail depois conta como conversão do Google (porque source não foi sobrescrito).

✅ Sempre passe os 4 canonical (utm_source, utm_campaign, utm_medium, utm_content) em todo link.

3. Mudar pra first-touch sem alinhar com BI

❌ Decidir unilateralmente "vamos preservar primeiro touch".

Por quê é ruim: todos os dashboards estão calibrados em last-touch. Mudança silenciosa quebra histórico de comparação.

✅ Discussão coordenada com BI antes. Se mudar, é deliberado e documentado.

Pixels e plataformas

4. Pixel duplicado (GTM tag + SDK próprio)

❌ Adicionar Meta Pixel via tag GTM custom e o front já carrega via useAnalytics.

Por quê é ruim: cada evento dispara 2x. Audiences inflam, conversion duplica, ROAS quebra.

✅ Olhe app/utils/metrics/* antes de adicionar pixel via GTM.

5. Tag GTM disparando Purchase em deposit_initiated

❌ Custom Event pix_generated → tag Purchase Meta.

Por quê é ruim: depósito gerado mas não confirmado dispara Purchase. Inflate brutal.

✅ Tag Purchase só no pix_confirmado_ftd (BFF confirmou).

6. Não passar transactionId na tag de conversion

❌ Conversion tag Google Ads sem transactionId.

Por quê é ruim: refresh da página dispara nova conversão. Mesma compra conta 2-3x.

✅ Sempre passa transactionId: {{DLV - transactionId}} — Google Ads deduplica por isso.

7. Mudar gtmId/pixelId em prod sem testar

❌ Update direto no backoffice → "deploy" instant.

Por quê é ruim: tag mal configurada dispara evento errado. Polui dashboards. Difícil de reverter.

✅ Testa no GTM Preview Mode primeiro. Validar com Pixel Helper/Tag Assistant.

8. Mudar cookieConsentCosmeticOnly sem aprovação registrada

❌ Alterar o flag numa brand por conta própria, em qualquer direção.

Por quê é ruim: o flag decide se a recusa do usuário tem efeito. Mudança sem aprovação registrada dos donos de marketing e de LGPD gera divergência entre o que a brand faz e o que foi acordado — e o efeito é imediato no deploy seguinte.

✅ Aprovação conjunta registrada antes. Contexto do comportamento atual em Modo Cosmético — página que, ela mesma, aguarda validação do responsável por LGPD.

9. Assumir que o boot do GCM v2 é denied

❌ Construir tag ou raciocínio de atribuição partindo de "os sinais sobem denied e só liberam depois do consent".

Por quê é ruim: não é o que o código faz. injectConsentDefaults() injeta os 7 sinais como granted, sem wait_for_update, para toda brand. O único ramo condicional é o inverso: um update para denied quando a brand não é cosmética e o usuário recusou em visita anterior.

✅ Leia a política real em Consent LGPD antes de assumir estado de consent. E note que essa página aguarda validação do responsável por LGPD.

10. Não testar o estado denied

❌ Lançar tag GTM nova testando só o caminho "aceitou tudo".

Por quê é ruim: o estado denied é alcançável — brand não-cosmética + recusa registrada em visita anterior dispara update all-denied no boot. Tag que dependa de cookie bloqueado falha em silêncio, e você não vê isso testando só o caminho felizes.

✅ Teste com cookies_consent gravado com performance/functionality/others todos false, numa brand com cookieConsentCosmeticOnly: false. Note que não há botão "Recusar" no banner — o caminho é Personalizar → desligar tudo → salvar, ou gravar o cookie na mão.

Cookies

cookieName: "rmkcactus" no remarketing.

Por quê é ruim: vaza plataforma white-label pro cliente final. Front rejeita em runtime e desativa o flag.

rmk<abreviação-da-brand> (rmk7k, rmkbpb, rmkst77).

❌ Setar HttpOnly: true no cookie de tracking.

Por quê é ruim: client não consegue ler. Submit de signup/deposit vai sem UTMs.

✅ Mantém HttpOnly: false. Atribuição precisa client + server consumir.

13. Sanitizar lastclick (Clever)

❌ Aplicar mesmo blocklist do cookie_tracking no lastclick.

Por quê é ruim: Clever pode usar formatos com strings que o blocklist mangia (document, appendChild, {macro}). Sanitização mutila IDs e quebra atribuição.

lastclick é opaco — sem sanitização. Mesmo padrão do legado Vue.

14. Mudar SameSite do lastclick

SameSite: "Lax" no lastclick.

Por quê é ruim: Clever entrega tráfego cross-site (iframe/popup/pixel chain). Browsers rejeitam Set-Cookie cross-site sem SameSite=None; Secure. Quebra ~99% do tráfego Clever.

SameSite: "None"; Secure: true — confirmado com time Clever em maio/2026.

❌ Mudar cookie_tracking pra tracking_cookie sem migração one-shot.

Por quê é ruim: usuários existentes perdem atribuição e o cookie velho fica órfão no browser.

✅ Migração one-shot na leitura: lê o nome legado, adota o valor, deleta o legado, nunca escreve nele. O exemplo implementado é LEGACY_CONSENT_COOKIE_NAMES = ["cactusCookiesConsent"] em app/utils/consent.ts — forma completa em Cookies → renomear cookie em produção.

16. TTL absurdo

trackingCookieTtlHours: 99999 (~11 anos).

Por quê é ruim: atribuição vaza entre campanhas. User que clicou em ad de 2 anos atrás conta como conversão do canal de 2 anos atrás.

✅ Janelas razoáveis: 24h–90 dias. O default é 720h (30 dias) — e é ele que vale em 10 das 13 brands, porque não declaram a chave.

Headers / Payload

17. Adicionar UTM em header HTTP

❌ Custom header X-UTM-Source em toda request.

Por quê é ruim: headers têm limite de tamanho (~8KB). UTMs custom (utm_oferta, utm_term, etc) estouram. Body suporta.

✅ UTMs viajam só no body de signup/deposit.

18. Adicionar src ao deposit

❌ Deposit payload com src: "fb_ads".

Por quê é ruim: quebra paridade com legado Vue. BFF valida payload e pode rejeitar.

src só em signup. Deposit é subset. (Lista exata em UTMs)

19. Adicionar affiliation_code/subid/app_source ao deposit

❌ Deposit payload com affiliation_code.

Por quê é ruim: mesma razão — paridade legado. Atribuição de depósito é resolvida pelo BFF via user.recommended_by, não pelo cookie.

✅ Deposit subset. Signup tem todos esses campos.

Modo App / TWA

20. Disparar pixel client em modo app

❌ Não desativar Meta Pixel quando ?app=true / ?is_twa=true.

Por quê é ruim: apps inflam métricas web. A atribuição mobile é AppsFlyer.

shouldLoadTrackers = !isAppMode — a cláusula !isBot não existe mais (removida em 2026-06). Honra esse gate, e não presuma comportamento diferente pra bot.

21. Mover AppsFlyer pra dentro do gate de trackers

if (!shouldLoadTrackers) return; appsFlyer.sendFTD(...).

Por quê é ruim: scopes inversos. App: gate=false (early return) → nunca roda. Web: appsFlyer.ready=false → nem chama. Resultado: nunca dispara. Bug histórico (maio/2026).

✅ AppsFlyer antes do gate. O hook decide internamente se está em app.

22. Modificar assetlinks.json

❌ Update direto em overrides/<brand>/public/.well-known/assetlinks.json.

Por quê é ruim: chaves criptográficas atreladas ao APK no Play Store. Typo quebra TWA em prod.

✅ Coordena com time mobile. Atualiza só quando há fingerprint novo do APK.

Debug / Prod

23. Tentar ?EnableDebug=1 em prod

❌ Esperar que toggle visual de debug funcione em prod.

Por quê é ruim: foi removido em maio/2026 por vazamento de cookies sensíveis (especificamente cf-worker-key em response).

✅ Use o Cloudflare Dashboard ou wrangler tail <worker-name> (sem --env — não existe environment no wrangler.toml). Ver Debug.

24. Reportar bug sem X-LOG-INFO

❌ "Tá quebrado, dá uma olhada".

Por quê é ruim: dev sem X-LOG-INFO não consegue correlacionar request específica do user a logs server-side.

✅ Sempre incluir X-LOG-INFO da request afetada (Network tab) ao reportar.

Audiência / Remarketing

25. Persistir viewed_game_<slug> granular sem cap-prioritário

❌ Marcar tag por jogo individual no <rmkCookie>_aud esperando que as funnel tags sejam poupadas.

Por quê é ruim: cassino tem 2k-5k games e o cap é de 40 tags. A eviction é puramente por idade — não existe prioridade canônica protegendo funnel tags, então ftd_completed (o sinal mais valioso) sai pra dar lugar a viewed_game_random.

✅ Granularidade provider-level (viewed_provider_pgsoft) ou dataLayer-only (sem persistir cookie). Detalhes em Remarketing.

26. Confiar no external_id antes de BFF aceitar

❌ Setar sendToBff: true no remarketing config sem coordenar com back.

Por quê é ruim: front envia external_id no payload, BFF ignora (não mapeou o campo). Tracking morto.

✅ Sempre alinha com BFF antes. Liga sendToBff: true só após backend support confirmado.

Geral

27. Pedir captura de campo novo só no front

❌ "Adiciona um tcclid no cookie, marketing pediu."

Por quê é ruim: front captura, mas BFF não processa. Dashboard de BI nunca vê o campo. Tracking morto.

✅ Sempre alinha com BFF primeiro. Front + back juntos.

28. Configurar feature flag em app/config/features/features.ts (default base) sem propagar pros overrides

❌ Adicionar remarketingId: { enabled: true } no default base e esquecer das brands com override.

Por quê é ruim: brand override é file-replacement (não deep-merge). Brands com override ficam com undefined, não herdam o default.

✅ Adicionar campo no tipo + default + propagar pra todos overrides existentes (ou documentar explicitamente que override é opt-in).

29. Esquecer que cada brand tem analytics config diferente

❌ Testar campanha em 7k-bet-br e deduzir que vai funcionar em betpontobet-bet-br.

Por quê é ruim: o override muda pixel ID, GTM ID, container (anaLayer vs dataLayer), whitelist de eventos, Floodlight, Pendo, TTL de atribuição. Funcionar em uma ≠ funcionar em outra — e 7k-bet-br é justamente a menos representativa (GTM primário desligado, anaLayer, estratégia engagement).

✅ Testa explicitamente em cada brand alvo. Diferenças no config map.

30. Assumir que os pixels de conversão são carregados com atraso

❌ "O pixel não disparou porque o scheduler ainda não liberou — ele espera LCP, primeira interação, ou 25s em auditoria."

Por quê é ruim: esse sistema de gating não existe mais. Foi removido em 2026-06 justamente porque quebrava marketing: eventos disparados antes dos gates eram descartados (não havia fila de replay), usuário que convertia sem interagir nunca era rastreado, e a auditoria media uma página que nenhum usuário real via. Hoje scheduleThirdParty(fn) executa fn() imediatamente (modulo o guard de prerender e o kill switch da brand). GTM, Meta Pixel, Taboola e Kwai são eager por default.

✅ Modelo atual, três faixas:

FaixaQuemQuando carrega
eager (default)GTM, Meta Pixel, Taboola, Kwai, Webtrends, Floodlightno mount
engagement (opt-in por brand — hoje só 7k-bet-br)os mesmosfilas primadas no mount (nenhum evento é perdido), download na 1ª interação real ou no marketingScriptsMaxDelayMs (5000)
deferredClarity, Hotjar, selo RAidle, ou 1ª interação, ou timeout de segurança
interaction-onlyPendosó após a 1ª interação real

Kill switch global das duas faixas: featuresConfig.thirdPartyScriptsDisabled.

31. Presumir que bots e auditorias sintéticas carregam menos script

❌ "O Lighthouse não conta porque a gente não carrega tracker pra ele."

Por quê é ruim: carrega. O branch de auditoria sintética foi removido em 2026-06 — decisão deliberada de não inflar score. Um número de Lighthouse ruim é um número real.

✅ Trate a métrica de laboratório como representativa. Se precisa melhorar, mude o carregamento (faixa engagement), não o gate.