Apostila completa de DevTools & Ferramental de Debug
Depurar não é espalhar console.log e torcer. É um método — reproduzir, isolar, formular hipótese, testar — apoiado por ferramentas que mostram o estado real do sistema: o DOM, a rede, a memória, a pilha de chamadas, o perfil de CPU. Esta apostila ensina o DevTools do browser a fundo, o debugger do editor e do Node, os DevTools de frameworks, e o ferramental de rede, bundle e diagnóstico — com o olho no que empresas esperam de um dev que "sabe se virar".
Depuração como método
Objetivo: transformar "depurar" de tentativa-e-erro num processo repetível — reproduzir, isolar, hipótese, testar — e entender por que console.log às cegas é o caminho lento.
1.1 O método científico aplicado a bugs
1. REPRODUZIR ── consigo fazer o bug acontecer de forma consistente?
(passos exatos, dados exatos, ambiente exato)
2. ISOLAR ── qual a MENOR parte do sistema que ainda exibe o bug?
(remover metade de cada vez — bisseção)
3. HIPÓTESE ── "acho que é X porque Y". Uma afirmação FALSIFICÁVEL.
4. TESTAR ── um experimento que confirma OU refuta a hipótese.
(não vários ao mesmo tempo)
5. ── confirmou? conserte, e escreva um TESTE que reproduz (não repete)
── refutou? volte ao passo 3 com o que aprendeu
O erro nº 1 ao depurar sob pressão é mudar cinco coisas, o bug sumir, e você não saber qual resolveu (nem se voltará). Mude uma coisa, observe, reverta se não ajudou. É lento? Não — é o caminho rápido; o caminho lento é o "chute → chute → chute" que às vezes esconde o bug em vez de corrigi-lo.
1.2 Reprodução mínima
Um bug que só acontece "às vezes, na produção, com esse cliente" é quase impossível de corrigir. O trabalho é reduzi-lo a uma reprodução mínima e determinística: o menor código/estado/passos que ainda falha. Benefícios: você entende a causa no processo de reduzir; fica trivial de testar a correção; e vira um caso de teste. Ferramentas: codesandbox/stackblitz para front, um script isolado para back, um curl para uma API.
1.3 console.log vs o debugger
console.log espalhado | Breakpoint / debugger |
|---|---|
| Você adivinha onde olhar e o quê imprimir; erra, adiciona mais, recarrega | Para no ponto e você inspeciona tudo — variáveis locais, escopo, this, call stack — sem prever |
| Ciclo: editar → salvar → recarregar → ler o console | Ciclo: pausou → olhou → seguiu (step) — sem recompilar |
| Fica no código (você esquece um em produção) | Não toca o código; logpoint imprime sem editar o arquivo |
| Útil para: fluxo em produção, timing, loops muito rápidos, logs estruturados | Útil para: entender estado, condições, "por que entrou/não entrou aqui" |
console.log tem lugar (Módulo 2 mostra como usá-lo bem), mas se a sua ferramenta principal é adicionar console.log, recarregar e ler — você está deixando 80% do poder do DevTools na mesa.
1.4 Outras técnicas que economizam horas
- Rubber duck: explicar o problema em voz alta (para um colega, ou um pato de borracha) força você a articular as premissas — e a errada aparece.
git bisect(Módulo 9): "funcionava na semana passada" → o git acha o commit exato que quebrou, por busca binária.- Ler a mensagem de erro inteira — o stack trace, a linha, o "caused by". A maioria diz exatamente o que é.
- Reverter para um estado bom conhecido e reaplicar em pedaços.
- Verificar as premissas: o arquivo que você editou é o que está rodando? o cache? a versão certa? a branch certa? o ambiente certo?
Entrevistas de debugging existem (às vezes ao vivo, com um bug plantado). Avaliam o processo: você reproduz antes de mexer? forma hipóteses testáveis? usa o debugger ou só console.log? isola em vez de chutar? Descrever o método (reproduzir → isolar → hipótese → testar → teste de regressão) já sinaliza senioridade. "Eu ia colocando console.log até achar" é resposta de júnior.
✏️ Exercício 1 — Estruture a investigação
"O botão de salvar não funciona para alguns usuários." É tudo que você tem. Escreva as 5 primeiras perguntas/passos, em ordem.
Gabarito (exemplo): (1) Reproduzir: consigo fazer acontecer? Com qual conta, qual dado, qual navegador, qual passo exato? Se não reproduz, coletar do usuário: vídeo, console, network (HAR), navegador/SO. (2) Delimitar "não funciona": o clique não dispara nada? dispara e dá erro? salva mas some? — abrir o DevTools: Console tem erro? Network mostra a request? Qual status? (3) Isolar a fatia de usuários: o que "alguns usuários" têm em comum? plano, permissão, feature flag, região, dado grande, caractere especial, timezone? (4) Hipótese falsificável: ex. "falha quando o campo X tem mais de N caracteres e o backend retorna 422". (5) Testar: reproduzir com esse dado específico; olhar a resposta da API no Network; confirmar. Depois: corrigir + teste de regressão com o caso.
DevTools — Elements & Console
Objetivo: inspecionar e editar o DOM e o CSS ao vivo, entender o box model e a especificidade no painel, e usar o Console como um REPL poderoso — não só como "onde os logs aparecem".
2.1 Abrir e navegar
- F12 ou Ctrl/Cmd+Shift+I; Ctrl/Cmd+Shift+C abre já no modo "inspecionar elemento" (clique num elemento da página).
- Ctrl/Cmd+Shift+P — o Command Menu: digite qualquer coisa ("screenshot", "coverage", "disable cache", "show rulers") — é o atalho para tudo.
- Docking: painel embaixo, à direita, ou janela separada (útil em telas pequenas ou depurando o próprio layout).
- Funciona igual em Chrome, Edge e (com nomes um pouco diferentes) Firefox. Este material usa a nomenclatura do Chrome/Edge; a lógica transfere.
2.2 Elements: DOM e CSS ao vivo
- Editar o DOM: duplo-clique num texto/atributo para editar; Enter confirma; botão direito → "Edit as HTML" para blocos; arrastar para reordenar; Delete remove. Tudo ao vivo, sem recarregar (perde-se no reload).
- Styles: cada regra CSS que se aplica, na ordem de cascata; riscadas = sobrescritas; ao lado, o arquivo:linha de origem (clicável). Marque/desmarque propriedades, edite valores (setas ↑↓ incrementam; Shift+seta = ±10). O bloco
element.style { }no topo é o estilo inline que você adicionou. - Computed: o valor final resolvido de cada propriedade, e — expandindo — de qual regra ele veio. É onde você descobre "por que esse texto está vermelho".
- Box model (aba Computed ou o diagrama): content / padding / border / margin com os valores; passe o mouse para ver realçado na página.
- Estados forçados (
:hov): forçar:hover,:focus,:active,:focus-withinpara estilizar/depurar estados sem precisar segurar o mouse. - Especificidade: a ordem das regras no painel já reflete a cascata; a regra que vence está no topo, sem riscos. Se a sua regra está riscada, outra é mais específica (ou vem depois, ou tem
!important). - Layout / Flexbox / Grid: badges
flex/gridao lado do elemento no DOM; clicar sobrepõe as linhas do grid/flex na página. A aba Layout lista todos os grids/flex da página. - Event Listeners (aba ao lado de Styles): todos os handlers ligados ao elemento (e aos ancestrais), com o arquivo:linha — ótimo para "o que roda quando eu clico aqui?".
2.3 O Console como REPL
// helpers que só existem no Console: $0 // o elemento selecionado agora no painel Elements ($1 = o anterior, etc.) $('.btn') // = document.querySelector('.btn') (só no Console, não é jQuery) $$('li') // = [...document.querySelectorAll('li')] (array de verdade) $x('//a[@href]') // consulta por XPath inspect($0) // abre o elemento/objeto no painel apropriado getEventListeners($0) // os listeners do elemento, como objeto copy(objetoOuTexto) // copia para o clipboard (ótimo p/ um JSON grande) monitorEvents($0, 'click') // loga cada evento; unmonitorEvents($0) para parar monitor(minhaFn) // loga cada chamada da função e seus argumentos queryObjects(Promise) // todas as instâncias vivas de um construtor
| Método | Para quê |
|---|---|
console.table(arr) | Array de objetos como tabela ordenável — muito melhor que log |
console.dir(el) | Um elemento DOM como objeto (propriedades) em vez de como HTML |
console.group() / groupEnd() | Agrupar logs relacionados (recolhível) |
console.assert(cond, msg) | Loga só se a condição for falsa |
console.count(label) / countReset | Quantas vezes esta linha foi executada |
console.time(l) / timeEnd(l) | Medir a duração de um trecho |
console.trace() | Imprime o stack trace de como chegou aqui |
%c em console.log('%ctexto', 'color:red') | Estilizar a saída |
- Live Expressions (o "olho" 👁 na barra do Console): fixa uma expressão que é reavaliada continuamente no topo — ex.:
document.activeElement,store.getState().user,performance.memory.usedJSHeapSize. - Filtros: por nível (Error/Warning/Info/Verbose), por texto, por contexto (top vs iframe), e Console settings → "Preserve log" (mantém entre navegações), "Selected context only".
- Console + qualquer painel: Esc abre o "drawer" do Console sobreposto a qualquer aba (você pode ver o Elements e o Console juntos).
Quando você faz console.log(obj) e clica para expandir depois, o DevTools mostra o estado do objeto no momento da expansão, não do log — se ele mudou nesse meio-tempo, você vê o valor novo e se confunde. Para congelar: console.log(structuredClone(obj)) ou console.log(JSON.parse(JSON.stringify(obj))), ou console.table.
Perguntas/tarefas ao vivo: "Por que este elemento está com essa cor/margem? mostre no DevTools" (Computed → de qual regra veio; ou Styles), "O que é $0?", "Como você lista todos os event listeners de um botão?" (aba Event Listeners ou getEventListeners($0)), "Como você inspeciona um estado :hover?" (forçar em :hov). Fluência aqui é esperada de qualquer front-end.
✏️ Exercício 2 — Investigue no Elements
Um link deveria ser azul e sublinhado, mas aparece preto e sem sublinhado. Descreva os passos no DevTools para descobrir por quê, sem tocar no código-fonte.
Gabarito: (1) Ctrl/Cmd+Shift+C e clicar no link → ele fica selecionado no Elements. (2) Aba Styles: procurar as regras que setam color e text-decoration; a que vence está no topo sem risco. Provavelmente há um a { color: inherit; text-decoration: none } (reset de design system) mais específico ou depois. (3) Aba Computed: expandir color e text-decoration-line → o DevTools mostra a regra exata e o arquivo:linha de origem. (4) Testar a hipótese: no bloco element.style, adicionar color: blue; text-decoration: underline — se resolver, confirma que era só o CSS sendo sobrescrito. (5) Conclusão: a correção é aumentar a especificidade da regra do link ou ajustar o reset — mas o diagnóstico saiu 100% do painel.
DevTools — Sources e breakpoints
Objetivo: parar o código no ponto certo com os vários tipos de breakpoint, inspecionar a pilha e o escopo, e usar source maps, blackboxing, snippets e local overrides.
3.1 O painel Sources
- Árvore de arquivos: Page (o que a página carregou), e — se houver source maps — os arquivos originais (TS, JSX, Sass) em "Authored" vs o bundle em "Deployed".
- Ctrl/Cmd+P abre qualquer arquivo por nome; Ctrl/Cmd+Shift+F busca em todos os arquivos carregados (ótimo para achar onde uma string ou seletor é usado).
- À direita: Call Stack, Scope (Local / Closure / Global), Watch, Breakpoints, e os breakpoints especiais (DOM, XHR, Event Listener, Exceptions).
3.2 Os tipos de breakpoint
| Tipo | Como | Quando |
|---|---|---|
| De linha | Clicar no número da linha (ou debugger; no código) | Parar toda vez que passar ali |
| Condicional | Botão direito na linha → Add conditional breakpoint → id === 42 | Loop com 5.000 iterações e só a 4.999ª te interessa |
| Logpoint | Botão direito → Add logpoint → 'valor', x, obj | Quer o efeito do console.log sem editar o arquivo nem parar |
| DOM | No Elements, botão direito no nó → Break on → subtree modifications / attribute modifications / node removal | "Quem está mexendo neste elemento?" — para no JS que causou a mutação |
| XHR/fetch | Painel Sources → XHR/fetch Breakpoints → + → um pedaço da URL | Para logo antes de uma requisição sair — vê a stack de quem a disparou |
| Event Listener | Sources → Event Listener Breakpoints → marcar "click", "keydown", "load"… | "O que roda quando o usuário clica/pressiona X?" |
| Exceptions | Botão ⏸ com o octógono → "Pause on uncaught exceptions" (e opcionalmente "caught") | Para no momento em que o erro é lançado, com o estado intacto |
3.3 Controlar a execução
| Ação | Atalho | Faz |
|---|---|---|
| Resume | F8 | Continua até o próximo breakpoint |
| Step over | F10 | Executa a linha; não entra em funções chamadas |
| Step into | F11 | Entra na função chamada na linha atual |
| Step out | Shift+F11 | Roda até sair da função atual |
| Deactivate breakpoints | Ctrl/Cmd+F8 | Ignora todos temporariamente sem apagá-los |
- Call Stack: clique em qualquer quadro para ver o escopo daquele nível (variáveis locais de quem chamou). "Restart frame" re-executa a função atual do começo.
- Scope: edite valores de variáveis ao vivo (duplo-clique) para testar "e se fosse outro valor?".
- Watch: adicione expressões (
this.state,items.length) reavaliadas a cada passo. - Console durante a pausa: tudo que você digita no Console roda no escopo do frame selecionado — chame funções, inspecione, teste correções.
3.4 Source maps
Seu código roda como um bundle minificado (app.a3f9.js), mas você escreveu TypeScript/JSX. O source map (.map) mapeia cada posição do bundle para o arquivo/linha/coluna original — o DevTools mostra e depura o seu código. Garanta que o build gera source maps (em dev sempre; em produção, considere hidden source maps enviados só ao Sentry). Se você vê código minificado no debugger, o source map não carregou (404, caminho errado, ou desligado).
3.5 Recursos que economizam tempo
- Blackboxing / "Ignore list": botão direito num arquivo (ou padrão em Settings) → "Add script to ignore list" — o step pula bibliotecas (React, lodash, polyfills) e o stack trace as esconde. Você depura só o seu código.
- Pretty print (
{ }no rodapé): re-indenta um arquivo minificado quando não há source map. - Snippets (Sources → Snippets): trechos de JS reutilizáveis que você roda em qualquer página com Ctrl/Cmd+Enter — ótimo para bookmarklets de debug, checagens de acessibilidade, etc.
- Local Overrides (Sources → Overrides → escolher uma pasta): o DevTools persiste suas edições de qualquer arquivo (CSS, JS, até respostas de rede) e as reaplica a cada reload — você testa uma correção num site que não é seu, ou itera CSS sem tocar no projeto. Também dá para sobrescrever headers de resposta.
- Workspaces (conectar uma pasta local): editar no DevTools salva no arquivo do disco — Elements/Sources viram um mini-editor sincronizado.
Perguntas: "Diferença entre step over e step into", "Como você para o código só quando x === 42?" (conditional breakpoint), "Um elemento some da tela e você não sabe qual código removeu — como descobre?" (DOM breakpoint em "node removal"), "Como depurar só o seu código ignorando as libs?" (blackboxing/ignore list), "Você vê código minificado no debugger — o que houve?" (source map não carregou). Saber logpoint e conditional breakpoint em vez de editar o arquivo é sinal de fluência.
✏️ Exercício 3 — "Uma request errada está saindo"
A aba Network mostra uma chamada POST /api/track com um corpo estranho, e você não sabe de onde ela vem. Descreva como achar o código responsável.
Gabarito: (1) Sources → XHR/fetch Breakpoints → + → digitar /api/track. (2) Reproduzir a ação → o debugger pausa no momento em que a request vai sair. (3) Olhar o Call Stack — o frame de cima é quem chamou fetch; descer pelos frames revela a cadeia (um handler de clique? um useEffect? um SDK de analytics?). Se houver muito ruído de biblioteca, adicionar os arquivos das libs à ignore list para o stack ficar só com o seu código. (4) No Scope/Console do frame, inspecionar os argumentos passados ao fetch para ver como o corpo estranho foi montado. (5) Alternativa: na aba Network, coluna Initiator já mostra a stack resumida da origem da request — às vezes basta.
DevTools — Network
Objetivo: ler o waterfall e o timing de cada request, simular condições de rede, bloquear e reproduzir requisições, e exportar para investigação.
4.1 A tabela e o waterfall
- Colunas úteis (botão direito no cabeçalho para escolher): Status, Type, Initiator (quem disparou), Size (transferido vs descomprimido — "(disk cache)" / "(memory cache)"), Time, Priority, Waterfall.
- Waterfall: a barra de cada request no tempo. Passe o mouse para o breakdown: Queueing, Stalled, DNS, Initial connection / SSL, Request sent, Waiting (TTFB) (o servidor pensando), Content Download. TTFB alto = backend/rede; Download alto = payload grande ou rede lenta.
- Filtros: por tipo (Fetch/XHR, JS, CSS, Img, Doc, WS, Manifest…), por texto, e a barra de filtro avançada:
status-code:500,domain:api.exemplo.com,larger-than:100k,method:POST,-domain:*.google.com(o-exclui),has-response-header:cache-control,mime-type:application/json. - Preserve log: mantém as requests entre navegações (essencial para depurar login/redirect). Disable cache (só com o DevTools aberto): força buscar tudo da rede.
4.2 Detalhes de uma request
Clique numa linha:
- Headers: URL, método, status, e todos os headers de request e response. "Payload" mostra o corpo enviado (form data ou JSON, já parseado). "Response" mostra o corpo recebido; "Preview" o renderiza (JSON como árvore, HTML, imagem).
- Timing: o breakdown detalhado daquela request.
- Cookies: os enviados e recebidos.
- Initiator: a stack de chamadas que originou a request (clicável até o seu código).
4.3 Ferramentas de reprodução e teste
| Ação (botão direito na request) | Uso |
|---|---|
| Copy → Copy as cURL / as fetch / as PowerShell | Reproduzir a request exata no terminal ou colar num arquivo — com todos os headers e cookies |
| Copy → Copy response | Pegar o JSON de resposta para inspecionar/mockar |
| Replay XHR | Re-disparar a mesma request a partir do DevTools |
| Block request URL / domain | Simular a falha de um recurso (um script de terceiro, uma API) e ver como a página se comporta |
| Override content (via Local Overrides) | Substituir a resposta de uma API por um JSON seu, sem backend — testar estados de erro/vazio |
| Save all as HAR | Exportar toda a sessão de rede (um .har) para anexar a um bug, ou importar de volta / analisar em outra ferramenta. Contém cookies e tokens — trate como sensível |
4.4 Throttling
- Perfis prontos ("Slow 4G", "Fast 4G", "Offline") ou custom (latência, download, upload). Testar como o app se comporta em rede ruim — spinners, timeouts, retries, offline.
- Combine com CPU throttling (no painel Performance) para simular um celular modesto.
4.5 WebSockets, SSE e outros
- Filtro WS: clique numa conexão WebSocket → aba Messages mostra cada frame enviado/recebido, com direção e timestamp.
- Server-Sent Events aparecem como uma request que não "fecha"; a aba EventStream mostra os eventos.
Perguntas: "O que significa TTFB alto?" (o servidor demorou a começar a responder — backend ou rede, não o download), "Como você reproduz uma request da aba Network no terminal?" (Copy as cURL), "Como testar como a página reage se uma API cai?" (Block request, ou override da resposta com um erro), "O que 'Preserve log' resolve?" (depurar fluxos com redirect/navegação), "Como exportar a rede para um bug report?" (HAR — e cuidado, tem tokens).
✏️ Exercício 4 — Página lenta ao carregar
Uma página demora ~6s para ficar utilizável. Descreva o que você olha na aba Network para diagnosticar.
Gabarito: (1) Recarregar com Disable cache e o Network aberto; ordenar por Time ou olhar o Waterfall. (2) Identificar o gargalo: uma request com Waiting (TTFB) enorme = backend lento numa API crítica no caminho. Requests grandes em Size = bundle/imagem sem otimização (comprimir, code-split, lazy). Muitas requests em série (cada uma começa quando a anterior termina) = waterfall de dependências — paralelizar/pré-carregar. (3) Ver a linha do DOMContentLoaded (azul) e Load (vermelha) no waterfall: o que acontece depois delas e ainda bloqueia o "utilizável". (4) Filtrar por Type: Doc/JS/CSS para achar recursos render-blocking. (5) Cruzar com o painel Performance (Módulo 5) para ver se é rede ou execução de JS (main thread travada). (6) Initiator das requests tardias: alguma é disparada por um script de terceiro pesado que dá para adiar?
DevTools — Performance e Web Vitals
Objetivo: gravar e ler um profile de performance — flame chart, main thread, long tasks, layout/paint — e medir as Core Web Vitals (LCP, INP, CLS) dentro do DevTools.
5.1 As Core Web Vitals
| Métrica | Mede | Bom |
|---|---|---|
| LCP (Largest Contentful Paint) | Quando o maior elemento de conteúdo aparece — a percepção de "carregou" | ≤ 2,5 s |
| INP (Interaction to Next Paint) | A latência da pior interação (clique/tecla) até a tela atualizar — a percepção de "responde" | ≤ 200 ms |
| CLS (Cumulative Layout Shift) | Quanto o conteúdo "pula" durante o carregamento | ≤ 0,1 |
No DevTools: o painel Performance mostra LCP/CLS numa gravação; a seção "Live Metrics" (Performance, versões recentes) mede LCP, CLS e INP enquanto você usa a página; a extensão Web Vitals mostra num overlay. Lembre: o número que o Google usa (CrUX) vem de usuários reais — o do seu laptop é só indicativo.
5.2 Gravar um profile
- Painel Performance → escolher CPU throttling (4× ou 6× para simular celular) e Network throttling se o foco é carregamento.
- Para carregamento: botão de reload (⟳) grava desde o início. Para uma interação: Record → fazer a ação → Stop. Mantenha curto (poucos segundos).
5.3 Ler o profile
- Trilha da CPU / Network no topo: visão geral; um bloco vermelho no canto de uma tarefa = long task (> 50 ms — bloqueia a resposta a interações).
- Frames / FPS: barras verdes = frames renderizados; buracos = travadas (jank).
- Main (thread principal): o flame chart — a pilha de chamadas ao longo do tempo. Largura = duração; um bloco largo no topo é onde o tempo foi gasto. Cores: amarelo = Scripting (JS), roxo = Rendering (Style/Layout), verde = Painting, cinza = System/idle.
- Bottom-Up / Call Tree / Event Log (embaixo): agregações — "Self Time" (tempo na própria função, sem filhas) vs "Total Time". Ordenar por Self Time acha a função que ela mesma queima CPU.
- Marcadores: DCL, L (Load), FP/FCP, LCP na régua; Layout Shift aparece na trilha Experience; clicar mostra o elemento que pulou.
5.4 Os problemas clássicos e o que procurar
| Sintoma no profile | Causa provável |
|---|---|
| Bloco amarelo largo antes do primeiro paint | JS pesado bloqueando o render (bundle grande, parse/compile, trabalho síncrono no boot) |
| Muitos blocos roxos "Recalculate Style" / "Layout" | Layout thrashing: ler layout (offsetHeight) e escrever estilo em loop, forçando reflows sucessivos |
| "Layout" gigante ocasional | Mudança que afeta muitos elementos (uma classe no <body>) |
| Long tasks durante o scroll/digitação | Handler pesado; falta de debounce/throttle; render de lista sem virtualização |
| Paint frequente numa área grande | Animar propriedades que causam paint/layout (top, width) em vez de transform/opacity; ligar "Paint flashing" no painel Rendering |
| CLS: elemento pula quando uma imagem/fonte carrega | Sem width/height ou aspect-ratio na imagem; injeção de banner acima do conteúdo; font-display |
"Acho que este componente está lento" não é dado. Grave o profile, ache o bloco largo, olhe o Self Time no Bottom-Up. Frequentemente o culpado não é o que você imaginava (é um JSON.parse de 2 MB, um date-fns chamado 10.000 vezes num render, uma lib de terceiro). Otimize o que o profiler aponta; re-meça para confirmar o ganho.
Perguntas: "O que são LCP, INP e CLS?", "Como você investiga uma página que trava ao digitar?" (Performance → gravar a interação → long tasks no Main → Bottom-Up por Self Time), "O que é layout thrashing e como aparece no profile?", "Por que animar transform e não top?" (evita layout/paint; fica no compositor), "Diferença entre Self Time e Total Time". Cruza com a apostila de CSS Avançado e de Observabilidade.
✏️ Exercício 5 — Jank ao abrir um menu
Ao clicar num botão que abre um menu suspenso com ~200 itens, a UI congela por ~400 ms. Descreva a investigação e as hipóteses.
Gabarito: (1) Performance → CPU throttling 4× → Record → clicar no botão → Stop. (2) Achar a long task logo após o evento de clique no Main. (3) Olhar o flame chart: um bloco largo de Scripting = construir/renderizar os 200 itens de uma vez (React reconciliando 200 nós, ou criar 200 elementos DOM em loop). Um bloco largo de Layout/Recalculate Style depois = inserir os 200 no DOM força um reflow grande, talvez agravado por CSS custoso (sombras, filtros) por item. (4) Bottom-Up por Self Time confirma quem gasta. Hipóteses/correções: virtualizar a lista (renderizar só os ~15 visíveis — react-window/@tanstack/virtual); adiar a montagem (abrir o container vazio e preencher em requestIdleCallback ou em chunks); simplificar o CSS por item; memoizar. Re-gravar para confirmar que a long task sumiu.
DevTools — Memory, Application e o resto
Objetivo: caçar vazamentos de memória com heap snapshots e allocation timeline, inspecionar storage/service workers/cache no painel Application, e conhecer Lighthouse, Coverage, Rendering, Animations e o painel Issues.
6.1 Memory — vazamentos de JS
Um memory leak no front: objetos que deveriam ser coletados continuam referenciados, e a heap cresce sem parar (a aba fica lenta, trava, ou o navegador mata a página). Ferramentas do painel Memory:
| Ferramenta | Uso |
|---|---|
| Heap snapshot | Uma "foto" de toda a memória JS. Tire uma, faça a ação suspeita N vezes (abrir/fechar um modal, navegar entre rotas), tire outra, e compare (modo Comparison): o que cresceu e não foi liberado é o vazamento. Procure "Detached" (nós DOM sem pai mas ainda referenciados por JS) e a coluna "Retained Size" |
| Allocation instrumentation on timeline | Grava as alocações ao longo do tempo; barras azuis que não ficam cinza (não foram liberadas) apontam o código que aloca e retém |
| Allocation sampling | Baixo overhead; mostra qual função alocou mais memória num período — bom para produção-like |
(1) Snapshot A. (2) Executar a ação suspeita ~10×, forçar GC (ícone de lixeira). (3) Snapshot B. (4) Ação ~10× de novo + GC. (5) Snapshot C. (6) No snapshot C, filtrar "Objects allocated between A and B" — o que sobreviveu duas rodadas de GC é quase certamente o leak. Causas típicas: addEventListener sem removeEventListener, timers (setInterval) nunca limpos, closures que capturam um objeto grande, caches/arrays globais que só crescem, e — em React — useEffect sem cleanup ou listeners registrados fora do ciclo.
6.2 Application — o estado do lado do cliente
- Storage: Local Storage, Session Storage, IndexedDB, Cookies — ver, editar e limpar cada entrada. "Clear site data" zera tudo (útil para testar o primeiro acesso).
- Service Workers: estado (activated/redundant), "Update on reload", "Bypass for network", "Unregister". Se "sua mudança não aparece", um service worker servindo do cache é o suspeito nº 1 — marque "Update on reload" ou "Bypass".
- Cache Storage: o que o service worker guardou; dá para deletar entradas.
- Manifest: o
manifest.jsondo PWA, com validação e um botão "Add to homescreen". - Background services: gravar Push, Notifications, Background Sync, Background Fetch para depurar.
- Frames: a árvore de iframes e os recursos de cada um.
6.3 Lighthouse
Aba Lighthouse: auditoria automatizada de Performance, Accessibility, Best Practices e SEO, com nota (0–100) e recomendações acionáveis ("elimine recursos que bloqueiam o render", "imagens sem dimensões", "contraste insuficiente"). Rode em modo incognito (extensões distorcem) e várias vezes (há variância). É um ponto de partida: a nota de Performance vem de uma simulação; use o painel Performance para o diagnóstico real, e o teste manual para acessibilidade (ver apostila de Acessibilidade & WCAG). Existe Lighthouse CI para gate no pipeline.
6.4 Coverage
Command Menu → "Show Coverage" → recarregar → mostra, por arquivo, quanto do CSS e JS foi realmente executado vs carregado. Barras vermelhas = código morto naquele carregamento — candidatos a code-splitting, lazy loading, ou remoção. Não é "cobertura de testes" — é "quanto do bundle a página usou".
6.5 Rendering
Command Menu → "Show Rendering". Ferramentas de diagnóstico visual:
- Paint flashing: pisca em verde as áreas repintadas — se a tela toda pisca a cada scroll, há um problema de paint.
- Layout Shift Regions: destaca em azul onde ocorre CLS.
- Frame Rendering Stats / FPS meter.
- Emular:
prefers-color-scheme,prefers-reduced-motion,forced-colors, deficiências de visão (protanopia, embaçado),printmedia — testar acessibilidade e o modo de impressão sem mudar as configs do SO. - Core Web Vitals overlay.
6.6 Outros painéis úteis
- Animations: captura e permite dar slow-motion, inspecionar e editar keyframes/timing de animações CSS e Web Animations — ótimo para acertar uma transição.
- CSS Overview: um relatório da página — todas as cores usadas, contrastes insuficientes, fontes, media queries, seletores não usados.
- Issues: avisos consolidados do navegador — cookies que vão quebrar (SameSite), mixed content, CSP, deprecações, problemas de acessibilidade detectáveis. Vale checar antes de cada release.
- Device Mode (ícone de celular): viewport responsivo, presets de dispositivo,
devicePixelRatio, emulação detouch, e captura de screenshot (inclusive de página inteira: Command Menu → "Capture full size screenshot"). - Recorder: grava um fluxo de cliques/digitação e o reproduz; exporta para Puppeteer ou Playwright (ver apostila de Testing & Automação) e mede a performance do fluxo.
- Security: estado do TLS, certificado, mixed content.
Perguntas: "Como você confirma e localiza um memory leak no front?" (heap snapshots comparados / three-snapshot; procurar Detached nodes e listeners/timers não limpos), "Minha alteração não aparece por mais que eu recarregue — o que pode ser?" (service worker no cache; Application → Update on reload / Bypass; ou cache HTTP), "Para que serve a aba Coverage?" (código não usado no carregamento → code-splitting), "Como testar dark mode / reduced-motion sem mexer no SO?" (Rendering → Emulate).
✏️ Exercício 6 — SPA que fica lenta com o tempo
Um app de dashboard fica progressivamente lento conforme o usuário navega entre as telas durante o dia, até travar. Descreva a investigação.
Gabarito: Cheira a memory leak acumulado a cada navegação de rota. (1) Painel Memory: three-snapshot — snapshot A na tela X; navegar X→Y→X ~10 vezes + forçar GC; snapshot B; repetir; snapshot C. (2) No snapshot final, ordenar por Retained Size e procurar: nós Detached (componentes desmontados cujo DOM ainda é referenciado), instâncias de componentes/stores que crescem em contagem, arrays/Maps que só aumentam. (3) Clicar num objeto suspeito → painel Retainers mostra quem o segura vivo (a cadeia de referências). Causas típicas num dashboard: setInterval de polling não limpo no unmount, addEventListener global (resize/scroll) sem remove, subscrições (WebSocket, event bus, RxJS) sem unsubscribe, cache de dados por rota que nunca expira, ou uma lib de gráfico que não faz destroy(). (4) Verificar no código os useEffect/ngOnDestroy/onUnmounted correspondentes. (5) Corrigir os cleanups, repetir o three-snapshot e confirmar que a heap volta ao baseline após GC.
Debuggers fora do browser
Objetivo: usar o debugger do editor (VS Code) com launch.json, depurar Node.js com --inspect e o DevTools, e conhecer os equivalentes em outras stacks.
7.1 O debugger do VS Code
O mesmo poder do DevTools (breakpoints, step, watch, call stack, debug console) dentro do editor, para backend, scripts, testes e — via extensões — frontend. Configura-se em .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug API",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/src/server.ts",
"runtimeArgs": ["--import=tsx"],
"envFile": "${workspaceFolder}/.env",
"console": "integratedTerminal"
},
{
"name": "Attach to :9229",
"type": "node",
"request": "attach",
"port": 9229
},
{
"name": "Debug current test",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/vitest",
"args": ["run", "${relativeFile}"],
"console": "integratedTerminal"
}
]
}
- launch = o VS Code inicia o processo; attach = o processo já está rodando (com
--inspect) e o VS Code se conecta. - Breakpoints: clicar na gutter; condicionais e logpoints (botão direito) iguais ao DevTools; "Break on exceptions" (painel Breakpoints).
- Debug Console: avalia expressões no frame pausado, como o Console do browser.
- JavaScript Debug Terminal (Command Palette): qualquer
node/npm/pnpmrodado nele já entra com o debugger anexado — semlaunch.json. - compounds: rodar backend + frontend juntos numa config só.
7.2 Node.js: --inspect
node --inspect app.js # abre o inspector na porta 9229; conecte o VS Code ou o Chrome node --inspect-brk app.js # pausa na 1ª linha — para depurar o boot / imports node --inspect=0.0.0.0:9229 app.js # dentro de um container (exponha a porta; NUNCA em produção pública) # com um runner: node --inspect-brk ./node_modules/.bin/vitest run NODE_OPTIONS='--inspect' npm start
- Chrome DevTools para Node: abra
chrome://inspect→ "Open dedicated DevTools for Node" → aparece o alvo; você tem Sources, Console, Memory (heap snapshots do servidor!) e Performance (CPU profile do servidor). - CPU profile de produção sem parar:
node --cpu-prof app.jsgera um.cpuprofileque abre no painel Performance; ounode --heap-prof. - Debug de testes: Jest/Vitest/Mocha rodam sob
--inspect-brk; ou a config "Debug current test" acima.
7.3 Outras stacks (o conceito transfere)
| Stack | Debugger |
|---|---|
| Python | debugpy + extensão do VS Code; breakpoint() (dispara o pdb/o debugger configurado); python -m pdb; ipdb |
| Go | delve (dlv debug, dlv test); integrado ao VS Code e GoLand |
| Java / Kotlin / C# | Debugger da IDE (IntelliJ, VS Code, Rider) via JDWP / o runtime .NET — breakpoints, hot reload, evaluate |
| Rust | CodeLLDB (LLDB) no VS Code; rust-lldb/rust-gdb |
| Contêiner | Expor a porta do inspector e attach; ou o "Dev Containers" do VS Code, que depura dentro do container |
| Remoto | VS Code Remote-SSH: editar e depurar num servidor como se fosse local |
A porta do inspector (9229) permite executar código arbitrário no processo. Nunca a exponha à internet nem a deixe ligada em produção. Em container de dev, mapeie só para localhost.
Perguntas: "Diferença entre launch e attach no launch.json", "Como você depura um teste específico com breakpoints?", "O que --inspect-brk faz de diferente de --inspect?" (pausa na 1ª linha), "Como pegar um CPU profile de um processo Node em produção?" (--cpu-prof / --prof / clinic.js, sem parar), "Dá para depurar dentro de um container?" (sim — expor a porta e attach, ou Dev Containers).
✏️ Exercício 7 — Bug só no backend, sob um endpoint específico
Um endpoint POST /orders às vezes retorna 500. Os logs mostram só "TypeError: cannot read property 'total' of undefined". Descreva o setup para depurar isso com o debugger, não com console.log.
Gabarito: (1) Rodar o servidor sob o debugger: config launch no launch.json apontando para o entrypoint, com envFile, ou usar o "JavaScript Debug Terminal" e npm run dev. (2) No painel Breakpoints, marcar "Uncaught Exceptions" (e "Caught" se o erro é capturado e re-lançado) — o debugger vai parar exatamente na linha do TypeError, com o estado intacto. (3) Reproduzir: disparar o POST /orders (via Postman/cURL — Módulo 9) com um payload que causa a falha. (4) Quando pausar: olhar o Scope local — qual variável é undefined? De onde ela deveria vir (um find que não achou? um await que resolveu null? um campo ausente no body)? Subir o Call Stack para ver o valor que entrou. (5) Formular a hipótese ("quando o pedido não tem itens, cart é undefined e cart.total explode"), confirmar com outro payload, corrigir (validação/guarda) e escrever um teste com esse payload.
DevTools de frameworks
Objetivo: as extensões e painéis específicos que mostram o que o DevTools genérico não vê — a árvore de componentes, os hooks, o estado, os re-renders e as queries.
8.1 React DevTools
Extensão (Chrome/Firefox) que adiciona duas abas:
- Components: a árvore de componentes React (não o DOM). Selecione um → veja props, state, hooks (na ordem, com o valor de cada), e o componente que o renderizou ("rendered by"). Edite props/state ao vivo. O ícone de "target" seleciona pelo elemento na página.
$rno Console = o componente selecionado. - Profiler: Record → interagir → Stop. Mostra cada commit (render) como uma barra; o flamegraph mostra quais componentes renderizaram e quanto tempo levaram; o ranked chart ordena pelos mais caros. Clicar num componente mostra "Why did this render?" (props mudaram? hook? state? o pai renderizou?). É a ferramenta para caçar re-renders desnecessários.
- Settings: "Highlight updates when components render" — pisca na tela o que re-renderiza; se um clique num botão pisca a árvore inteira, há um problema de memoização / contexto mal fatiado.
8.2 Vue DevTools
Extensão + o overlay standalone (@vue/devtools). Inspetor de componentes (props, data, computed, setup state — editáveis), a timeline (eventos, mutações do Pinia/Vuex, performance de componentes), inspetor de rotas (Vue Router), e o inspetor do Pinia/Vuex (estado, getters, histórico de mutações, time-travel).
8.3 Estado: Redux / Zustand / etc.
- Redux DevTools (extensão + middleware): cada action disparada, o diff do estado que ela causou, o estado completo em qualquer ponto, time-travel (voltar e reproduzir), e "dispatch" manual de actions. É o padrão-ouro de depuração de estado.
- Zustand, Jotai, XState se conectam à mesma extensão Redux DevTools (via middleware/devtools).
- MobX tem seu próprio; Recoil/Signals idem.
8.4 Data-fetching e roteamento
- TanStack Query Devtools (
<ReactQueryDevtools />ou o painel embutido): cada query com seu status (fresh/stale/fetching/inactive), a chave, os dados em cache, odataUpdatedAt, e botões para invalidar/refetch/resetar. Resolve "por que essa tela mostra dado velho?" na hora. - TanStack Router / React Router: devtools que mostram a rota casada, os params, os loaders e o estado de navegação.
- Apollo Client Devtools (GraphQL): o cache normalizado, as queries ativas, e um "GraphiQL" embutido para rodar queries contra o mesmo endpoint (ver apostila de API Design).
- Angular DevTools: árvore de componentes, o injector tree (DI), e um profiler de change detection.
Um bug de "a lista não atualiza" pode estar no DOM/CSS (Elements), na rede (Network — a request nem saiu, ou voltou vazia), no cache de dados (TanStack Query Devtools — a query está stale mas não refez), no estado (Redux DevTools — a action não disparou, ou o reducer não tratou), ou no render (React Profiler — o componente não re-renderizou porque uma prop é === à anterior). Saber qual ferramenta abrir para cada hipótese é o que torna a investigação rápida.
Perguntas de front sénior: "Como você descobre por que um componente re-renderiza?" (React Profiler → "Why did this render?"; ou "Highlight updates"), "A tela mostra dado desatualizado — como investiga?" (TanStack Query Devtools: status da query; ou Redux DevTools: a action e o diff), "Como você inspeciona os hooks de um componente e edita o state ao vivo?" (React DevTools → Components), "O que o Redux DevTools te dá além do estado?" (histórico de actions, diff, time-travel).
✏️ Exercício 8 — Formulário lento a cada tecla
Num formulário React, digitar num campo trava por ~100 ms por caractere. Descreva a investigação com as ferramentas certas.
Gabarito: (1) React DevTools → Settings → "Highlight updates": digitar e ver o que pisca. Se a árvore inteira (ou uma seção grande) pisca a cada tecla, o problema é re-render em cascata — o state do input está num nível alto (ou num Context) que re-renderiza todos os filhos. (2) Profiler: gravar a digitação de uma letra; ver o commit; o flamegraph mostra quais componentes renderizaram; clicar nos caros → "Why did this render?" (prop nova a cada render? função inline não memoizada? o Context inteiro mudou?). (3) Confirmar no painel Performance (Módulo 5) que a long task por tecla bate com o tempo do commit. Correções típicas: mover o state do campo para o componente mais próximo (ou usar componentes não controlados / ref), fatiar o Context, memo + useCallback/useMemo nas props passadas, ou useDeferredValue/debounce para trabalho derivado pesado (validação, filtro de lista). Re-gravar para confirmar.
Rede, bundles e diagnóstico além do browser
Objetivo: clientes de API, interceptação de tráfego e túneis, análise de bundle, monitoramento de erro no cliente, git bisect, e depuração de mobile.
9.1 Clientes de API
| Ferramenta | Nota |
|---|---|
| Postman | O mais completo (coleções, ambientes, testes, mocks, monitors) — e o mais pesado; requer conta/nuvem para o pleno uso |
| Insomnia | Mais leve; bom suporte a GraphQL e gRPC |
| Bruno | Open source, local-first: as requests são arquivos de texto (.bru) que vão para o git junto do projeto — sem nuvem, versionável, revisável em PR |
| Hoppscotch | Web, leve, open source |
HTTPie (http / https) | CLI com sintaxe humana: http POST api/orders name=Ana — ótimo para scripts e para colar num bug |
| curl | Universal; o "Copy as cURL" do Network (Módulo 4) gera exatamente a request do browser. Ver apostila de Linux & Shell |
| Editores | A extensão REST Client do VS Code (arquivos .http) e o cliente HTTP do JetBrains — requests versionadas ao lado do código |
Prefira ferramentas local-first (Bruno, .http) para as requests do projeto: elas entram no repositório, todo mundo tem a mesma coleção, e a revisão de PR pega mudanças de contrato. Postman/Insomnia brilham para exploração e para APIs de terceiros.
9.2 Interceptar e modificar tráfego
Quando o "Copy as cURL" e o Network não bastam — depurar um app mobile, um SDK opaco, ou modificar respostas de forma sistemática:
- mitmproxy (open source, CLI + web UI): proxy HTTPS que intercepta, mostra e permite modificar requests/responses, escrever scripts em Python para reescrever tráfego, e gravar/reproduzir. O padrão para inspecionar o que um dispositivo/app realmente envia.
- Proxyman (macOS/iOS, ótima UX) / Charles Proxy (clássico, multiplataforma, pago): mesma ideia com interface gráfica — breakpoints de rede, map local/remote, throttling, SSL proxying.
- Uso típico: configurar o proxy no Wi-Fi do celular + instalar o certificado da ferramenta → ver e alterar todo o tráfego HTTPS do dispositivo.
9.3 Túneis: expor o localhost
- ngrok / cloudflared (Cloudflare Tunnel) / localtunnel: dão uma URL pública HTTPS que aponta para o seu
localhost:3000. Para: testar webhooks (Stripe, GitHub, etc. — eles precisam de uma URL alcançável), mostrar um preview a alguém, testar em um dispositivo real, ou depurar OAuth callbacks. ngrok tem uma UI de inspeção das requests recebidas (parecida com o Network).
9.4 Análise de bundle
| Ferramenta | Uso |
|---|---|
| source-map-explorer | A partir do JS + source map de produção, um treemap de "de qual arquivo/módulo vem cada byte do bundle". Independente do bundler |
| vite-bundle-visualizer / rollup-plugin-visualizer | Treemap para projetos Vite/Rollup |
| webpack-bundle-analyzer | O clássico para Webpack |
| @next/bundle-analyzer | Para Next.js |
| Bundlephobia / pkg-size | "Quanto essa dependência vai adicionar ao meu bundle?" antes de instalar |
| Import Cost (extensão VS Code) | Mostra o tamanho de cada import ao lado da linha |
O que procurar: uma dependência gorda que dá para trocar (moment → date-fns/Temporal), um pacote inteiro importado para usar uma função (import _ from 'lodash' → import debounce from 'lodash-es/debounce'), duplicatas de uma lib em versões diferentes, polyfills desnecessários, e código que deveria ser lazy (ver Coverage, Módulo 6).
9.5 Monitoramento de erro no cliente
- Sentry (o mais comum), Bugsnag, Rollbar, Datadog RUM, Highlight, LogRocket (grava a sessão): capturam os erros JS que acontecem nos usuários reais — stack trace (com source maps enviados no build), navegador/SO, os "breadcrumbs" (cliques, requests, navegação) que antecederam, e frequentemente uma repetição da sessão. É o que transforma "alguns usuários" (Módulo 1) em "este erro, neste componente, nestes navegadores, desde o deploy de terça". Cruza com a apostila de Observabilidade & SRE.
- Configure o upload de source maps no CI (e mantenha-os privados) para os stack traces virem legíveis.
9.6 git bisect — o debugger do histórico
git bisect start git bisect bad # o commit atual está quebrado git bisect good v2.3.0 # este era bom # o git faz checkout no meio do caminho; você testa e diz: git bisect good / git bisect bad # ... repete (busca binária) até: # "abc123 is the first bad commit" git bisect reset # automático, se você tem um teste/script que retorna 0=bom, !=0=ruim: git bisect run ./repro.sh
Em ~log₂(N) passos, o git aponta o commit exato que introduziu o bug — muitas vezes o diff já mostra a causa. Requer que cada commit compile/rode (mais um motivo para commits pequenos e verdes).
9.7 Depuração mobile
- iOS (Safari): no iPhone, Ajustes → Safari → Avançado → Inspetor da Web; no Mac, Safari → Desenvolvedor → [seu iPhone] → a aba. Você tem um Web Inspector completo (Elements/Console/Network/etc.) do Safari do dispositivo. Simulador do Xcode idem.
- Android (Chrome): ativar "Depuração USB" no telefone, conectar, abrir
chrome://inspectno desktop → "inspect" na aba do dispositivo → DevTools remoto completo, inclusive screencast da tela. - React Native: o menu de dev (agitar o device) → o debugger; nas versões modernas, React Native DevTools (baseado no Chrome DevTools) com Console, Sources, breakpoints e React DevTools integrados. Flipper (Meta) foi a plataforma de plugins padrão (rede, layout, bancos, logs) — hoje sendo substituída pelo novo DevTools nativo; ainda em muitos projetos.
- Flutter: o DevTools do Flutter/Dart — inspetor de widgets, timeline de performance, profiler de memória, o "repaint rainbow".
Perguntas: "Como você testa um webhook localmente?" (ngrok/cloudflared expõe o localhost), "Como inspecionar o que um app mobile envia?" (proxy — mitmproxy/Proxyman/Charles com certificado; ou remote debugging), "Como descobrir o que engordou o bundle?" (source-map-explorer / bundle analyzer; Import Cost), "Um bug apareceu essa semana e você não sabe qual mudança causou" (git bisect, idealmente git bisect run), "Como você depura o Safari do iPhone?". Ferramentas a citar: Sentry, ngrok, mitmproxy, source-map-explorer.
✏️ Exercício 9 — Escolha a ferramenta
Para cada tarefa, qual ferramenta deste módulo (ou dos anteriores)? (a) o app mobile de um cliente dá erro numa chamada de API e você não tem o código do app; (b) o webhook do Stripe não chega no seu ambiente local; (c) o bundle de produção pulou de 300 KB para 1,2 MB e você não sabe por quê; (d) "essa tela funcionava mês passado"; (e) você precisa rodar a mesma sequência de 8 requests autenticadas repetidamente para testar um fluxo.
Gabarito: (a) Um proxy (mitmproxy / Proxyman / Charles) com o certificado instalado no device — para ver a request e a resposta reais que o app faz. (b) Um túnel (ngrok / cloudflared) apontando para o localhost, e configurar essa URL pública no dashboard do Stripe; a UI do ngrok mostra as requests recebidas. (c) Um bundle analyzer / source-map-explorer no build de produção — o treemap revela a dependência nova (ou a que deixou de ser tree-shaken); comparar com o build anterior. (d) git bisect entre a última tag boa e HEAD, idealmente com git bisect run e um script que reproduz. (e) Um cliente de API com coleção (Bruno/Postman) com um ambiente que guarda o token e requests encadeadas; ou um arquivo .http versionado; ou "Copy as cURL" das 8 requests do Network num script de shell.
Mercado de trabalho: roadmap, entrevistas e portfólio
Objetivo: converter o conteúdo dos módulos anteriores em produtividade e aprovação — onde a fluência com ferramentas pesa, roadmap, perguntas de entrevista e o que mostrar.
10.1 Onde isso conta
- Front-end: DevTools inteiro + React/Vue DevTools + análise de bundle e Web Vitals são competência de base, não diferencial — a ausência chama atenção.
- Full-stack / back-end: debugger do editor, Node
--inspect, clientes de API, profiles de CPU/heap,git bisect. - QA / SDET: Network, HAR, Recorder→Playwright, throttling, a11y no DevTools.
- Suporte / on-call: ler um HAR e um stack trace do Sentry, reproduzir com "Copy as cURL", interpretar um profile.
- Em entrevista técnica, "como você depuraria isto?" é uma pergunta recorrente — e a fluência com ferramentas aparece na resposta.
10.2 Roadmap de estudo (4–6 semanas)
| Semanas | Foco | Prática |
|---|---|---|
| 1 | Método de depuração + Elements & Console a fundo (Módulos 1–2) | Resolver 10 "por que este elemento está assim?" só no painel; usar 8 métodos de console diferentes |
| 2 | Sources: todos os tipos de breakpoint, call stack, blackboxing, overrides (Módulo 3) | Depurar um bug real sem adicionar nenhum console.log; usar conditional breakpoint e logpoint; testar Local Overrides num site público |
| 3 | Network + Performance + Web Vitals (Módulos 4–5) | Fazer um profile de carregamento e um de interação; achar e explicar uma long task; reproduzir uma request com Copy as cURL |
| 4 | Memory, Application, Lighthouse, Coverage, Rendering (Módulo 6) | Caçar um leak com o fluxo three-snapshot (plantar um em um app de teste); rodar Coverage e listar o código morto |
| 5 | Debugger do VS Code + Node --inspect + DevTools de framework (Módulos 7–8) | Configurar um launch.json com launch/attach/test; caçar um re-render com o React Profiler |
| 6 | Clientes de API, proxy, túnel, bundle analyzer, git bisect, mobile (Módulo 9); portfólio | Um git bisect run; analisar o bundle de um projeto; interceptar o tráfego de um device com mitmproxy |
10.3 Banco de perguntas de entrevista (com a resposta que aprova)
Júnior/pleno — "Como você depura um bug de front-end?"
Método: reproduzir de forma consistente → isolar a menor fatia que falha → formular uma hipótese falsificável → testar uma variável por vez → corrigir + teste de regressão. Ferramentas por camada: Console/Elements para DOM e CSS, Network para "a request saiu? o que voltou?", Sources com breakpoint (condicional/logpoint, não editar o arquivo) para o estado, Performance para lentidão, React/Vue DevTools para componentes e re-renders. Evito console.log às cegas — o breakpoint mostra tudo sem prever.
Pleno — "Um elemento é removido da tela e você não sabe qual código faz isso"
No painel Elements, botão direito no nó (ou no pai) → "Break on" → "node removal" (ou "subtree modifications"). Reproduzir a ação: o debugger pausa no JS que causou a mutação, com o Call Stack mostrando a cadeia até o meu código (com as libs na ignore list para reduzir ruído).
Pleno — "A página está lenta. Como você descobre o quê?"
Separar rede de execução. Network (Disable cache): há request com TTFB alto no caminho crítico? recursos grandes? waterfall em série? Performance (com CPU throttling): gravar o carregamento/interação, achar as long tasks no Main, e no Bottom-Up ordenar por Self Time para a função culpada. Não otimizo por intuição — otimizo o que o profiler aponta e re-meço.
Pleno/sénior — "Como você confirma um memory leak no browser?"
Painel Memory, fluxo de três snapshots: snapshot, executar a ação suspeita ~10× + forçar GC, snapshot, repetir, snapshot. No último, filtrar objetos alocados entre os primeiros e ordenar por Retained Size; procurar nós "Detached" (DOM desmontado ainda referenciado) e ver os Retainers (quem segura). Causas comuns: listeners/timers/subscrições sem cleanup, closures capturando objetos grandes, caches globais que só crescem.
Sénior — "Minha alteração não aparece por mais que eu recarregue"
Checar, em ordem: (1) é o arquivo/branch/ambiente certo que está rodando? (2) cache HTTP — recarregar com o DevTools aberto e "Disable cache"; (3) service worker servindo do cache — Application → Service Workers → "Update on reload" ou "Bypass for network", e Cache Storage; (4) CDN/edge cache; (5) build não rodou / HMR travou. O service worker é o suspeito nº 1 em PWA.
Sénior — "Um componente React re-renderiza demais"
React DevTools: ligar "Highlight updates" para ver o alcance; gravar no Profiler e usar "Why did this render?" em cada commit — prop nova a cada render (função/objeto inline não memoizado), Context inteiro mudando (fatiar), ou o pai renderizando (memo). Confirmar no painel Performance que o custo bate. Corrigir com memoização direcionada, split de Context, ou mover o state para mais perto — e re-medir.
Armadilha — "Eu depuro colocando console.log até achar"
Funciona, mas é o caminho lento: você adivinha onde olhar, edita, recompila, recarrega, lê — e repete. O breakpoint para no ponto e revela todo o estado sem prever; o logpoint dá o efeito do console.log sem tocar no arquivo; o conditional breakpoint pega só o caso que importa; o "pause on exceptions" para exatamente onde o erro nasce. console.log tem lugar (produção, timing, logs estruturados), mas como ferramenta principal deixa 80% do poder na mesa.
10.4 O que mostrar (portfólio)
- Um writeup de investigação: "diagnostiquei um vazamento de memória / uma página lenta / uma request misteriosa" — com os passos, os prints do DevTools (heap comparison, flame chart, waterfall) e o raciocínio. Vale mais que "sei usar o DevTools".
- Snippets de debug num gist: checagens úteis para colar no Console (contar listeners, achar elementos sem
alt, medir render, dump de estado). - Um
launch.jsoncompleto de um projeto real (launch + attach + debug de teste + compound), documentado. - Antes/depois de performance: um profile com uma long task, a correção, e o profile depois — com os números.
- Um
git bisect runresolvido, com o script de reprodução.
10.5 Fontes para continuar
- Chrome DevTools: a documentação oficial (
developer.chrome.com/docs/devtools) — cada painel tem um guia; o "What's New in DevTools" a cada versão; o "Tips" (Command Menu → digitar); o canal do Chrome for Developers. - Firefox: a MDN "Firefox DevTools User Docs".
- VS Code: a página "Debugging" da documentação; "Debugging recipes" para cada stack.
- Framework: a doc do React DevTools e o guia de "Profiler"; a do Vue DevTools; a do Redux DevTools; a da TanStack Query Devtools.
- Prática: "DevTools challenges" e o "DevTools Tips" de Umar Hansa; pegar um site lento conhecido e escrever a auditoria; plantar bugs num projeto de treino e caçá-los só com ferramentas.
Quatro ideias sustentam o uso de ferramentas de debug: (1) depurar é método — reproduzir, isolar, hipótese, testar — e a ferramenta serve o método, não o contrário; (2) o breakpoint mostra tudo sem prever, enquanto o console.log só mostra o que você adivinhou imprimir; (3) cada camada tem a sua ferramenta — DOM/CSS no Elements, rede no Network, estado nos DevTools de framework, CPU/memória no Performance/Memory, histórico no git bisect — e a fluência é saber qual abrir para cada hipótese; (4) meça, não adivinhe, e re-meça para confirmar o ganho. As ferramentas evoluem a cada release; o método, não.