Aula 01 · 75–100 minutos
O que é system design de front-end e por que é diferente do back-end
Aula introdutória de System Design de Front-end: o que é, como difere do back-end, o método RADIO e a primeira decisão defensável.
Resultado e mapa da aula
Ao final, você conseguirá receber um pedido vago — "desenha o front-end de um feed de notícias" — e devolvê-lo como um primeiro corte de decisão com: objetivo mensurável do ponto de vista do usuário, requisitos que restringem a arquitetura, estratégia de renderização candidata, separação entre estado local e estado de servidor, e uma forma de provar que a decisão funciona. Isso é o que separa "eu escolheria React com SSR" de uma proposta de engenharia.
- Entendemos por que front-end system design é uma disciplina própria, não "system design mais fácil".
- Aplicamos o método RADIO a um pedido vago.
- Vemos o fluxo de decisão de ponta a ponta em um diagrama.
- Orçamos performance com números, não adjetivos.
- Tratamos qualidade (Core Web Vitals, acessibilidade, segurança) como contrato.
- Praticamos com um caso real: busca com sugestões (autocomplete).
O problema real: "vamos usar React com SSR" não é um design
Front-end system design é frequentemente tratado como uma versão "menor" do system design de backend — como se bastasse desenhar componentes em vez de serviços. Isso é um erro de escopo. O front-end tem sua própria fonte de complexidade: ele executa em um ambiente que você não controla (o dispositivo e a rede do usuário), precisa lidar com um humano observando cada milissegundo de atraso, e a "infraestrutura" dele é o próprio navegador — parsing, layout, paint, memória, bateria.
Antes de desenhar, transforme a frase vaga em perguntas verificáveis: quem vê essa tela — usuário anônimo (SEO importa) ou logado (SEO não importa)? Qual é o dispositivo/rede mais comum e o pior caso aceitável? O conteúdo pode ser cacheado ou é personalizado por usuário? Existe atualização em tempo real? O que deve acontecer quando a API de dados falha ou demora? Que evidência prova que a UI está rápida e acessível?
O método: RADIO aplicado a uma decisão real
RADIO é o framework mais citado especificamente para entrevistas e decisões de front-end system design — distinto do framework de backend porque o vocabulário de "componentes e dados" aqui é sobre UI e cliente, não sobre bancos e filas (GreatFrontEnd Playbook).
- Requirements exploration: quem usa, em que dispositivo/rede, anônimo ou logado, tempo real ou não, qual é o resultado observável que define sucesso.
- Architecture: estratégia de renderização (CSR/SSR/SSG/ISR) e os componentes principais — só depois de saber o que a etapa 1 exige.
- Data model: o que é estado de servidor (cópia cacheável de uma fonte externa) e o que é estado local (pertence só à sessão de UI); qual é a forma dos dados que os componentes consomem.
- Interface: o contrato entre componentes e entre o front-end e o backend — props, eventos, formato de resposta da API.
- Optimizations: performance (Core Web Vitals), acessibilidade e segurança — tratadas como parte do desenho, não como revisão de última hora.
Essa ordem importa porque o navegador executa em uma sequência concreta: primeiro chega uma resposta HTML/JS (que depende da estratégia de renderização escolhida na etapa 2); só então componentes recebem dados (etapa 3) através de um contrato (etapa 4); e só faz sentido otimizar (etapa 5) o que já foi definido nas etapas anteriores. Otimizar uma arquitetura errada é desperdício.
Fluxo de decisão: do requisito à evidência
Requisitos (usuário anônimo, mobile, feed com atualização periódica) levam a uma escolha de arquitetura (SSR para a primeira página, CSR para paginação seguinte). O modelo de dados separa posts do servidor (cacheáveis) de interações locais (like otimista). A interface define o contrato da API de paginação. As otimizações cobrem Core Web Vitals e acessibilidade. A evidência fecha o ciclo com métricas de campo.
Diagrama 1 — A decisão relevante é a fronteira entre o que é servido antes da interação (requisitos → arquitetura → dados → interface) e o que prova que a escolha funciona na prática (otimizações → evidência).
O mesmo fluxo, em detalhe
O diagrama abaixo aplica o método RADIO de forma genérica — a sequência que vale para qualquer estudo de caso desta trilha, não só o feed. Leia da esquerda para a direita: cada etapa produz uma decisão testável; a segunda linha mostra o risco concreto de pular cada etapa.
Diagrama 2 — Use-o na questão 1 e no exercício guiado: identifique em qual etapa do RADIO cada decisão do exercício se encaixa.
Orçamento: performance também é uma hipótese numérica, não um adjetivo
"Precisa ser rápido" não é um requisito verificável. Use os limiares perceptuais humanos como ponto de partida: respostas até 0,1 s parecem instantâneas; até 1 s o usuário nota o atraso mas não perde o fio do pensamento; até 10 s ele ainda espera, mas precisa de feedback visível de progresso (Nielsen Norman Group). Isso vira orçamento de UI: uma interação de "curtir" deve responder visualmente em menos de 0,1 s (via estado otimista local), enquanto carregar a próxima página do feed pode tolerar até ~1 s antes de precisar de um indicador de carregamento.
Do lado da rede, transforme "internet lenta" em número: um payload JSON de 200 KB em uma conexão 3G efetiva (~400 Kbps úteis) leva perto de 4 segundos só de transferência, antes de qualquer parsing ou renderização. Se o requisito diz "funciona em 3G", isso limita imediatamente o tamanho do payload inicial e empurra a decisão para paginação, compressão e code splitting — não para "adicionar mais cache depois".
Qualidade tem contrato — Core Web Vitals, acessibilidade e segurança
- Carregamento (LCP): tempo até o maior elemento visível da tela renderizar. Mede a percepção de "a página chegou".
- Estabilidade visual (CLS): soma de deslocamentos inesperados de layout. Mede se o conteúdo "pula" enquanto carrega.
- Responsividade (INP): latência entre uma interação e a próxima pintura que reflete o resultado dela. Mede se a UI trava ao ser usada.
Essas três métricas são os Core Web Vitals oficiais do Google, medidos preferencialmente com dados reais de campo (RUM) via Chrome UX Report — não apenas em um teste de laboratório isolado. Trate metas como "LCP p75 abaixo de 2,5 s no mobile" como um SLO de front-end: específico, medido em produção, e com dono.
Acessibilidade segue o mesmo princípio: WCAG 2.2 é uma recomendação normativa do W3C com critérios de sucesso verificáveis (W3C WCAG 2.2) — não uma revisão visual subjetiva de última hora. E segurança no navegador não é um adendo: qualquer tela que renderiza texto vindo de um servidor ou de outro usuário carrega risco de XSS por padrão, e isso é tratado nas etapas de "Otimizações" do RADIO, não depois do deploy.
Alternativas que falham — e o que fazer no lugar
Escolher a estratégia de renderização antes dos requisitos
"Vamos usar CSR porque é mais simples" ou "vamos usar SSR porque é mais rápido" são afirmações sem contexto. CSR pode ser exatamente certo para um painel interno logado sem necessidade de SEO; SSR pode ser obrigatório para uma página de produto pública que precisa ser indexada. A pergunta certa é: quem acessa, com qual objetivo de SEO/latência, e qual é o custo de operar cada opção.
Tratar todo dado como estado global
Colocar a resposta de uma API de busca no mesmo lugar (e com a mesma ferramenta) que o estado de "o menu está aberto" produz bugs de invalidação: um é uma cópia cacheável de uma fonte externa, o outro pertence só à sessão de UI. Essa distinção será a base da aula sobre modelo de dados.
Adicionar acessibilidade e performance no fim
Revisar Core Web Vitals ou leitura por leitor de tela só antes do lançamento costuma exigir reescrever decisões de arquitetura (ex.: dividir um bundle que já ficou grande, ou trocar uma div clicável por um elemento semântico). Tratar essas exigências como parte da etapa "Optimizations" do RADIO — durante o desenho, não depois — custa muito menos.
Prometer "vai escalar" sem medir nada
Sem uma métrica de campo (RUM) ligada à decisão, não há como saber se ela funcionou nem como defendê-la em uma revisão. Toda decisão de arquitetura de front-end desta trilha termina com uma evidência: uma métrica, um teste ou uma medição real, não uma opinião.
Prática guiada: busca com sugestões (autocomplete)
Raciocínio antes do código
O requisito central é a correção temporal: a última tecla digitada deve "vencer" qualquer resposta anterior que chegue depois dela, mesmo fora de ordem (uma resposta de rede pode chegar atrasada). Isso não é um detalhe de implementação — é a invariante que a arquitetura precisa proteger, do mesmo jeito que a aula de system design de backend protege "duas pessoas não podem reservar o mesmo horário".
Uma abordagem plausível, mas incorreta
Disparar uma requisição a cada tecla digitada e simplesmente renderizar cada resposta assim que ela chega parece funcionar em um teste manual — mas quebra sob rede real, onde requisições podem chegar fora de ordem.
// Incorreto: nenhuma proteção contra resposta desatualizada.
input.addEventListener('input', async (e) => {
const query = e.target.value;
const res = await fetch(`/api/suggest?q=${query}`);
const items = await res.json();
renderList(items); // pode renderizar uma resposta antiga sobre uma mais nova
});
O problema não é chamar fetch a cada tecla; é não haver nenhum mecanismo que descarte uma resposta que ficou obsoleta porque uma tecla mais recente já foi digitada.
Uma solução correta, com a invariante explícita
Debounce reduz o número de requisições (otimização de rede), mas a correção vem de duas coisas: um AbortController para cancelar a requisição anterior, e uma verificação de que a resposta corresponde à última consulta antes de renderizar.
let controller;
let latestQuery = '';
const debouncedSuggest = debounce(async (query) => {
controller?.abort(); // cancela a requisição em voo anterior
controller = new AbortController();
latestQuery = query;
try {
const res = await fetch(`/api/suggest?q=${encodeURIComponent(query)}`, { signal: controller.signal });
const items = await res.json();
if (query !== latestQuery) return; // resposta obsoleta, mesmo sem abort
renderList(items);
} catch (err) {
if (err.name !== 'AbortError') showFallback(); // falha real: degradar, não travar
}
}, 200);
function renderList(items) {
list.replaceChildren(
...items.map((text) => {
const li = document.createElement('li');
li.textContent = text; // nunca innerHTML com texto vindo do servidor
li.setAttribute('role', 'option');
return li;
})
);
}
Dois detalhes não são estilo, são a correção e a segurança da solução: (1) a verificação query !== latestQuery garante a invariante mesmo em condições de corrida que o abort não cobre sozinho (por exemplo, uma resposta que já estava em processamento no navegador); (2) renderList usa textContent e createElement, nunca concatenação de string em innerHTML, porque as sugestões vêm de uma fonte externa (a API) e podem conter texto controlado por outro usuário (ex.: título de um produto cadastrado por terceiros) — inserir isso via innerHTML abriria uma injeção de XSS refletida no seu próprio catálogo (CWE-79). Content-Security-Policy é uma defesa em profundidade complementar aqui, não um substituto: mesmo com CSP, a saída ainda deve ser tratada como dado, não como marcação confiável (OWASP XSS Prevention Cheat Sheet).
Uma solução possível, de ponta a ponta
- Requisitos: resposta percebida < 0,1 s ao digitar (feedback local imediato de "buscando"); nenhuma sugestão desatualizada renderizada; navegável por teclado; degrada sem travar se a API falhar.
- Arquitetura: CSR para o componente de busca (é interativo e depende de estado de sessão), independente da estratégia de renderização do resto da página.
- Dados: lista de sugestões é estado de servidor (derivado da última consulta válida); o texto digitado é estado local do input.
- Interface: contrato
GET /api/suggest?q=retornando uma lista de strings; input comrole="combobox", lista comrole="listbox"/optionpara leitor de tela e navegação por teclado. - Otimizações: debounce de ~200 ms, cancelamento via
AbortController, deduplicação de consulta repetida, renderização segura viatextContent/DOM, CSP como camada adicional. - Evidência: métrica de RUM para tempo até a primeira sugestão renderizada; teste automatizado que digita rápido e verifica que apenas a última resposta é exibida; teste de teclado (setas + Enter + Escape).
Verificação: um teste deve simular duas respostas chegando fora de ordem (a mais antiga chegando depois da mais nova) e provar que a lista final reflete a consulta mais recente. Um teste de falha deve simular a API respondendo com erro e verificar que a UI mostra um estado de fallback em vez de travar ou lançar uma exceção não tratada.
Recupere da memória: 10 questões
Recupere da memória: 10 questões
Por que front-end system design não é “system design mais fácil”?
Na ordem do RADIO, o que vem antes de “Architecture”?
Uma página pública que precisa ser indexada por buscadores pesa a favor de qual decisão inicial?
Que limiar de tempo a Nielsen Norman Group associa a “parece instantâneo”?
O que o INP mede?
Por que separar estado de servidor de estado local?
No exercício de autocomplete, qual mecanismo evita renderizar uma resposta desatualizada?
Por que usar textContent/createElement em vez de innerHTML para renderizar sugestões vindas da API?
Qual é o papel do CSP nesse cenário?
Qual decisão mais caracteriza a etapa “Optimizations” do RADIO?
Perguntas de entrevista e de revisão
"Por onde você começa uma entrevista de front-end system design?" Resposta forte: esclareça requisitos antes de falar de tecnologia — quem usa, em que dispositivo/rede, se há necessidade de SEO ou tempo real — e só então proponha arquitetura, seguindo RADIO. Mostre que sabe quando pedir mais contexto em vez de assumir.
"Quando você usaria SSR em vez de CSR?" Não responda "SSR é sempre melhor para performance". Explique o trade-off: SSR ajuda tempo até conteúdo visível e SEO, ao custo de processamento por requisição no servidor; CSR pode ser preferível para telas altamente interativas e logadas onde SEO não importa.
"Como você garante que uma UI de busca não trava com digitação rápida?" Cite debounce para reduzir chamadas, AbortController para cancelar requisições obsoletas, e uma verificação explícita de que a resposta corresponde à consulta mais recente — a invariante real é ordem, não só volume de chamadas.
"O que muda quando você passa de Senior para Principal Front-end?" Diga que a unidade de decisão deixa de ser um componente ou uma tela e passa a ser plataforma: contratos entre times de front-end, golden paths, custo agregado de decisões repetidas, e mecanismos para que outras equipes tomem boas decisões sem centralizar toda revisão em uma pessoa.
Feche o modelo mental
Front-end system design começa no comportamento humano observando a tela e termina em evidência de produção. Um bom primeiro desenho separa requisito de tecnologia, escolhe renderização a partir de necessidade real (não modismo), distingue estado de servidor de estado local, trata performance/acessibilidade/segurança como parte do contrato, e sempre aponta para uma métrica ou teste que prova que funcionou.
Próxima aula planejada: RADIO em profundidade — como conduzir cada etapa numa entrevista real.
Referências primárias para aprofundar
O framework usado nesta aula é o RADIO, descrito no GreatFrontEnd Front End System Design Playbook e no System Design Handbook — Frontend System Design. Os limiares de tempo de resposta percebido vêm do artigo "Response Times: The 3 Important Limits" da Nielsen Norman Group. A definição oficial de Core Web Vitals está em web.dev: Web Vitals. Acessibilidade segue a W3C WCAG 2.2. A prevenção de XSS no exercício segue o OWASP XSS Prevention Cheat Sheet, complementado pela especificação de Content Security Policy Level 3. Fundamentos do caminho crítico de renderização citados na trilha vêm da documentação da MDN.
Consulte também as fontes fundamentais da trilha e o catálogo completo de recursos, disponíveis no repositório do curso.