Skip to content
··9 min de leitura

Telas em Branco Destroem a Confiança. Eu Tinha 31 Delas.

Encontrei 31 telas em branco no meu SaaS — tudo porque esqueci que multi-tenancy não é só sobre acesso a dados, mas sobre contexto de URL. Veja como o Claude Code me ajudou a corrigir todas em uma noite.

1º de novembro, 8h47. Recebi o e-mail.

"Oi Chandler — cliquei em 'Ver' e apareceu uma página em branco. Perdi todos os meus dados da entrevista?"

Meu estômago afundou. Páginas em branco não são bugs. São destruidoras de confiança.

Abri o console de dev. Verifiquei a rota. Usuário de agência, visualizando o cliente "Acme Corp", clicou em um botão, e... a URL mudou de /clients/acme-corp/agents/agent-name/results/123 para só /agent-name/results/123.

Perdeu o contexto do cliente. O React Router não encontrou a rota. Tela em branco.

"Ok, esse é um bug," pensei. "Vou corrigir e seguir em frente."

Configurei o Claude Code para analisar a base de código por padrões similares, então fui curtir meu sábado com a família. Almoço. Compras. Crianças. A rotina de sempre.

Mais tarde, fui checar. O Claude Code tinha encontrado o padrão — e não era bonito.

Até as 19h42, tinha corrigido 14 bugs. Às 19h48, encontrei mais 8. Às 19h56, o total chegou a 31.

Todos com a mesma causa raiz. Todos com o mesmo fix. Todos porque esqueci uma coisa ao construir multi-tenancy: navegação não é só sobre dados, é sobre contexto.

---

Quão Ruim Era, De Verdade?

Deixa eu ser honesto sobre o que esses 31 bugs significavam:

Para os usuários:

- Usuários de agências encontrando telas em branco (parecia quebrado, não bugado)

- Perda de progresso do fluxo de trabalho no meio da sessão

- "Essa plataforma é estável o suficiente para nossos clientes?"

- Cada tela em branco = um passo mais perto de cancelar o trial

Para mim:

- 5+ relatórios de bug por dia (todos relacionados a navegação)

- 2-3 horas depurando cada um individualmente

- Não conseguia lançar novas funcionalidades (muito ocupado apagando incêndios)

- Medo genuíno: "E se quebrei algo mais e ainda não sei?"

A questão existencial: Se não consigo nem fazer a navegação funcionar, por que alguém deveria confiar ao STRAŦUM a estratégia de marketing dos seus clientes?

Telas em branco destroem a confiança mais rápido que qualquer coisa.

---

O Bug Que Começou Tudo

Deixa eu mostrar exatamente o que aconteceu.

Fluxo do usuário (o que deveria ter funcionado):

1. Ir para /clients/acme-corp/agents/analysis

2. Clicar em "Iniciar Sessão"

3. Completar a análise

4. Clicar em "Ver Resultados"

5. Ver os resultados em /clients/acme-corp/agents/analysis/results/123

O que realmente aconteceu:

1. ✅ /clients/acme-corp/agents/analysis (ótimo)

2. ✅ Iniciar sessão (funcionando)

3. ✅ Completar análise (dados salvos)

4. ❌ **Clicar em "Ver Resultados" → PÁGINA EM BRANCO**

5. ❌ URL mudou para /analysis/results/123 (perdeu contexto do cliente)

O React Router procurou uma rota em /analysis/results/123. Não existia para usuários de agência. Não renderizou nada.

O usuário vê: Tela branca em branco. Sem mensagem de erro. Sem spinner de carregamento. Só... nada.

---

O Que Encontrei Quando Eu (Na Verdade o Claude Code) Começou a Vasculhar

Aqui está como era o código quebrado:

```typescript
// AgentPage.tsx (SANITIZADO - o padrão quebrado)
import \{ useParams, useNavigate \} from 'react-router-dom';

export function AgentPage() \{
  const { clientSlug \} = useParams<\{ clientSlug: string \}>();
  const navigate = useNavigate();

  const handleViewResults = (sessionId: string) => \{
    // Problema: Rota hardcoded, sem contexto de cliente
    navigate(`/agent-name/results/${sessionId\}`);
  };

  return (
    // ... componente
  );
}
```

