Nenhum verbete encontrado.
2. Terminal e linha de comando
Falar com a máquina digitando — o que é scriptável e repetível.
Command Line Interface: programa que se opera digitando comandos. git, npm, curl. A vantagem sobre a GUI é ser automatizável — o que você faz uma vez, você repete mil.
Graphical User Interface: a versão com janelas e botões. O painel de um serviço é GUI; o comando equivalente é CLI. Quase toda ferramenta séria tem as duas, e a CLI costuma receber as novidades primeiro.
Integrated Development Environment: um editor de texto mais as ferramentas em volta, sabendo umas das outras. Um editor comum abre e salva arquivo; um IDE sabe o que o arquivo significa — completa nomes, pula para onde a função foi definida, acusa erro enquanto você digita, renomeia os 40 usos de uma função (e só os usos, não as ocorrências do mesmo texto), depura parando numa linha, e traz terminal e Git embutidos.
A fronteira com “editor” deixou de existir, e o que a dissolveu tem nome: LSP (Language Server Protocol, Microsoft, 2016). A inteligência saiu de dentro do editor e virou um programa separado — o language server. O editor pergunta “o que completa aqui?”, “onde isso foi definido?”, e o servidor responde.
editor ──LSP──> language server (tsserver, pyright, rust-analyzer…)
Escreve-se o suporte a uma linguagem uma vez, e todo editor ganha — por isso VS Code, Neovim e Zed entendem TypeScript igualmente bem: é o mesmo tsserver por trás dos três. É a mesma forma do PostgREST e do harness: separar quem decide de quem apresenta, com um protocolo entre os dois.
A armadilha: “no meu editor não dá erro” não prova nada. O IDE roda a sua cópia das ferramentas — o VS Code usa o TypeScript embutido nele, não o do seu package.json, a menos que você mande usar o do workspace; o ESLint dele pode ler outra configuração; e ele tem plugins que o servidor não tem. A verdade é o que o comando diz: npm run build, tsc --noEmit. O editor é conveniência; o terminal é o árbitro.
O programa que lê o que você digita no terminal e executa. O terminal é a janela; o shell é quem interpreta lá dentro. Bash, zsh e PowerShell são shells diferentes, com sintaxes diferentes.
O que o torna mais que um lançador de programas é o pipe — por isso os comandos são pequenos e combináveis.
O shell padrão de Linux e Mac, e também uma linguagem de script (arquivos .sh). No Windows o equivalente nativo é o PowerShell, que faz o mesmo com outra sintaxe. Importa porque quase toda documentação e toda automação assumem Bash.
O operador |: a saída de um comando vira a entrada do próximo.
cat vendas.csv | grep "2026-09" | wc -l
“Leia o arquivo → fique só com as linhas de setembro → conte quantas são.” É a ideia central do shell: ferramentas pequenas, encadeadas.
Here-document: sintaxe do shell para entregar um texto de várias linhas à entrada de um comando, sem criar arquivo temporário. O nome diz que o documento está aqui, no próprio script.
git commit -F - <<'MSG'
Acrescenta RPC
Entra pelo contraste com REST.
MSG
As aspas no delimitador decidem tudo: <<'EOF' é literal — $HOME continua $HOME e nada executa; <<EOF expande variáveis e executa o que estiver entre crases. Aspas em 90% dos casos: mensagem de commit, bloco de SQL, script de outra linguagem.
Variantes: <<-EOF remove tabulações do início das linhas, para indentar o bloco dentro de um if — só tab, não espaço; e a here-string <<<, de uma linha só.
A armadilha: o delimitador de fechamento precisa estar sozinho, no começo da linha — um espaço antes e o shell nunca acha o fim. O erro não menciona isso: diz unexpected EOF while looking for matching quote, apontando para um lugar sem relação com o problema, porque o shell seguiu lendo e tropeçou na primeira aspa que encontrou. No PowerShell o equivalente é @'…'@, e lá o fechamento precisa estar na coluna zero.
ls cmd.
Lista os arquivos da pasta. Em PowerShell, Get-ChildItem — que também atende por ls.
Despeja o conteúdo de um arquivo na tela. O nome vem de concatenate, porque também junta vários. Em PowerShell, Get-Content.
Filtra linhas que casam com um padrão. É o comando que responde “onde no código isso é usado?” em um segundo:
grep -rn "STRIPE_SECRET_KEY" src/
O nome vem de g/re/p, um comando do editor ed — globally search a regular expression and print. Aceita regex. Em PowerShell, Select-String.
Expressão regular: um mini-idioma para descrever padrões de texto. ^\d{3}\.\d{3} descreve “começa com três dígitos, ponto, três dígitos”. Aparece em grep, em todo editor e em praticamente toda linguagem. Um dos investimentos de melhor retorno por hora estudada.
Faz uma requisição HTTP e imprime a resposta. É o navegador sem navegador — e, por isso, a forma mais honesta de medir a web: mostra o que chegou antes de qualquer JavaScript rodar.
-s silencia a barra de progresso
-L segue redirecionamentos
-I mostra só os cabeçalhos
-H envia um cabeçalho
-A finge ser outro cliente (User-Agent)
Secure Shell: o protocolo para abrir um shell numa máquina remota, com o tráfego criptografado. É como se administra um VPS.
ssh usuario@203.0.113.10
A partir daí, ls, cat e tudo mais rodam lá, e você vê a saída aqui. Serve também para copiar arquivos (scp, rsync) e para túneis.
Autenticação por chave, não por senha. Você gera um par: a privada fica na sua máquina e nunca sai; a pública vai para o servidor. Quem tiver a privada entra — por isso ela é protegida por passphrase, e por isso senha via SSH costuma ser desabilitada. É a mesma lógica do mTLS: provar identidade com chave, não com segredo compartilhado.
O Git usa SSH como uma das formas de falar com o remote — daí URLs no formato git@github.com:usuario/repo.git, alternativa ao HTTPS.
SSH e SSL/TLS são coisas diferentes, apesar das siglas parecidas: ambos criptografam, mas SSH é acesso remoto a máquina e tem seu próprio sistema de confiança — sem autoridade certificadora. Daí o aviso de “host key” na primeira conexão: você é quem confirma.
Agendador de tarefas, e por extensão a sintaxe dele: cinco campos — minuto, hora, dia, mês, dia da semana. 0 3 * * * é “todo dia às 3h”.
Ponto importante: um cron que dispara uma função serverless não contradiz o modelo dela. O relógio está fora; para a função, é uma requisição como outra qualquer. Veja stateless.
3. Linguagens, módulos e pacotes
Em que se escreve, e como um arquivo usa o outro.
A única linguagem que o navegador executa nativamente — acidente histórico, não escolha de engenharia, mas um fato duro: comportamento que precisa acontecer na máquina do usuário acaba em JS. Com o Node.js ela também roda no servidor, onde não tem vantagem nenhuma sobre Python, Go ou C# além da conveniência de ser a mesma dos dois lados.
JavaScript com tipos: você declara que valor é número, e o editor reclama antes de rodar. Os tipos somem no build — o que roda é JS puro. Ver type checker.
TypeScript com JSX permitido dentro. Regra prática: arquivo que desenha tela é .tsx; arquivo de lógica pura é .ts.
JavaScript marcado explicitamente como módulo ESM. Existe porque o Node convive com dois sistemas de módulo, e a extensão vence a configuração do projeto. Por isso um vite.config.mjs funciona em qualquer lugar. O oposto é .cjs, sempre CommonJS.
ECMAScript Modules: o padrão oficial da linguagem para um arquivo usar outro, e o único que o navegador entende nativamente.
import fs from 'fs';
export { minhaFuncao };
A diferença que importa em relação ao CommonJS não é a escrita, é quando: import é estático — as dependências são declaradas no topo e podem ser conhecidas antes de rodar. É isso que torna o tree-shaking possível.
O sistema de módulos antigo do Node (2009): require() e module.exports. Como require é uma chamada de função comum, pode estar dentro de um if e ter o caminho montado em tempo de execução — flexível, e por isso não analisável estaticamente.
Hoje escreva ESM. CommonJS aparece em código legado e em erros como “require is not defined in ES module scope”, que é sempre essa fronteira sendo cruzada.
O motor que roda JavaScript fora do navegador. É o que permite JS no servidor, e é o que executa npm, o build do Vite e o Next.js. Quando um projeto pede “Node 20”, é a versão desse motor.
Não confundir com nó (node) no sentido de elemento de uma árvore ou grafo — “um nó do DOM”, “um nó do cluster”. Mesma palavra em inglês, assunto diferente.
Node Package Manager. Faz duas coisas: instala dependências (npm install react baixa e anota no package.json) e roda scripts (npm run dev, npm run build).
O package-lock.json fixa as versões exatas que funcionaram, e entra no repo. Alternativas com o mesmo papel: pnpm, yarn, bun.
A pasta onde o npm despeja o código baixado das dependências. Enorme e inteiramente regenerável a partir do package.json — por isso vai no .gitignore e nunca entra no repo.
Linguagem de servidor, de 1995. Roda no servidor e gera o HTML que vai ao navegador. Importa por um motivo: WordPress é PHP, e por isso exige hospedagem tradicional com banco e disco.
O modelo clássico é o oposto do stack moderno — cada requisição executa o arquivo do zero e devolve a página pronta, sem build e sem front separado. Carrega má fama das versões antigas; o PHP 8 é rápido, e o Laravel é um framework bem resolvido.
A linguagem dos bancos relacionais: você descreve qual resultado quer, e o banco decide como obtê-lo (é declarativa). Todo Postgres — tabelas, políticas de acesso, funções, gatilhos — é SQL.
Formato binário que permite rodar no navegador código compilado de Rust, C++ ou Go, com desempenho perto do nativo. Usado no que é pesado: edição de vídeo, jogos, planilhas, Figma.
Não substitui o JavaScript: não toca o DOM diretamente, e precisa de JS para isso. É um motor, não uma alternativa.
4. A página no navegador
As três camadas — e o erro de tratá-las como alternativas.
A camada de estrutura e conteúdo: “isto é um título, isto é um parágrafo, isto é um campo de e-mail”. É o que existe.
Não é alternativa ao CSS nem ao JavaScript — são três camadas da mesma página. Sem HTML não há página; uma página só com HTML funciona, feia.
A camada de aparência: cor, tamanho, posição, espaçamento, o que muda no celular, o tema escuro. É como aquilo se parece.
Muita coisa que se fazia com JS hoje é CSS — menu, animação, layout responsivo. Sendo declarativo e executado pelo motor do navegador, é mais rápido e quebra menos que o equivalente em script.
1 · layout (CSS)
Em CSS, todo elemento é uma caixa com quatro anéis: conteúdo, padding, borda e margin.
- padding — espaço por dentro da borda. É o que dá respiro a um botão.
- margin — espaço por fora. Afasta o elemento dos vizinhos.
Quase todo layout torto é um destes trocado pelo outro. No Tailwind: p-6 é padding, m-6 é margin.
2 · criptografia
Completar uma mensagem com bytes extras até o tamanho de bloco exigido pelo algoritmo. Sentido não relacionado ao primeiro — mas é o que aparece em mensagens como padding error ao manipular certificados, e que faria procurar no lugar errado.
Document Object Model: a representação do HTML em memória, como árvore de objetos que o JavaScript lê e modifica. O arquivo HTML é texto; ao carregá-lo, o navegador o parseia e constrói o DOM.
O DOM é a página. Mudou o DOM, a tela muda na hora — o arquivo original não é consultado de novo. Daí “ver código-fonte” mostrar uma coisa e o inspetor mostrar outra.
Três consequências: é por aí que o React trabalha; é por isso que CSR atrapalha o SEO; e é por isso que dá window is not defined no servidor, onde não há navegador nem DOM.
Sintaxe que deixa escrever um pedaço de tela parecido com HTML dentro do JavaScript:
function Saudacao({ nome }) {
return <h1>Olá, {nome}</h1>;
}
O navegador não entende: o build converte para chamadas de função comuns. É açúcar sintático, e as chaves {} são a fronteira — dentro delas é JavaScript de verdade. Daí a extensão .tsx.
Biblioteca de interface. Em vez de mandar o navegador “mude este texto, esconda aquele botão”, você descreve como a tela deve ser dado o estado atual:
tela = f(estado)
Mudou o estado, o React recalcula e aplica só a diferença no DOM — comparando com uma cópia leve da árvore, o virtual DOM. Você para de manipular a tela e passa a manipular dados. A unidade é o componente: uma função que devolve um pedaço de tela, com estado próprio. Ver hook e declarativo.
Single-Page Application. Depois da primeira carga, navegar entre telas não recarrega a página — o JavaScript troca o conteúdo na hora. Navegação instantânea, ao custo de precisar reconstruir à mão o que o navegador dava de graça: rotas, botão de voltar, recarregar sem perder o lugar, e SEO.
“Pegue o rótulo e mude o texto” é imperativo — você dá os passos. “O rótulo mostra o nome do usuário” é declarativo — você descreve o resultado e o sistema descobre os passos.
A indústria inteira foi do primeiro para o segundo: React, SwiftUI e Jetpack Compose são o mesmo movimento em três plataformas, e CSS e SQL já eram declarativos. É a distinção conceitual que mais se reaproveita.
5. Build e ferramentas de projeto
Do código que você escreve ao pacote que de fato roda.
A etapa que transforma o código que você escreve no pacote que roda. Existe porque os dois são diferentes: você escreve para ser legível; o navegador quer algo pequeno e em JavaScript puro. Em ordem:
Um projeto sem build é aquele em que o arquivo que você escreve já é o arquivo que roda. Não confundir com deploy, que é publicar o resultado.
A palavra serve de verbo e de substantivo: “rodar o build” é a etapa; “o build de ontem quebrou” é o resultado daquela execução — o conteúdo do dist/. O contexto separa.
Traduzir entre duas linguagens de alto nível — TypeScript para JavaScript, sintaxe nova para sintaxe compatível. Diferente de compilar, que traduz para linguagem de máquina (C, Rust, Go). O build da web transpila; não compila nesse sentido.
Juntar milhares de arquivos-fonte em poucos arquivos de saída, porque cada arquivo seria uma ida à rede. O resultado é o que o usuário baixa — daí “o bundle está grande” ser uma queixa de desempenho.
“Sacudir a árvore”: descartar do bundle o código importado e não usado. Só é possível porque ESM é estático — a ferramenta sabe, lendo o arquivo, o que é de fato usado.
Apagar espaços e comentários e encurtar nomes internos, deixando o comportamento igual. Reduz o que trafega; torna o resultado ilegível para humanos, o que é irrelevante porque ninguém lê o dist/.
De distribution: a pasta onde o build cospe o resultado pronto. Alguns projetos chamam de build/; o Next usa .next/. Três regras: ninguém edita (é gerada), não entra no repo, e é o que vai para a hospedagem.
A diferença é a direção do controle, e ela decide quanto do desenho do seu projeto já vem decidido.
- Biblioteca — você chama ela, quando quiser. Uma função de formatar data, um cliente de HTTP. Você manda; ela responde.
- Framework — ela chama você. Você preenche os lugares que ela reservou, e ela decide quando executá-los. É a inversão de controle: Next.js decide quando sua página roda, e você não o chama.
Daí a troca: framework dá muito pronto e cobra que você aceite o formato dele; biblioteca dá pouco e não impõe nada. E daí a dificuldade de trocar de framework depois — a forma do projeto é a do framework.
O React se chama biblioteca e na prática funciona como framework: ele decide quando seu componente é chamado. A fronteira é menos nítida do que os rótulos sugerem — o que importa é a pergunta “quem chama quem?”. Ver harness e runtime.
Duas coisas, e as duas aparecem toda hora.
O programa que executa: Node, Deno, Bun, o motor do navegador, a máquina virtual do Java. Só executa — não chama seu código de volta como um framework, nem o observa como um harness. Quando uma plataforma diz “runtime: nodejs20”, é disto que fala.
O momento: em tempo de execução, por oposição a em tempo de compilação ou de build. “Erro de runtime” é o que só aparece rodando — e é exatamente o que a análise estática não pega. Os tipos do TypeScript somem antes do runtime; por isso dado vindo de uma API pode desmentir o tipo declarado sem ninguém avisar.
O estado de partida que um harness monta antes de rodar um teste: as linhas no banco, o arquivo em disco, a sessão autenticada, o relógio congelado numa data. O nome vem do laboratório — o suporte que segura a peça na posição certa para a medição.
A regra que evita a maior parte da dor: cada teste recebe a sua, e desfaz ao sair. Fixture compartilhada faz testes passarem na ordem certa e falharem na ordem trocada — um defeito que some quando você vai investigar.
No Brasil quase ninguém traduz; se traduzir, é “massa de teste”. Não confundir com mock, que substitui uma dependência por uma falsa: a fixture é o dado, o mock é o vizinho.
Duas ferramentas num pacote. Em desenvolvimento sobe um servidor local com HMR; em produção faz o build e produz o dist/. Um app Vite + React é servido como arquivos estáticos e renderizado no navegador (CSR).
Hot Module Replacement: você salva o arquivo e a tela atualiza só aquele pedaço, sem recarregar e sem perder o estado da aplicação. É o que torna o desenvolvimento em Vite ou Next tolerável.
Pôr um hash no nome do arquivo gerado (index-a3f9c1.js) para que conteúdo novo produza nome novo. Assim o navegador pode guardar o arquivo para sempre sem risco. Sem isso, você publica a correção e o usuário continua vendo a versão velha — um clássico de deploy.
Framework de CSS utilitário: em vez de escrever regras num arquivo à parte, você compõe a aparência com classes pequenas e de propósito único, direto no elemento.
<div class="flex items-center gap-4 rounded-lg p-6 dark:bg-slate-800">
Ganha: não inventa nomes (metade do trabalho de CSS), vê a aparência sem trocar de arquivo, valores vindos de uma escala fixa, e o CSS final é minúsculo porque o build gera só o que foi usado. Perde: o HTML fica verborrágico, e mudança global depende de disciplina ou de script.
Programa que lê o código sem executá-lo e aponta problemas — o nome vem de lint, a fiapagem da roupa. Pega erro provável (variável não usada, await esquecido, hook dentro de um if), estilo, e regras próprias do projeto. Em JS/TS o padrão é o ESLint.
Não opina sobre correção: só reescreve o layout do código segundo uma regra fixa. Acaba com discussão de estilo, e ninguém indenta à mão. Vizinho do lint, com papel diferente.
Verifica se os tipos batem (tsc, do TypeScript). É mais fundo que lint, e é a razão principal de usar TypeScript. Junto com lint e formatter, forma a análise estática — encontrar problema antes de rodar. O teste é o complemento que só pega o que aparece rodando.
7. Hospedagem e execução
Onde o código roda — e a distinção que decide tudo: existe entre as requisições, ou não?
Publicar o resultado do build. São etapas separadas — build produz, deploy leva — ainda que plataformas como a Vercel façam as duas em sequência.
Hospedagem que acrescenta cinco coisas a “um servidor com arquivos”: build automático a cada push, CDN, HTTPS e domínio, deploy atômico com rollback, e funções serverless para o que precisa de servidor.
Alternativas: Netlify, Cloudflare Pages, Render; GitHub Pages para o que é só estático; S3 + CloudFront montado à mão; VPS para controle total.
Content Delivery Network: uma rede de servidores espalhados pelo mundo que guardam cópias dos seus arquivos e entregam a partir do ponto mais próximo de quem pediu. Resolve um problema físico — ver latência —, e nenhuma otimização de código compete com isso.
Cada ponto da rede é um PoP (point of presence), também chamado de edge — a mesma borda das edge functions. Cada PoP é um cache:
visitante → PoP mais próximo ─┬─ tem cópia? entrega (cache HIT)
└─ não tem? busca na origem,
guarda e entrega (cache MISS)
A origem é onde o arquivo de verdade mora. O primeiro visitante de cada região paga a viagem longa; os seguintes, não — por isso conteúdo estático é tão barato de servir: a origem é consultada uma vez por região, não uma vez por visitante.
O que cacheia bem: HTML estático, JS, CSS, imagens, vídeo, fontes — ou seja, todo o dist/ de um app Vite e todo site SSG. Mal ou nada: resposta personalizada por usuário, qualquer coisa atrás de login, consulta de API que depende de quem pergunta. Um banco como o Supabase não fica atrás de CDN nesse sentido: a CDN acelera a casca do app, não os dados.
Costuma vir junto, e é metade do valor: TLS terminado na borda (o handshake acontece perto), compressão automática, proteção contra DDoS — o ataque bate na rede e não na sua origem — e o IP da origem escondido.
Quem faz: Cloudflare, Fastly, Akamai, CloudFront. Vercel, Netlify e GitHub Pages já vêm com uma embutida.
A parte difícil é a invalidação. Cache rápido é fácil; cache correto é o problema. Cada resposta carrega um TTL (time to live) no cabeçalho Cache-Control dizendo por quanto tempo pode ser reaproveitada — alto é rápido e arriscado, baixo é fresco e caro —, e existe o purge, que manda esquecer um arquivo agora. A saída elegante é a que o build já dá: conteúdo imutável com nome único. index-a3f9c1.js pode ter TTL de um ano, porque conteúdo novo gera nome novo; só o HTML, que aponta para esses nomes, fica com TTL curto. Ver cache busting.
São três caches independentes, e confundi-los custa tempo de diagnóstico: o do navegador (no disco do usuário; limpa com recarga forçada ou nome de arquivo novo), o da CDN (nos PoPs; limpa por TTL ou purge) e a origem (limpa no deploy). Quando o navegador mostra uma versão e o curl mostra outra, no mesmo instante, é quase sempre o primeiro — curl não tem cache, é processo novo a cada execução.
Time To First Byte: o tempo entre o navegador começar a pedir uma página e o primeiro byte da resposta chegar. É uma soma de etapas:
DNS → handshake TCP → handshake TLS → pedido viaja →
servidor processa → 1º byte volta
└───────────────────────── TTFB ──────────────────────────┘
Fica de fora: baixar o resto, parsear o HTML, buscar CSS e JS, desenhar. TTFB é só a largada.
Duas metades, e diagnosticar é saber qual delas: rede (DNS, os dois handshakes, a viagem — é latência pura, resolvida com CDN) e servidor (consulta ao banco, renderização, cold start — resolvida com código e cache). Otimizar a metade errada não move o número.
O curl decompõe, e cada valor é acumulado desde o início:
curl -o /dev/null -s -w "dns: %{time_namelookup}\ntcp: %{time_connect}\ntls: %{time_appconnect}\nttfb: %{time_starttransfer}\ntotal:%{time_total}\n" https://seusite.com.br/
Se tls já é 0,4 s, o problema é distância. Se tls é 0,05 s e ttfb é 0,9 s, o servidor passou 850 ms pensando. Referências grosseiras: abaixo de 200 ms é bom, acima de 800 ms incomoda.
A armadilha: TTFB engana numa SPA. Um app CSR tem TTFB excelente — o servidor devolve na hora um HTML praticamente vazio —, mas o usuário não vê nada: falta baixar o bundle, executá-lo, buscar os dados e desenhar. Primeiro byte em 80 ms, primeira palavra legível em 2,5 s. Já um app SSR tem TTFB pior — consultou o banco antes de responder — e mostra conteúdo antes. Entre CSR e SSR, o TTFB anda na direção oposta da experiência, e comparar os dois por ele leva à conclusão errada.
Daí as métricas de referência serem outras: FCP (primeiro pixel de conteúdo), LCP (o elemento principal ficar visível), INP (resposta à primeira interação) e CLS (o quanto a página pula enquanto carrega). LCP, INP e CLS são os Core Web Vitals, sinal de ranqueamento do Google; TTFB não é um deles — é diagnóstico, serve para explicar um LCP ruim, não para substituí-lo.
TTFB é o piso: nada aparece antes dele, então 1,5 s condena o LCP — mas um TTFB ótimo não garante nada. No seu stack: SSG + CDN dá o melhor possível (arquivo pronto, servido do PoP mais próximo); SSR inclui a consulta ao banco, e é o caminho mais curto entre índice faltando e página lenta; serverless soma o cold start inteiro, e é por isso que a primeira visita depois de um período parado parece quebrada e a segunda parece rápida.
O conjunto de três métricas que o Google elegeu como medida de experiência e usa como sinal de ranqueamento: LCP, INP e CLS. Cada uma cobre uma pergunta diferente:
- LCP — carregou? (o conteúdo principal ficou visível)
- INP — responde? (a interação teve retorno rápido)
- CLS — fica quieto? (a página não pula durante o carregamento)
TTFB e FCP não são Core Web Vitals — são diagnóstico, servem para explicar um LCP ruim. É uma distinção que economiza trabalho: otimizar TTFB num app que já tem TTFB bom não move o ranqueamento.
Duas medições, e elas discordam. Lab é o teste sintético (Lighthouse, PageSpeed) — reprodutível, e não é o que o Google usa. Field é o CrUX, coletado de usuários reais no Chrome, e considera o percentil 75: passar significa que 75% das visitas passaram, com aparelho fraco e rede ruim incluídos. Site que vai bem no Lighthouse e mal no relatório do Search Console está batendo nessa diferença.
O peso no ranqueamento é real, mas modesto: serve de desempate entre páginas de relevância parecida, não substitui conteúdo. Ver SEO.
First Contentful Paint: quando o primeiro conteúdo aparece — qualquer texto, imagem ou desenho, ainda que não seja o que a pessoa veio ver. É o instante em que a tela deixa de estar em branco.
Vem depois do TTFB e antes do LCP. Bom até ~1,8 s. Não é Core Web Vital: um FCP ótimo com LCP péssimo é comum — a barra de navegação apareceu depressa e o conteúdo demorou.
Largest Contentful Paint: quando o maior elemento de conteúdo visível termina de ser desenhado — normalmente a imagem de destaque ou o bloco de texto principal. É a melhor aproximação automática de “a página carregou” do ponto de vista de quem olha.
Bom até 2,5 s; acima de 4 s é ruim. É o Core Web Vital mais importante e o mais fácil de piorar sem perceber.
As causas mais comuns, em ordem: TTFB alto (é o piso — nada aparece antes), imagem grande sem otimizar, fonte que bloqueia o texto, e CSR — porque o conteúdo só existe depois de baixar e executar o bundle e buscar os dados.
Interaction to Next Paint: quanto tempo a página leva para mostrar resposta visual a um clique, toque ou tecla. Mede a interação mais lenta da visita, não a média — o que torna a métrica honesta.
Bom até 200 ms; acima de 500 ms é ruim. Substituiu o FID em 2024, que media só o atraso da primeira interação e por isso quase sempre passava.
A causa quase sempre é JavaScript ocupando a thread principal: um recálculo pesado, uma lista enorme redesenhando, ou o próprio custo de hidratação num app SSR — a página parece pronta e ainda não responde.
Cumulative Layout Shift: o quanto a página pula enquanto carrega. É a métrica da irritação de clicar num botão e ele se mover no instante do clique. Não é tempo: é uma pontuação, boa até 0,1 e ruim acima de 0,25.
As causas são poucas e todas evitáveis: imagem sem width e height declarados (o navegador não reserva o espaço), banner ou aviso inserido no topo depois, fonte que troca e muda a altura do texto, e conteúdo que chega por API e empurra o que já estava na tela.
É o mais barato de consertar dos três, e o único que depende quase só de CSS disciplinado — reservar espaço antes de saber o que vai ocupá-lo.
O tempo que um pedido leva para ir e voltar, independentemente de quanto dado trafega. Não confundir com banda, que é quanto cabe por segundo: banda larga com latência alta continua parecendo lenta, porque a espera acontece antes de o primeiro byte chegar.
É um limite físico, não um problema de congestionamento. Um sinal não passa da velocidade da luz, e na fibra vai a uns dois terços disso. São Paulo → Virgínia são cerca de 7.500 km: ~40 ms de ida no melhor caso teórico, ~80 ms de ida e volta, e 120 a 200 ms na prática, com os roteadores do caminho.
O que piora é que raramente é uma ida e volta. Abrir uma página pede o handshake TCP, depois o handshake TLS, depois o HTML, depois o CSS e o JS que o HTML referencia. Cinco idas e voltas a 150 ms são 750 ms antes de aparecer qualquer coisa — e nada disso é culpa do seu código.
Daí três coisas deste glossário existirem: a CDN (encurta a distância), as edge functions (encurtam a distância do código) e o bundle (reduz o número de idas). E daí a regra de ouro de desempenho na web: reduzir o número de viagens rende mais que reduzir o tamanho de cada uma.
Hospedagem gratuita de arquivos estáticos direto de um repo do GitHub. Sem build sofisticado e sem funções — e exatamente suficiente para uma página como esta.
Virtual Private Server: uma máquina Linux virtual que é sua, com IP e root. De uns US$5/mês (DigitalOcean, Hetzner, Linode).
A conta real não é o aluguel, é a manutenção. Um VPS envelhece: pacote desatualizado, disco que enche, certificado que expira, SSH mal configurado. É trabalho recorrente para sempre, e raramente vale para um produto só.
Modelo em que chega uma requisição, a plataforma acorda uma instância do seu código, ela responde e morre. Entre requisições, nada seu está rodando — por isso é barato: paga-se por milissegundo, não por máquina ligada.
As restrições: nada sobrevive entre chamadas, teto de tempo por requisição, cold start, disco efêmero, e nenhum acesso à camada de socket (o que inviabiliza mTLS e WebSocket).
Cuidado com um mal-entendido comum: trabalho agendado funciona em serverless — o cron está fora e chama a função. O que não funciona é guardar estado entre invocações. Ver stateless.
Uma função serverless que roda na borda da rede — no ponto da CDN mais próximo de quem chamou — em vez de numa única região. Ganha latência: a função executa a 30 ms do usuário, não a 200 ms.
Borda (edge) é o termo geral para “o mais perto possível de quem acessa”. A mesma ideia que faz a CDN servir arquivos rápido, aplicada a código.
O preço é um ambiente mais restrito que o de um servidor comum: tempo de execução curto, menos memória, e um subconjunto das bibliotecas — várias que dependem de recursos de sistema não funcionam. E, sendo serverless, herda tudo daquele modelo: nada persiste entre chamadas, cold start, e nenhum acesso à camada de socket.
Onde aparece: Edge Functions do Supabase (Deno), Edge Functions da Vercel, Cloudflare Workers. São bons para autenticação, redirecionamento, personalização leve e webhook — e ruins para trabalho longo ou que precise de processo.
Medir antes de supor: é comum ouvir que esse ambiente “não fala com a internet”. Vale testar com mais de um destino antes de aceitar — um destino que falha não prova “qualquer destino”. Ver medir antes de afirmar.
O atraso da primeira chamada a uma função serverless depois de um período parado: a plataforma precisa carregar o ambiente antes de executar. As seguintes são rápidas, enquanto a instância seguir viva.
Stateless: cada execução carrega tudo o que precisa e não lembra da anterior. É o modelo do HTTP, do REST e do serverless — todo estado mora fora (banco, fila, storage) e é recarregado.
Stateful: algo persiste em memória entre um momento e o próximo — uma sessão autenticada, uma conexão aberta, uma fila sendo consumida. Isso exige processo de longa duração.
A pergunta que decide a arquitetura: seu código precisa existir também quando ninguém está pedindo nada?
Programa que sobe e fica ligado, decidindo o que fazer: mantém memória, conexões abertas e disco que persiste. Paga-se pela máquina, ocupada ou não.
Necessário quando há estado em memória entre execuções, conexão persistente (WebSocket, mTLS), ou trabalho longo que precisa de ritmo. Hospeda-se em Fly.io, Render, Railway ou VPS.
Worker conc. 3 sentidos
1 · servidor (modo worker)
Um processo que não escuta porta nenhuma e não tem URL: fica ligado consumindo uma fila. Distingue-se do web service, que recebe HTTP e responde.
usuário → web service → põe na fila → responde "recebi" na hora
↓
worker processa devagar, grava o resultado
Existe porque o usuário não pode esperar três minutos numa requisição HTTP.
2 · navegador (Web Worker)
Um script que roda numa thread separada, para não travar a interface enquanto processa algo pesado. Não enxerga o DOM — comunica-se com a página por mensagens. O Service Worker é um primo que intercepta requisições e viabiliza funcionamento offline; é a peça central do MV3.
3 · plataforma (Cloudflare Workers)
Nome comercial de um produto serverless — que, apesar do nome, é o oposto do sentido 1: acorda por requisição e morre. Confusão fácil, e cara, ao escolher onde hospedar um processo.
Lista de trabalhos pendentes que alguém vai processar depois. Dá três coisas: resposta imediata ao usuário, ritmo controlado (útil quando o destino bloqueia excesso de chamadas) e repetição automática em caso de falha. Consumida por um worker.
URL pública que plataformas como a Vercel geram para cada deploy, do tipo projeto-3f9a2b.vercel.app. Útil para revisar antes de publicar.
Risco: são acessíveis e podem ser indexadas, fazendo o app aparecer no Google em vários endereços, um deles com código velho. Confira o cabeçalho:
curl -sI https://SEU-PREVIEW.vercel.app/ | grep -i robots
Deve vir noindex.
Empacotam um navegador dentro de um aplicativo de desktop — VS Code, Slack, Discord, Figma são isso. Ganha-se reaproveitar time e código da web; paga-se em memória e tamanho. O Tauri ataca esse custo usando o navegador que o sistema já tem e escrevendo o núcleo em Rust.
Fora do navegador, o papel do JavaScript cabe à linguagem da plataforma — C# com WPF/XAML, Swift com SwiftUI, Kotlin com Compose. O que é universal é o laço de eventos: espera → acontece algo → código roda → estado muda → tela é redesenhada.
8. Dados, banco e formatos
Onde a informação mora, e em que forma ela viaja.
Banco de dados relacional open-source, e o que está embaixo do Supabase. Guarda tabelas, executa SQL, garante transações e aplica as regras de integridade e de acesso. É um processo que escuta na porta 5432 e fala um protocolo binário próprio — não HTTP.
Tudo o que o Supabase faz — políticas de acesso, migrations, funções, gatilhos — é Postgres puro com uma camada de conveniência por cima.
A grafia: é PostgreSQL, com “gre”. Não PostgresSQL nem PostsSQL. O banco se chamava POSTGRES (de post-Ingres, o sucessor de um banco chamado Ingres, em Berkeley, nos anos 1980) e, ao ganhar SQL em 1996, virou PostgreSQL — o “SQL” substituiu as últimas letras de “Postgres” em vez de se somar a elas. Postgres é o apelido corrente e significa exatamente a mesma coisa.
Não confundir com PostgREST, que é outro programa, de outro autor, e cujo nome difere por duas letras.
Plataforma que entrega um Postgres gerenciado mais o que costuma vir junto: autenticação, API automática (PostgREST), armazenamento de arquivos, tempo real por WebSocket e funções. O ponto é não ser uma caixa-preta: por baixo é Postgres, e você pode descer até o SQL.
Data Definition Language: a fatia do SQL que mexe na estrutura do banco, não nos dados. O SQL se divide em quatro famílias, e as siglas aparecem em documentação o tempo todo:
- DDL — define a estrutura:
CREATE, ALTER, DROP, TRUNCATE
- DML — mexe nos dados:
INSERT, UPDATE, DELETE (e SELECT, que alguns separam como DQL)
- DCL — controla o acesso:
GRANT, REVOKE
- TCL — controla a transação:
BEGIN, COMMIT, ROLLBACK
A divisão parece escolástica e tem três consequências concretas.
1. Uma migration é exatamente isso: uma sequência de comandos DDL, versionada. Por isso é perigosa de um jeito próprio — DROP COLUMN não tem desfazer, enquanto um DELETE errado ainda tem backup da linha.
2. No Postgres, DDL é transacional — e isso é raro. Dá para envolver CREATE TABLE, ALTER TABLE e CREATE INDEX num BEGIN … COMMIT e, se o último passo falhar, tudo volta atrás. No MySQL cada DDL confirma sozinho, e uma migration que quebra no meio deixa o banco num estado híbrido para consertar à mão. É das razões técnicas mais fortes para preferir Postgres.
3. É o gancho de um event trigger. on ddl_command_end dispara ao fim de qualquer comando DDL, o que permite reagir a um CREATE FUNCTION que ninguém previu — inclusive escrito por outra sessão, meses depois.
E a separação entre as famílias é o que torna esse problema possível: CREATE FUNCTION é DDL, mas a concessão que abre a função nova a um papel público é DCL — e vem de configuração padrão, sem comando nenhum. Não há DCL para interceptar; o gatilho precisa se pendurar no DDL e desfazer o que veio de brinde.
Create, Read, Update, Delete — as quatro operações básicas sobre um registro. “Uma tela de CRUD” é uma tela que lista, cria, edita e apaga. É o vocabulário para dizer “essa parte não tem nada de especial”. Mapeia direto nos verbos do REST.
Parsear é ler um texto bruto e transformar em estrutura que o programa entende; o parser é o código que faz isso. Um leitor de nota fiscal é essencialmente um parser: pega o HTML da página e extrai itens, valores e CNPJ. “Erro de parsing” significa que o texto veio num formato diferente do esperado.
O navegador parseia HTML para construir o DOM.
JavaScript Object Notation: o formato padrão de troca de dados entre sistemas. Texto, legível, com objetos, listas, números, strings e booleanos — e nada mais (sem data nativa, sem comentários).
{ "titulo": "Parquet", "ano": 2013, "colunar": true }
Formato de configuração legível, onde a indentação define a estrutura em vez de chaves. É o formato de GitHub Actions, docker-compose e afins.
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: npm ci
Três armadilhas: tab é proibido (só espaço); recuo inconsistente quebra; e valores sem aspas podem virar outra coisa — no e off viram booleano, 1.20 vira número. Na dúvida, aspas.
O espaço em branco no começo da linha, usado para mostrar hierarquia. Na maioria das linguagens é cosmética — o JavaScript usa chaves para delimitar blocos, e mal indentado roda igual.
Em YAML e Python, não: a indentação é a sintaxe, e mudar o recuo muda o significado — às vezes para outro significado válido, o que é pior, porque não dá erro. Em Makefile, ao contrário de tudo, o tab é obrigatório.
Formato de arquivo para dados tabulares, colunar. Num CSV o arquivo é linha 1 inteira, linha 2 inteira; no Parquet é toda a coluna A, depois toda a coluna B.
SUM(valor) lê só aquela coluna — as outras nem saem do disco
- comprime muito melhor (valores do mesmo tipo, repetidos): 5 a 10× menor que o CSV
- tem tipos e esquema embutidos — CNPJ não perde o zero à esquerda
O preço: ilegível por humano, e ruim para escrever uma linha por vez. É formato de análise (Spark, DuckDB, pandas), não de aplicação transacional.
Depende do contexto, e valem os dois sentidos.
Delta Lake: um monte de arquivos Parquet mais um log de transações dizendo quais deles formam a versão atual da tabela. O Parquet guarda os dados; o log acrescenta transação, UPDATE/DELETE e histórico.
Delta genérico: guardar só a diferença em vez do estado inteiro — é o que o Git faz, e o que uma API faz em “sincronização incremental”. O fio comum: delta é a mudança, não o todo.
9. APIs, protocolos e rede
Como dois programas conversam.
Application Programming Interface: o conjunto de operações que um sistema oferece para outro programa usar. Não é tela, é contrato.
O termo é amplo — uma biblioteca tem API sem rede nenhuma envolvida. Quando se diz “a API do sistema X”, quase sempre se fala da fatia que acontece por rede, sobre HTTP. E aí ainda há uma escolha de estilo:
API ⊃ API web ⊃ { REST, GraphQL, gRPC, tRPC, SOAP, … }
REST é um desses estilos, não sinônimo de API nem um protocolo à parte: é uma convenção sobre como usar o HTTP que já existe. Ver GraphQL / gRPC.
O protocolo da web. É stateless e sempre iniciado pelo cliente: você pede, o servidor responde, acabou — o servidor não tem como falar primeiro (daí WebSocket).
Cada resposta traz um status, que faz parte do contrato: 200 ok, 201 criado, 400 pedido malformado, 401 sem credencial, 403 credencial válida mas sem permissão, 404 não existe, 500 erro do servidor. HTTPS é o mesmo protocolo dentro de TLS.
REST pad. 2 sentidos
Sim, é sigla — REpresentational State Transfer —, e ela não ajuda. É um estilo de API, um entre vários, e a palavra é usada em dois sentidos bem diferentes:
1 · o sentido estrito
O da tese de doutorado de Roy Fielding, de 2000: um conjunto de restrições arquiteturais, entre elas a hipermídia — a resposta deve dizer ao cliente quais ações são possíveis a seguir, como links numa página. O que trafega não é o objeto, é uma representação do estado dele. Quase ninguém implementa isso.
2 · o sentido corrente
O que “API REST” significa na prática, em 2026: uma API sobre HTTP em que a URL identifica uma coisa e o verbo diz o que fazer com ela, trocando JSON. É este que você vai encontrar.
GET /receitas/482 → me dá
PATCH /receitas/482 → altera
DELETE /receitas/482 → apaga
POST /receitas → cria
O contraste é com o que existia antes — tudo POST, a ação escondida num parâmetro (?action=deleteRecipe), cada API com suas convenções. REST padronizou: o verbo já está no HTTP, use-o. Daí sair de graça o cache e o fato de você adivinhar uma API que nunca viu. Mapeia no CRUD.
Daí uma discussão recorrente e pouco útil: alguém aponta que sua API “não é REST de verdade”. No sentido 1, quase nenhuma é.
Um programa separado — de outro autor, escrito em Haskell — que fica na frente de um banco Postgres e o expõe como API REST. O nome é trocadilho: Postgres + REST.
navegador ──HTTP/JSON──> PostgREST ──SQL──> PostgreSQL
Você não escreve nenhum endpoint. Ele lê o esquema do banco — tabelas, colunas, tipos, chaves estrangeiras, funções — e gera a API sozinho. Criou uma tabela? Existe a rota, com filtro, ordenação, paginação e relacionamentos, sem uma linha de código de servidor. Criou uma coluna? Aparece no mesmo instante — não há camada intermediária para atualizar, e por isso ela não tem como sair de sincronia.
GET /receitas?tempo=lt.30&select=nome&order=nome
↓
select nome from receitas where tempo < 30 order by nome;
É o que está por baixo do cliente do Supabase: supabase.from('x').select() não fala com o banco — monta uma URL e chama /rest/v1/…, visível no painel de rede do navegador.
Como a autorização funciona, que é a peça engenhosa: o PostgREST conecta com um papel authenticator que sozinho quase nada pode. A cada requisição ele lê o JWT, extrai o papel (anon sem login, authenticated com) e o ID do usuário, executa SET LOCAL ROLE e publica o ID em variáveis de sessão — só então roda o SQL. É daí que auth.uid() tira o valor dentro de uma política.
A autorização acontece dentro do Postgres, não aqui. O PostgREST só entrega ao banco a identidade de quem pergunta. Três consequências: tabela sem RLS habilitada fica aberta, porque não há camada de servidor para barrar; revoke … from public não fecha nada, porque anon é concessão à parte; e um grant execute a anon expõe a função à internet mesmo que nenhuma tela a chame.
Duas pegadinhas do dia a dia. O limite de linhas — o Supabase configura um teto por resposta, e ele não avisa nem dá erro: devolve o teto, e quem esperava mais conclui que o banco tem menos. Só aparece quando o dado cresce. E os erros vêm do Postgres, atravessando o PostgREST: depurar API do Supabase é depurar SQL e RLS, não a camada HTTP.
Remote Procedure Call: fazer uma chamada a outra máquina parecer uma chamada de função local. Você chama pelo nome, passa argumentos, recebe um retorno — e o fato de aquilo ter atravessado a rede fica escondido.
O contraste com REST é o que esclarece, porque são duas maneiras de pensar uma API:
- REST — a URL nomeia uma coisa, e o verbo HTTP diz o que fazer:
DELETE /receitas/482.
- RPC — o endpoint nomeia uma ação:
POST /apagarReceita com {id: 482}.
Nenhum é melhor em geral; cada um cabe num tipo de problema. REST é excelente quando a API é CRUD sobre recursos. RPC é melhor quando a operação não é CRUD e não corresponde a recurso nenhum — “recalcular o rateio do mês”, “enviar o convite”, “gerar o PDF”. Forçar isso em REST produz substantivos inventados para descrever verbos. Quase toda API real é híbrida.
Onde aparece sem o nome: o rpc() do Supabase chama uma função do Postgres — é RPC dentro de uma API REST, e o PostgREST reserva o caminho /rpc/ exatamente como válvula de escape para o que não é recurso.
supabase.rpc('calcular_rateio', { mes: '2026-09' })
↓
POST /rest/v1/rpc/calcular_rateio
Consequência de segurança: a função é um endpoint público se estiver concedida a um papel público — daí o peso de um grant execute a anon. E a chamada casa por nome e parâmetros: errar um nome de parâmetro devolve “função não existe”, não “parâmetro errado”, porque assinatura diferente é outra função.
A família: gRPC (binário, com contrato em .proto), tRPC (tipos compartilhados em TypeScript), e os antecessores SOAP e XML-RPC, verbosos e em XML — SOAP ainda aparece em sistema público brasileiro.
A abstração é deliberadamente vazada, e a crítica clássica é de 1994 (A Note on Distributed Computing): fazer o remoto parecer local esconde justamente o que não se pode esquecer. Função local não tem latência, não falha por rede, e ou executa ou não executa. Chamada remota pode demorar, pode falhar depois de já ter executado do outro lado — e então repetir não é seguro — e pode nunca responder. Quem trata RPC como função comum esquece timeout, repetição e idempotência, e descobre em produção.
Alternativas ao REST. GraphQL: o cliente pede exatamente os campos que quer, numa URL só — resolve o excesso e a falta de dados de APIs REST, ao custo de complexidade no servidor. gRPC: binário e rápido, para serviço falando com serviço. tRPC: tipos compartilhados entre front e back em TypeScript.
Uma URL sua que um serviço externo chama quando acontece um evento — é como um provedor de pagamento avisa seu sistema de que a cobrança passou. Inverte o sentido da API: em vez de você perguntar (polling), ele avisa.
Detalhe que só aparece depois de publicado: endpoint de webhook costuma ser por conta, não por produto. Se dois apps usam a mesma conta, cada rota recebe os eventos do outro — e deve responder 200 quando o evento não é dela, e não erro, ou o serviço retenta por dias.
Hook conc. 3 sentidos
“Gancho”: um ponto onde o sistema chama um código seu automaticamente quando algo acontece. Três usos bem diferentes:
- Git hook — script que roda antes do commit, por exemplo para rodar lint.
- Webhook — chamada vinda de um serviço externo.
- React hook —
useState, useEffect e os seus próprios: funções que engancham estado e ciclo de vida num componente do React. Sentido bem distinto dos outros dois.
Domain Name System: a lista telefônica da internet. Traduz um nome (seusite.com.br) no endereço IP da máquina que responde por ele — porque o roteamento funciona por número, não por nome.
É a primeira etapa de qualquer acesso, e por isso entra inteira no TTFB. Costuma ser rápida por causa do cache em várias camadas (navegador, sistema, provedor), e a primeira consulta a um domínio novo é a que custa.
Os registros que aparecem na prática: A aponta para um IPv4, AAAA para IPv6, CNAME aponta um nome para outro nome (é como se liga um domínio à Vercel ou ao GitHub Pages), MX diz quem recebe e-mail, e TXT guarda texto — usado para provar posse do domínio e para SPF e DKIM.
Cada registro tem TTL, como um cache. Por isso mudança de DNS não é instantânea: o mundo continua usando o valor antigo até o TTL expirar. Baixar o TTL antes de uma migração é o truque que evita horas de espera — e precisa ser feito com antecedência, porque o TTL antigo ainda vale.
A base sobre a qual tudo o mais roda. IP entrega pacotes de uma máquina a outra, sem garantia: podem sumir, chegar fora de ordem ou duplicados. TCP constrói em cima disso uma conexão confiável — numera os pacotes, confirma o recebimento, reenvia o que faltou e entrega tudo na ordem certa.
HTTP, TLS, SSH e a conexão com o Postgres são todos TCP por baixo. Um socket é a ponta dessa conexão.
A conexão começa pelo handshake de três passos, que custa uma ida e volta antes de qualquer dado — somada à do TLS, são duas antes do primeiro byte útil. É a razão de reaproveitar conexões importar, e parte do que o TTFB mede.
O primo é o UDP, que só dispara pacotes sem confirmar nada — mais rápido e sem garantia, usado em vídeo ao vivo, jogos e DNS. O HTTP/3 roda sobre QUIC, que é confiabilidade reconstruída em cima de UDP, justamente para eliminar uma das idas e voltas.
A abstração do sistema operacional para uma conexão de rede: um canal por onde entram e saem bytes. Tudo em rede é socket lá no fundo.
Socket cru (raw socket) é usar esse canal sem protocolo de aplicação por cima — você manda os bytes que quiser. Aparece em protocolos que não são HTTP, em equipamento industrial, e quando é preciso controlar a camada TLS, como no mTLS. É a camada que plataformas serverless não expõem.
Mantém uma conexão aberta e de mão dupla entre navegador e servidor. Começa como uma requisição HTTP pedindo “faça upgrade”; aceito, a mesma conexão fica aberta e os dois lados mandam mensagem a qualquer momento. A URL muda para wss://.
Resolve o que o HTTP não faz: o servidor falar primeiro — chat, notificação, cotação, edição simultânea. É o que está por trás do tempo real do Supabase. E, por manter conexão viva, exige processo, não serverless.
Beacon conc. 3 sentidos
Farol. O nome descreve o mecanismo: um sinal curto, de mão única, disparado para anunciar presença ou registrar que algo aconteceu — sem esperar resposta útil. Ninguém responde ao farol; ele só precisa ser visto.
1 · telemetria na web
O problema: quando a pessoa fecha a aba, o navegador cancela as requisições em voo — um fetch() disparado no fim da visita não chega. É por isso que métrica de saída é a mais difícil de coletar: o evento acontece exatamente quando o canal morre.
navigator.sendBeacon('/eventos', JSON.stringify(dados))
Isso entrega o pacote ao navegador, não ao servidor: ele se encarrega de enviá-lo mesmo depois de a página deixar de existir. Em troca, só POST, sem cabeçalhos próprios, com teto de tamanho, e você nunca lê a resposta. A alternativa moderna com mais controle é fetch(url, { keepalive: true }).
2 · rádio, proximidade
Um transmissor que emite um identificador a cada fração de segundo, e nada mais — iBeacon (Apple) e Eddystone (Google), para localização dentro de prédios, onde o GPS não chega. O mesmo nome vale no Wi-Fi: o roteador emite beacon frames anunciando a rede, e é assim que o nome dela aparece na lista antes de qualquer conexão.
3 · segurança
Beaconing: um programa instalado numa máquina que chama de volta quem o pôs lá, em intervalos regulares, perguntando se há ordem nova. O sinal que o denuncia é a regularidade — tráfego humano é irregular; beaconing tem período.
Duas armadilhas no sentido 1. Um beacon que falha falha em silêncio, por construção — aceitável para o evento de saída, péssimo como único caminho de toda a telemetria. E o gatilho: o reflexo é usar beforeunload, mas no celular esses eventos muitas vezes nunca acontecem (a aba é congelada, ou o app é morto). O confiável é visibilitychange quando o estado vira hidden — o último instante garantido.
INP e CLS só têm valor final quando a visita acaba — o INP é a interação mais lenta da visita inteira, o CLS soma os pulos até o fim. Por isso toda biblioteca de Core Web Vitals reporta por beacon.
Perguntar repetidamente “tem novidade?” — a gambiarra pré-WebSocket. Desperdiça chamadas e atrasa a notícia. O long polling ameniza fazendo o servidor segurar a resposta até ter algo.
Cabeçalho HTTP em que o cliente diz quem é — navegador, robô, ou o que ele quiser alegar. Serviços usam isso para liberar ou bloquear.
Pode virar incompatibilidade real: um mesmo sistema pode precisar de um User-Agent de navegador para um destino e ser recusado por ele em outro. Testa-se com curl -A. Ver curl.
Proxy conc. 2 sentidos
Um intermediário por onde a conexão passa. O nome é o mesmo para duas coisas que ficam em lados opostos.
1 · de saída (forward)
Fica do lado do cliente e fala com a internet em nome dele. É o que o navegador usa quando se configura “proxy” nas opções ou por PAC. Para HTTPS, o navegador manda CONNECT host:443 e o proxy vira um túnel cego — ele sabe para onde, não o quê. Só vê o conteúdo se fizer bridging.
2 · reverso
Fica do lado do servidor e recebe em nome dele: nginx na frente de uma aplicação, o CDN na frente do site, a Vercel na frente da sua função. O cliente nem sabe que ele existe.
Um proxy de saída pode autenticar quem o usa: responde 407 Proxy Authentication Required e o cliente reenvia com Proxy-Authorization. Uma extensão de Chrome preenche isso sozinha por onAuthRequired. Se o PAC apontar para HTTPS proxy:443 em vez de PROXY, o próprio CONNECT vai cifrado.
Proxy Auto-Config: um arquivo JavaScript de uma função só, FindProxyForURL(url, host), que o navegador chama a cada requisição para decidir se ela vai direto (DIRECT) ou por um proxy (PROXY host:porta ou HTTPS host:porta). É o jeito de rotear só alguns domínios por um proxy e deixar o resto em paz.
“Instalar” um PAC é dizer ao navegador onde ele está — nas configurações do sistema, por política de empresa, ou por uma extensão com a permissão proxy, que o entrega por chrome.proxy.settings.set. Neste último caso, o PAC pode ser gerado por pessoa e trocado a qualquer momento sem ninguém instalar nada: a extensão baixa a lista, monta a função, aplica. É configuração do navegador, não do computador — outro navegador, outro perfil ou outra máquina não o veem.
10. Segredos, certificados e segurança
O que não pode vazar, e onde guardar.
Arquivo de variáveis de ambiente — chaves de API, senhas, URLs de projeto — mantido fora do repo pelo .gitignore. O costume é versionar um .env.example com os nomes das variáveis e nenhum valor, para quem clonar saber o que precisa preencher.
Numa SPA, variável embutida no bundle não é segredo — está no navegador do usuário. Segredo de verdade só existe do lado do servidor.
Cofre de segredos: guarda muitos segredos de tipos variados, criptografados, com política de quem lê o quê e registro de cada leitura — e te devolve o valor. É o modelo, não uma falha: em algum momento o segredo entra na memória da aplicação.
Existe embutido no Supabase e como produto autônomo (HashiCorp Vault). Complementar ao HSM, não alternativo.
Hardware Security Module: uma caixa física ou chip que guarda a chave privada e nunca a entrega. Você manda o dado, ele devolve assinado. Não há operação que exporte a chave — nem para você.
A diferença para o Vault: o Vault devolve o segredo, o HSM devolve só o resultado. Se o servidor for comprometido, o atacante assina enquanto tiver acesso, mas não leva a chave. Um token A3 é um HSM pequeno.
Composição clássica: o HSM guarda a chave-mestra, e o Vault usa essa chave para criptografar todo o resto.
A biblioteca (e a CLI) que faz o trabalho de criptografia na maior parte da internet: gerar chaves, criar e inspecionar certificados, converter formatos, abrir um .pfx/.p12. É a ferramenta de quem mexe com certificado digital.
No padrão brasileiro de certificação digital: A1 é um arquivo (.pfx) protegido por senha, que pode ser copiado e instalado — validade de um ano. A3 vive num token ou cartão, é um HSM pequeno, e a chave não sai de lá — validade maior.
A diferença prática é exatamente a de Vault e HSM: o A1 é conveniente e copiável; o A3 é seguro e não automatizável sem o dispositivo presente.
Transport Layer Security (sucessor do SSL): a camada que criptografa a conexão. HTTP dentro de TLS é HTTPS. Dá três garantias: sigilo, integridade, e autenticidade do servidor — ele prova quem é, com um certificado assinado por uma autoridade que o navegador reconhece.
Mutual TLS: os dois lados provam quem são. Além do servidor, o cliente apresenta um certificado durante o handshake. É autenticação por certificado, não por senha.
Consequência arquitetural: isso acontece na camada de conexão, antes de qualquer HTTP. Exige controle sobre o socket — carregar a chave privada, montar o contexto TLS, negociar —, e plataformas serverless não expõem esse nível. Não é limitação de cota: a peça não existe. Por isso esse tipo de sistema pede processo.
“Aperto de mão”: a negociação inicial de uma conexão, antes de qualquer dado útil. São dois, em camadas, e acontecem nesta ordem:
1 · TCP
O de três passos (SYN, SYN-ACK, ACK) que estabelece a conexão bruta entre duas máquinas. É a base de todo socket, e onde falham problemas de rede, porta fechada ou firewall.
2 · TLS
Em cima do anterior: acerta a versão do protocolo e o algoritmo de criptografia, verifica o certificado do servidor (e, em mTLS, o do cliente) e estabelece a chave da sessão. Ver TLS.
Distinguir os dois é diagnóstico puro: connection refused é o primeiro; erro de certificado é o segundo — e ambos acontecem antes de existir requisição HTTP. É também a etapa que custa tempo, e o motivo de reaproveitar conexões importar.
JSON Web Token (fala-se “djót”): um token de acesso que carrega dentro de si quem é o portador, em vez de ser um código que o servidor precisa consultar num banco. São três partes separadas por ponto — cabeçalho, dados e assinatura — em JSON codificado:
eyJhbGciOi… . eyJzdWIiOiIxMjMiLCJyb2xlIjoiYXV0aGVudGljYXRlZCJ9 . SflKxwRJ…
cabeçalho dados (sub, role, exp…) assinatura
A assinatura é o mecanismo inteiro. Quem tem a chave secreta consegue verificar, em memória e sem consultar nada, que aquele conteúdo não foi alterado. É isso que permite ao PostgREST saber quem está perguntando sem ida ao banco, e é o que torna o modelo stateless.
O que quase todo mundo erra: os dados não são secretos. Estão apenas codificados em base64, não criptografados — qualquer pessoa cola o token num site e lê o conteúdo. A assinatura garante que ninguém alterou, não que ninguém leu. Nunca ponha em JWT nada que não possa ser lido pelo portador.
A contrapartida do stateless: um JWT válido não pode ser cancelado, porque ninguém o consulta em lugar nenhum. Daí a validade curta (minutos ou uma hora) somada a um refresh token, este sim guardado e revogável. Logout não invalida o JWT que já saiu — ele apenas expira.
Token conc. 2 sentidos
1 · segurança
Uma referência opaca que substitui um dado sensível. O caso clássico é o cartão de crédito: o número vai do navegador direto ao processador de pagamento, e o que volta para você é um token que só serve para cobrar naquela conta — e não vale nada se vazar. Ver PCI DSS.
Também se usa para credenciais de acesso (token de sessão ou de API), com a mesma ideia: algo revogável e de escopo limitado, em vez da senha real.
2 · modelos de linguagem
A unidade em que uma IA lê e escreve: um pedaço de palavra, não uma letra nem uma palavra inteira. “Glossário” pode ser dois ou três tokens; em português eles rendem menos que em inglês. É a unidade de cobrança das APIs de IA e a medida da janela de contexto — quanto o modelo consegue considerar de uma vez.
Um terceiro sentido existe em compiladores: o parser quebra o código em tokens antes de entendê-lo. Os três compartilham a ideia de “unidade mínima que representa outra coisa”.
Payment Card Industry Data Security Standard: a norma que as bandeiras impõem a quem toca dados de cartão. Não é lei, é contrato — descumprir custa multa e, no limite, o direito de aceitar cartão. Exige rede segmentada, criptografia, log de tudo, varredura trimestral e auditoria: um programa contínuo.
A conclusão prática é uma só: você não quer estar no escopo. Basta que o número do cartão nunca toque seu servidor — usando o formulário hospedado do provedor (um iframe dele, ou redirecionamento) e recebendo de volta um token.
O jeito de estragar é específico: construir o próprio formulário de cartão e mandar os dados ao seu back-end. Nesse instante você entra no escopo inteiro.
CA conc. 2 sentidos
Certificate Authority, autoridade certificadora: quem assina certificados e, com isso, garante que aquela chave pública pertence àquele nome. Confiar numa CA é confiar em tudo que ela assinar.
1 · pública
Let's Encrypt, DigiCert, GlobalSign — e, no Brasil, as credenciadas na ICP-Brasil (Serasa, Certisign, Soluti). Já vêm na lista de confiança de todo navegador e sistema operacional, o trust store. É por isso que um site com certificado delas abre sem aviso.
2 · interna
Uma CA criada por você, com openssl, que só vale onde você a instalar. Serve para redes de empresa, ambientes de teste e para um proxy que faz bridging. Instalá-la numa máquina é dar a quem tem a chave dela o poder de se passar por qualquer site — a menos que o certificado da CA traga name constraints, que limitam os domínios para os quais ela pode emitir.
A lista de CAs em que um programa confia. Não é uma só por máquina: o Windows tem a dele (e o Chrome e o Edge a usam), o Firefox tem a própria, o Java tem o cacerts, e o Node traz a da Mozilla embutida — no Node 24, --use-system-ca faz ele usar a do sistema.
Consequência prática: “instalar a CA” significa acrescentá-la a uma dessas listas. No Windows, certutil -user -addstore Root ca.pem faz isso para o usuário atual, sem administrador; sem -user, para a máquina inteira. Cada programa com lista própria é um passo de instalação a mais. Ver certificado folha.
Ninguém confia direto no certificado de um site. Confia-se numa CA raiz, que assina uma CA intermediária, que assina o certificado do site — o folha. Validar é percorrer essa corrente até chegar numa raiz que esteja no trust store.
O servidor deve mandar a cadeia inteira, menos a raiz. Quando manda só a folha, o navegador ainda abre a página, porque busca o intermediário sozinho pelo AIA; um programa que use OpenSSL não busca, e falha com unable to verify the first certificate. O openssl s_client mostra a cadeia recebida nas linhas s: (sujeito) e i: (emissor).
Leaf certificate: o último elo da cadeia, o certificado do site em si (ou da pessoa, ou da empresa). É o único que não assina outros — a árvore termina nele, daí o nome. Todo o resto acima dele é CA.
Num proxy de bridging, é o que se emite na hora, um por domínio, assinado pela CA interna: precisa trazer o nome do site em subjectAltName, ou o Chrome recusa. E um certificado folha sem ponto de revogação (CRL ou OCSP) é aceito pelo Chrome mas recusado pelo Schannel do Windows (PowerShell, .NET, curl.exe) com revocation status is unknown.
Authority Information Access: um campo dentro do certificado que diz onde baixar o certificado de quem o emitiu. É o que permite ao navegador completar uma cadeia que o servidor mandou incompleta — Chrome, Edge e Windows fazem essa busca; OpenSSL, Node e Go não.
Efeito colateral: um site mal configurado “funciona no navegador” e falha na automação, e a culpa parece ser do seu código. A saída é embarcar as intermediárias que faltam no ca da conexão.
Dois jeitos de guardar a mesma coisa — chave e certificado — em arquivo.
PEM é texto: blocos -----BEGIN CERTIFICATE----- em base64, um item por bloco, sem senha por padrão. É o que openssl, Node e Go leem sem esforço.
PFX (ou .p12, formato PKCS#12) é binário: chave, certificado e cadeia num pacote só, cifrado com senha. É como o certificado A1 é entregue no Brasil. Os antigos, exportados de Windows velho, usam RC2-40 e 3DES, que o OpenSSL 3 recusa por padrão (Unsupported PKCS12 PFX data) — no Node, --openssl-legacy-provider resolve. Por isso um sistema que recebe .pfx costuma abri-lo uma vez e guardar em PEM cifrado com chave própria: o resto do sistema nunca mais lida com o formato.
Um proxy que termina o TLS do cliente com um certificado próprio e abre outro TLS com o destino — duas conexões cifradas com o proxy no meio, copiando bytes de uma para a outra. É o mecanismo de todo antivírus e firewall corporativo que “inspeciona HTTPS”, e também o de um serviço que apresenta um certificado de cliente (mTLS) em nome de quem não o tem.
Exige duas coisas: uma CA interna instalada no cliente, e os dois lados falando a mesma versão de HTTP — o proxy copia bytes, então se o navegador negociar HTTP/2 e o destino HTTP/1.1, nada funciona; prende-se o ALPN em http/1.1 dos dois lados. Do lado do cliente, o cadeado mostra a CA interna, não o site: é o sinal de que o proxy está no caminho.
12. Conteúdo e web pública
Ser encontrado, e ser compartilhável.
Search Engine Optimization: fazer o conteúdo ser encontrado em buscadores. Só alcança o que abre sem login — página atrás de autenticação não é assunto de SEO.
A renderização importa, mas não é tudo: CSR não torna o site invisível (o Google executa JavaScript), e sim mais lento para indexar, frágil, com títulos repetidos, e completamente opaco para os robôs que não executam JS — WhatsApp e redes sociais entre eles. Ver Open Graph.
Desempenho entra como sinal de ranqueamento pelos Core Web Vitals — e a métrica a olhar é o LCP, não o TTFB. Numa SPA os dois discordam: TTFB ótimo com LCP ruim é a assinatura do modelo, não sinal de que está tudo bem.
Arquivo na raiz do site que diz aos robôs o que não visitar.
User-agent: *
Disallow: /admin/
Sitemap: https://exemplo.com.br/sitemap.xml
Três mal-entendidos: (1) é um pedido, não uma trava — e por ser público, listar /admin/ apenas publica onde ele fica; (2) impede a visita, não a indexação (ver noindex); (3) um Disallow: / esquecido derruba o site inteiro da busca, silenciosamente — é o primeiro item a conferir em qualquer “não aparecemos no Google”.
Instrução para o robô não listar a página no resultado. Vai na própria página, nunca no robots.txt:
<meta name="robots" content="noindex">
Ou como cabeçalho HTTP — única opção quando não há HTML (PDF, imagem):
X-Robots-Tag: noindex
A armadilha: Disallow no robots.txt impede o robô de ver o noindex. Bloqueada, a página não é baixada, a meta tag não é lida, e a URL pode ser listada assim mesmo — sem descrição. Para tirar do Google de verdade: libere no robots.txt e ponha noindex na página.
Lista das URLs que você quer que sejam indexadas, com data de atualização. Não garante indexação; ajuda o robô a descobrir o que existe, sobretudo em site grande ou com páginas pouco linkadas. Frameworks como o Next geram o arquivo a partir das rotas, o que evita que ele envelheça.
O endereço que a página declara como o original de si mesma. Vem de cânone: a versão oficial, a que vale. Vai no <head>:
<link rel="canonical" href="https://www.site.com.br/receitas/risoto">
O problema que resolve: para você é uma página; para o buscador, cada endereço é uma página diferente. Estes quatro podem mostrar exatamente o mesmo conteúdo:
https://www.site.com.br/receitas/risoto
https://www.site.com.br/receitas/risoto?utm_source=instagram
https://site.com.br/receitas/risoto
http://www.site.com.br/receitas/risoto
Diante de cópias idênticas sem nenhuma dizendo qual é a original, o Google escolhe sozinho — e avisa no Search Console: “Cópia sem página canônica selecionada pelo usuário”. A tag é a resposta: não importa por onde você chegou, o original é este.
Três detalhes que confundem:
- É sugestão, não ordem. O Google costuma aceitar, mas ignora uma canonical que pareça errada — apontando para página de conteúdo bem diferente, por exemplo.
- Não é redirecionamento. O redirect (301/308) leva a pessoa para outro endereço; a canonical não leva ninguém a lugar nenhum — a pessoa fica onde está, e só o robô recebe o recado. Por isso o Search Console lista os dois como motivos separados, e “Página com redirecionamento” quase sempre é o redirect funcionando, não defeito.
- Toda página aponta para si mesma, com URL absoluta e na forma escolhida (
https, com ou sem www). Parece redundante; é o que absorve os ?utm= e as variações que você não controla.
A armadilha da SPA: quando o servidor devolve o mesmo index.html para qualquer caminho, /qualquer-coisa-errada responde 200 com o conteúdo da home — uma fábrica de cópias. Uma canonical fixa apontando para a home resolve isso de graça, enquanto o app não tiver rotas de conteúdo. No dia em que existir /receitas/risoto, a mesma linha passa a mentir: toda receita se declara cópia da home e nenhuma é indexada. Aí a canonical tem de ser gerada por rota — e precisa estar no HTML servido, pelo mesmo motivo do Open Graph.
Com o noindex forma um par que se confunde: noindex diz “não liste esta página”; canonical diz “liste, mas pelo outro endereço”. Usar os dois na mesma página manda recados contraditórios.
A parte legível da URL que identifica um item:
/receitas/risoto-de-abobora-com-alecrim
└──────── o slug ────────┘
Minúsculas, sem acento, sem espaço (hífen no lugar), sem caractere especial. O nome vem do jornal impresso: slug era o apelido curto que a redação dava a uma matéria durante a produção, antes do título definitivo.
- É melhor que
/receitas/482 — diz ao humano e ao buscador do que se trata antes de abrir.
- Uma vez indexado, mudar custa caro: links quebram e o ranqueamento zera. Exige redirecionamento permanente (301) para não perder o que havia. Por isso se decide com cuidado e se congela.
- Precisa ser único. Duas receitas de mesmo nome se resolvem com sufixo (
-2) ou com slug híbrido (/receitas/482-risoto-de-abobora), que é legível e garantidamente único.
Cada âncora deste glossário (#cache-busting, #edge-function) é um slug.
Conjunto de meta tags criado pelo Facebook em 2010 e adotado por todo mundo, que define o card exibido quando alguém compartilha o link.
<meta property="og:title" content="Risoto de abóbora">
<meta property="og:description" content="Pronto em 40 min, serve 4.">
<meta property="og:image" content="https://site.com.br/img/risoto.jpg">
<meta property="og:url" content="https://site.com.br/receitas/risoto">
<meta property="og:type" content="article">
Três detalhes que fazem falhar na prática:
- É
property=, não name=. As outras meta tags usam name; trocar faz várias ferramentas ignorarem em silêncio.
- A imagem precisa de URL absoluta — quem lê está em outro servidor. Idealmente ~1200×630.
- Não podem ser injetadas por JavaScript. Os robôs de WhatsApp, Instagram, Facebook e LinkedIn não executam JS: leem o HTML servido e vão embora. Numa SPA, tags adicionadas depois pelo React não existem para eles, e o link aparece sem foto nem título.
O X/Twitter tem um conjunto próprio (twitter:card), mas cai nas og: quando elas faltam — então og primeiro.
Esses serviços cacheiam o card: corrigir a tag não corrige o link já compartilhado. Cada plataforma tem um depurador para forçar a releitura. É cache outra vez, agora numa camada que não é sua.
Marcação que descreve o conteúdo de forma legível por máquina: que aquilo é uma receita, com ingredientes, tempo de preparo, porções e avaliação. Vai num bloco à parte:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Recipe",
"name": "Risoto de abóbora com alecrim",
"recipeIngredient": ["300 g de arroz arbóreo", "500 g de abóbora"],
"totalTime": "PT40M",
"recipeYield": "4 porções"
}
</script>
O “LD” é de Linked Data, dados ligados — e é a parte interessante. A web liga documentos por links, mas as coisas descritas neles não se ligam: meu site diz "autor" e o seu diz "criador", e nenhuma máquina sabe que é o mesmo conceito. Linked Data resolve identificando conceitos por URL — duas páginas que apontam para a mesma definição estão falando da mesma coisa, comprovadamente. É a ideia de web semântica, de Tim Berners-Lee.
Daí as três chaves especiais: @context é o vocabulário (“quando eu disser name, é no sentido de schema.org”) e é o que transforma JSON comum em dado ligado; @type é que coisa é (Recipe, Product, Organization, RealEstateListing); @id é o identificador global, quando outras páginas precisam apontar para o item.
Por que JSON-LD venceu as alternativas anteriores (microdata, RDFa): elas exigiam espalhar atributos pelo HTML, entrelaçando dado e marcação — mudar o layout quebrava os dados. JSON-LD é um bloco separado, independente da estrutura visual. O Google recomenda explicitamente.
O retorno é concreto: é o que produz o resultado rico — foto, estrelas, tempo de preparo —, que ocupa várias vezes mais espaço na busca que um resultado comum. Depende de o HTML ser servido pronto, o que liga isso diretamente à escolha de renderização.
CMS (Content Management System) é onde pessoas não-técnicas escrevem e editam o conteúdo do site. O WordPress tradicional é acoplado: guarda o texto e monta a página.
Headless (“sem cabeça”) jogou fora a parte de montar a página. Sobra um banco de conteúdo estruturado mais uma API que o devolve como dados — JSON, sem HTML nem CSS. Quem desenha é você. Exemplos: Contentful, Sanity, Strapi, Directus, Payload.
Resolve modelagem de conteúdo, edição sem tocar no repo, fluxo editorial e um conteúdo servindo vários destinos. Custa a pré-visualização fiel, dependência de um serviço, e assinatura.
O critério: headless se paga quando há alguém além do programador escrevendo conteúdo. Se só você edita, um arquivo Markdown no repo já é um CMS de graça — com diff e histórico.
PHP + MySQL; o WooCommerce é o plugin que o transforma em loja. Roda cerca de 40% da web.
Não cabe na Vercel, e não é limitação de plano: precisa de banco, escreve no próprio sistema de arquivos (upload de imagem, plugin instalado) e se administra por dentro, com o site no ar — tudo incompatível com disco efêmero e pacote imutável vindo do Git.
O moderno equivalente é o commerce headless: Next na Vercel mais um back-end de comércio por API (Shopify, Medusa, Stripe). Ganha desempenho e flexibilidade; perde onde importa para lojista pequeno — sem programador, nada muda na loja. O critério não é técnico: é quem opera no dia a dia.