Vê o problema?

Extraí clientSlug da URL. Usei para buscar dados. Mas quando naveguei, esqueci completamente dele.

Rotas de agência parecem assim: /clients/acme-corp/agents/agent-name

Rotas de PME parecem assim: /agent-name

Hardcodei o padrão de PME. Usuários de agência = quebrado.

---

A Percepção: Eu Tinha 31 Desses

14h15. Corrigi o primeiro bug. Fiz commit. Me senti bem.

15h42. Encontrei outro em um agente diferente. Mesmo padrão. Corrigi.

16h18. Outro agente. A mesma coisa.

17h30. Parei e encarei minha tela por 10 minutos sólidos.

Cada página de agente tinha o mesmo bug. Cada componente aninhado que navegava. Cada elemento de UI compartilhado com um botão "Ir para...".

Eu poderia corrigir um por um e passar 2 dias. Ou poderia encontrar o padrão e corrigir todos sistematicamente.

Escolhi sistemático.

---

O Fix: Navegação com Consciência de Contexto

Em vez de useParams() em cada componente, pedi ao Claude Code para criar um provider de Context:

```typescript
// contexts/ClientContext.tsx
import \{ createContext, useContext \} from 'react';
import \{ useParams \} from 'react-router-dom';

interface ClientContextValue \{
  clientSlug: string | null;
\}

const ClientContext = createContext<ClientContextValue | null>(null);

export function ClientContextProvider(\{ children \}: \{ children: React.ReactNode \}) \{
  // Extrai clientSlug UMA VEZ no nível de layout
  const { clientSlug \} = useParams<\{ clientSlug: string \}>();

  return (
    <ClientContext.Provider value=\{{ clientSlug: clientSlug || null \}}>
      \{children\}
    </ClientContext.Provider>
  );
}

export function useClientContext() \{
  const context = useContext(ClientContext);
  if (!context) {
    throw new Error('useClientContext must be used within ClientContextProvider');
  \}
  return context;
}
```

Então envolvi as rotas de agência:

```typescript
// App.tsx
<Route path="/clients/:clientSlug/*" element={
  <ClientContextProvider>
    <ClientLayout />
  </ClientContextProvider>
}>
  <Route path="agents/analysis" element={<AnalysisAgent />} />
  <Route path="agents/strategy" element={<StrategyAgent />} />
  \{/* ... todas as rotas com escopo de cliente */\}
</Route>
```

Agora cada componente dentro tem acesso a clientSlug via context, não params.

O helper de roteamento (porque digitar o mesmo if/else 20 vezes cansa):

```typescript
// hooks/useContextRoute.ts
import \{ useClientContext \} from '@/contexts/ClientContext';

export function useContextRoute() \{
  const { clientSlug \} = useClientContext();

  const buildRoute = (route: string) => \{
    if (clientSlug) {
      // Rota de agência: /clients/acme-corp/agents/...
      const cleanRoute = route.startsWith('/') ? route.slice(1) : route;
      return `/clients/${clientSlug\}/$\{cleanRoute\}`;
    }
    // Rota de PME: /agent-name/...
    return route;
  };

  return \{ buildRoute, clientSlug \};
}
```

Uso (muito mais limpo):

```typescript
// AgentPage.tsx (SANITIZADO - o padrão corrigido)
import \{ useClientContext \} from '@/contexts/ClientContext';
import \{ useNavigate \} from 'react-router-dom';
import \{ useContextRoute \} from '@/hooks/useContextRoute';

export function AgentPage() \{
  const { clientSlug \} = useClientContext();
  const \{ buildRoute \} = useContextRoute();
  const navigate = useNavigate();

  const handleViewResults = (sessionId: string) => \{
    // Funciona para usuários de PME E Agência
    navigate(buildRoute(`agents/analysis/results/${sessionId\}`));
  };

  return (
    // ... componente
  );
}
```

Uma função helper. Com consciência de contexto. Funciona para ambos os tipos de usuário.

---

O Fix Sistemático: Um Dia, 31 Arquivos

Uma vez que tinha o padrão, virou mecânico.

1º de novembro de 2025 — O Sprint

Tarde (14h - 19h) — Encontrando e categorizando:

- Descobri o padrão em todas as páginas de agentes

- Construí o provider de Context e o helper de roteamento

- Testei a abordagem no primeiro agente

Noite (19h - 20h) — O fix sistemático:

19h42 — Primeira onda (14 bugs):

```
fix(multi-tenant): fix 14 navigation bugs and refactor all agents to useClientContext()

Frontend Changes (8 files):
- Refactored 6 agents to use useClientContext() hook
- Fixed 14 navigation bugs that lost client context across multiple agents

Backend Changes (5 files):
- Added client_id parameter to all save operations
- Updated base classes to extract client_id properly

Files changed: 14
Insertions: +732
Deletions: -164
```

19h48 — Segunda onda (8 bugs a mais):

```
fix(multi-tenant): fix 8 navigation bugs in strategy page

- Fixed 8 navigation buttons that lost client context
- Total bugs fixed: 22 (14 + 8)

Files changed: 1
Insertions: +71
Deletions: -23
```

19h56 — Onda final (9 bugs a mais):

```
fix(multi-tenant): fix 9 navigation bugs in interview and tool pages

- Fixed remaining navigation issues in nested components
- Total bugs fixed: 31 bugs across entire application

Files changed: 2
Insertions: +46
Deletions: -10
```

Percorri cada fluxo de agente. Usuário de PME. Usuário de agência. Cliquei em cada botão. Sem páginas em branco.

Dano total: 17 arquivos alterados, 849 inserções, 197 exclusões.

Tempo investido: Cerca de 6 horas de trabalho não contínuo com o Claude Code (descoberta às 14h → commit final às 20h).

Tempo economizado: Provavelmente 30+ horas de correções individuais de bugs e suporte ao usuário.

---

O Que Aprendi (Do Jeito Difícil)

1. Navegação multi-tenant é mais difícil que isolamento de dados

Você pode filtrar dados por org_id. Essa é a parte fácil.

Mas navegação? Você tem duas estruturas de URL válidas para a mesma funcionalidade:

```
PME:    /agent-name/session/123
Agência: /clients/acme-corp/agents/agent-name/session/123
```

Cada chamada navigate() precisa saber qual padrão usar. Erre uma vez, e os usuários veem páginas em branco.

2. useParams() mente para você

```typescript
const \{ clientSlug \} = useParams();
```

Isso funciona... até a rota mudar. Então clientSlug fica undefined, e a próxima navegação quebra.

React Context não mente. Está sempre disponível, sempre consistente.

3. Conte cada bug antes de declarar vitória

Achei que tinha 14 bugs. Aí encontrei mais 8. Depois mais 9.

Lição: Faça grep em toda a base de código, não apenas nos arquivos que você acha que têm bugs.

4. Quando você encontra o mesmo bug duas vezes, pare e crie um padrão

Correção individual: 31 bugs = provavelmente uma semana

Correção sistemática: Encontre o padrão → Crie um helper → Corrija todas as instâncias = 6 horas

Os 10 minutos que passei encarando minha tela às 17h30 me pouparam dias.

5. Bugs de navegação são ameaças existenciais

Ficamos obcecados com gerenciamento de estado, busca de dados, otimização de API.

Mas navegação quebrada = telas em branco = "Esta plataforma está quebrada."

Usuários não ligam para suas políticas de RLS ou sua arquitetura multi-tenant. Ligam que clicar em "Ver Resultados" mostra os resultados. Essa mesma lacuna entre "tecnicamente funciona" e "realmente pronto" apareceu de novo quando construí um app iOS nativo com IA — o Claude Code gerou o scaffold rápido, mas o polimento que faz usuários confiarem em um produto? Esses são os 40% que só humanos podem entregar.

---

O Padrão (Para Seu App Multi-Tenant)

Se você está construindo SaaS multi-tenant com URLs hierárquicas:

✅ Passo 1: Crie um provider de Context no nível do layout

```typescript
<ClientContextProvider>
  \{/* Todas as rotas com escopo de tenant */\}
</ClientContextProvider>
```

✅ Passo 2: Construa um helper de roteamento

```typescript
const \{ buildRoute \} = useContextRoute();
navigate(buildRoute('agents/analysis/session/123'));
```

✅ Passo 3: Nunca hardcode rotas

```typescript
// ❌ RUIM
navigate('/analysis/session/123');

// ✅ BOM
navigate(buildRoute('agents/analysis/session/123'));
```

✅ Passo 4: Faça grep em TODAS as chamadas de navegação

```bash
# Encontre cada chamada navigate()
grep -r "navigate(" src/ > navigation_audit.txt

# Encontre rotas hardcoded
grep -r "navigate('/" src/ | grep -v "buildRoute"
```

✅ Passo 5: Teste com todos os tipos de usuário

Fluxo de PME. Fluxo de agência. Cada botão. Cada link. Sem páginas em branco.

---

Os Resultados

Antes de 1º de novembro:

- 31 bugs de navegação à espreita

- 5+ relatórios de bug por dia

- Usuários de agência questionando a estabilidade da plataforma

- Eu: Aterrorizado de lançar novas funcionalidades

Depois de 1º de novembro:

- 0 bugs de navegação

- 0 tickets de suporte relacionados a navegação

- Padrão estabelecido para desenvolvimento futuro

- Eu: Confiante lançando novas rotas de agentes

Velocidade de desenvolvimento:

- Adicionar novas rotas: 5 minutos (era 30 minutos + "isso vai quebrar?")

- Relatórios de bug: 0 (eram 5+ por dia)

- Confiança do usuário: Restaurada ("A plataforma parece estável agora")

As 6 horas que passei corrigindo esses bugs valeram 10× em carga de suporte reduzida e confiança do usuário restaurada.

---

Por Que Estou Compartilhando Isso

Navegação multi-tenant não é glamourosa. Ninguém comemora "corrigiu 31 bugs em 6 horas" da maneira que comemoram "lançou nova funcionalidade."

Mas se você está construindo SaaS multi-tenant como eu com https://stratum.chandlernguyen.com/ uma plataforma de marketing com IA para agências — você vai se deparar com isso. Talvez não 31 bugs. Talvez só 5. Mas vai encontrar.

Quando encontrar, lembre-se:

1. Context > Params para estado de navegação

2. Sistemático > Individual para correções

3. Faça grep em toda a base de código (você vai encontrar mais do que imagina)

4. Teste com todos os tipos de usuário antes de declarar vitória

E se você se encontrar encarando uma tela em branco se perguntando para onde foi seu contexto, saiba que já estive na mesma situação. :)

Qual é o bug mais doloroso que você já lançou para usuários reais — o tipo que só descobriu porque alguém te contou? Adoraria genuinamente ouvir.

Abraços,

Chandler

A série de arquitetura do STRAŦUM: Essa crise de navegação foi parte de uma jornada maior de multi-tenancy. Começou com construir multi-tenancy no Dia 2, escalou quando tive que reconstruir todo o schema no Dia 67 e concluiu quando descobri que meu banco de dados estava correto mas 296x muito lento.

---

Ainda codando, ainda aprendendo, ainda encontrando bugs em grupos de 31.

Solicite acesso alpha em https://stratum.chandlernguyen.com/request-invitation

---

P.S. — O usuário que reportou aquele primeiro bug? Ele não perdeu os dados da entrevista. Estavam salvos no banco de dados. Só não conseguia vê-los por causa de uma rota quebrada. Quando corrigi às 19h42, todo o trabalho dele ainda estava lá. Esse pequeno alívio fez as 6 horas valerem a pena.

---

Continuar Lendo

Minha Jornada
Conectar
Idioma
Preferências