Notas
Artigos técnicos e rascunhos longos
- Cloudflare, Neon e VPS: mapa de infra TypeScript para fechar orçamentoCliente pergunta quanto custa hospedar o sistema. Você olha o escopo, pensa em Workers, Postgres, fila, PDF em lote... e responde "depende". Não é evasiva: orçamento de infra em TypeScript hoje cai em três caminhos que se repetem em quase todo briefing. Ter o mapa na cabeça corta a reunião técnica para cinco minutos e evita vender VPS de R$ 150 para CRUD que cabe no free tier da Cloudflare. A pergunta certa não é "qual stack é melhor". É quanto o cliente aceita pagar, quanto processamento pesado existe e quanto você quer brigar com SQLite no código. Combo Cloudflare: barato e unificado Pages + Workers + D1 + R2 fecha MVPs, portfólios dinâmicos, SaaS inicial e sistema interno leve. Custo real fica entre zero e uns R$ 30/mês se passar do free e precisar do Workers Paid. A dor aparece no banco. D1 é SQLite distribuído: sem enums nativos como no Postgres, JSONB com semântica diferente, migração que exige pensar em compatibilidade de schema entre regiões. Prisma tradicional atrita; Drizzle encaixa melhor porque o modelo já nasce enxuto para edge. Você escreve queries conscientes de que não está num Postgres completo. Para CRUD com poucas tabelas e sem RBAC elaborado, funciona. Para e-commerce com permissões em árvore, você vai sentir falta de tipos de coluna que o ORM assume por default. Escolha esse caminho quando o cliente quer barato, o domínio é simples e você topa adaptar o data layer em vez de forçar Prisma como se fosse Neon. Neon na borda: o meio-termo que eu fecho na maioria Pages + Workers + Neon + R2 é o ponto onde DX e custo se encontram. Neon Postgres serverless com free tier generoso; Cloudflare segura edge e assets no R2. Custo ainda orbita zero a R$ 30/mês na fase inicial. Aqui Prisma volta a fazer sentido: enums, UUID, JSONB, migrations sem gambiarra. RBAC, multi-tenant leve, relatórios com joins complexos ficam no território natural do Postgres sem você administrar máquina. Worker faz auth, validação, proxy para o banco via driver HTTP ou connection pooling do Neon. Pouca dor de cabeça operacional. | Cenário | Stack | Custo típico | Quando fechar | | --- | --- | --- | --- | | MVP barato | Pages, Workers, D1, R2 | R$ 0–30 | CRUD simples, time aceita Drizzle | | Negócio real | Pages, Workers, Neon, R2 | R$ 0–30 | Prisma, permissões, joins | | CPU pesada | Workers + VPS + Neon ou Postgres local | R$ 30–150 | PDF/vídeo em lote, filas longas | | CPU pesada + budget | Workers + API especializada + R2 | variável | Cliente paga SaaS de processamento | Se o cliente tem orçamento mínimo mas o produto não é brinquedo, esse é o default. VPS quando o Worker não aguenta o trabalho Geração de PDF com dezenas de páginas, conversão de vídeo, scraping com browser headless, WebSocket de longa duração: Worker não foi feito para isso. CPU limitada, memória curta, timeout de subrequest em torno de 100 segundos no fetch externo. A arquitetura híbrida coloca o Worker como gateway de I/O na borda e empurra o trabalho sujo para VPS (Hetzner, OVH) rodando microserviço em Node/Bun com Docker, fila Redis ou RabbitMQ, worker consumindo jobs. Custo sobe para R$ 30–150/mês conforme RAM e CPU. Em troca, processamento previsível e sem depender de limite de wall clock do edge. Você vira DevOps parcial: patch de SO, firewall, restart de container, monitorar fila travada. O cliente paga pela previsibilidade; você não promete render de vídeo dentro de V8 na Irlanda. Quarta variante: terceirizar o pesado Se dinheiro não é gargalo, VPS some da conversa. Worker recebe upload, manda para iLovePDF, Cloudinary ou API equivalente, salva resultado no R2. Manutenção de biblioteca de PDF quebra com arquivo esquisito vira problema deles. Mil usuários simultâneos não derrubam sua máquina de 4 GB. O único detalhe técnico que não pode ignorar: fetch síncrono no Worker morre se o processamento passar do timeout. Job de PDF grande precisa de fluxo assíncrono com webhook. Worker POSTa o arquivo, registra jobid no D1 ou Neon, responde 202 pro cliente. API externa processa e bate no endpoint de callback do Worker com URL do arquivo pronto; Worker grava no R2 e atualiza status. Conexão com o usuário fecha em segundos; o trabalho pesado acontece fora do limite de 100 s. Na call de escopo, eu desenho esses quatro quadrantes no chat e pergunto: CRUD ou CPU? Barato ou conforto com Prisma? Quem opera servidor? Resposta em uma frase cada; orçamento de infra sai na hora. O resto é implementação.Nota ·
- SSR cacheável na borda: aguenta viral e edição do cliente sem deployCliente pede painel pra trocar hero, depoimentos e preços sem abrir PR. Você não quer voltar pro WordPress, mas também não quer herdar conta de compute porque a landing viralizou num tweet errado. O padrão de acesso é leitura pura: mil visitantes por dia, um editor entrando duas vezes na semana. Montar app server com Postgres e fila de deploy pra isso é overkill; gerar HTML estático no CI e mandar o cliente editar Markdown no Git é underkill. O meio-termo que fecha a conta é SSR cacheável na Cloudflare: render no Worker, conteúdo mutável no banco, HTML público servido da borda como se fosse SSG. O tráfego comum nunca chega no Worker A rota pública (/, /blog/[slug], /precos) renderiza no servidor, consulta o D1 uma vez no cache miss e devolve HTML com header explícito: s-maxage manda a CDN compartilhada (Cloudflare) guardar a resposta por 24 horas. O browser pode ter max-age menor se quiser, mas o que importa pro custo é a borda: cem mil pageviews no dia viram quase cem mil hits em RAM na PoP mais próxima, TTFB na casa de dezenas de milissegundos, zero execução de Worker, zero leitura no D1. Comportamento indistinguível de site estático exportado no build. O Worker só acorda quando o TTL expira, quando você publica deploy novo com hash de asset diferente, ou quando dispara Purge depois de edição no admin. Uma query D1 por invalidação de página, não por visitante. Se o artigo mais lido recebe dez mil acessos entre dois purges manuais, você pagou uma leitura, não dez mil. Comparar com SSR "puro" na Vercel ou num container sem CDN na frente: cada request é cold start + query + render. Funciona no preview com três acessos; no pico vira GB-s e read units acumulando enquanto o cliente acha que "é só uma landing". Picos e DDoS morrem antes do D1 Plataformas serverless cobram por tempo de CPU e por invocação. Tráfego malicioso ou post viral que você não planejou não diferencia visitante legítimo de bot na hora de fechar a fatura. Na Cloudflare, a camada de cache é o primeiro filtro: request repetido com mesma URL e mesmos headers de cache não desce pro origin. Ataque de volumetria contra / esgota budget de edge (que no plano gratuito é generoso) muito antes de esgotar o free tier do D1 ou estourar limite de requests do Worker. Isso não substitui WAF nem rate limit em rota de login, mas pra superfície pública de leitura o modelo é assimétrico a seu favor. O adversário gasta banda batendo em HTML já materializado; você não paga query de banco por bot. | Caminho | Worker | D1 | Custo marginal no pico | |---------|--------|-----|------------------------| | Visitante, cache HIT | não | não | zero | | Visitante, cache MISS | sim | 1 read | uma invocação | | Editor salva no /admin | sim | 1 write | fixo por edição | | Purge após save | API call | não | invalida HTML antigo | Admin, purge e DX que o cliente entende O painel fica no mesmo domínio (/admin), autenticado, fora do cache público. Salvar atualiza linha no D1 e chama a API de purge da Cloudflare pros paths afetados (/, /blog/meu-post). O próximo visitante pega MISS, o Worker re-renderiza com dado novo, a CDN cacheia de novo. Fluxo inteiro sem rebuild, sem commit no Git do cliente, sem webhook de CMS externo se você não quiser. Do lado do desenvolvedor a stack continua a que você já usa: TypeScript, Tailwind, histórico Git só com código. O conteúdo mutável vive no banco edge, não espalhado em dez arquivos JSON versionados. Pra freelancer, isso fecha o contrato: entrega profissional, cliente autônomo, você não fica de plantão porque "precisa rodar deploy pra trocar telefone". Detalhe que derruba o esquema: colocar cookie de sessão ou header Authorization na mesma rota cacheável que o visitante anônimo usa. A CDN não pode servir HTML de usuário A pra usuário B. Mantenha rotas públicas sem variável por sessão; admin e API de escrita em paths separados com Cache-Control: private ou sem cache. O HTML que vai pra borda precisa ser byte-identical pra qualquer anônimo. Snippet mínimo do purge após update (o token fica em secret do Worker): Não precisa purgar o site inteiro; invalidar só as URLs que o D1 alimenta reduz MISS desnecessário nas páginas que não mudaram. Projetos nessa faixa (institucional, blog de empresa, landing de campanha) raramente precisam de invalidação em tempo real ao segundo. TTL de um dia com purge manual no save é aceitável pro negócio e excelente pro bolso. Você ganha flexibilidade de CMS leve sem assinar a arquitetura de um SaaS multi-tenant; o preço de um viral é tempo de cache servido de graça, não surpresa na fatura do mês que vem.Nota ·
- Dev júnior remoto no Brasil: 10 plataformas e o atrito real de cada umaCliquei em Candidatura Simplificada numa vaga júnior remota no LinkedIn às 9h. Às 9h04 o recrutador postou que já tinha mais de 800 inscritos. Coaches de recolocação estimam que o recrutador abre dezenas de perfis nos primeiros dias e deixa o resto na fila até a vaga fechar. Easy Apply, segundo quem mede funil de candidatura, responde pouco; mensagem direta pro gestor costuma performar melhor, mas não existe estudo público com percentual fechado. Ninguém contrata só pelo botão. Em 2025 e 2026, brdev e TabNews repetem outro tema: vaga fantasma, júnior pedido com requisito de pleno, processo longo sem retorno. As plataformas abaixo não resolvem isso. Só mudam onde você perde tempo. LinkedIn: volume alto, sinal baixo O LinkedIn segue obrigatório no Brasil, com mais de 100 milhões de usuários no país. O problema não é a rede: é tratar Candidatura Simplificada como estratégia principal. Candidatos reclamam de limite diário de envios, botão que falha no app e perfis que entram na contagem oficial sem nunca serem lidos. Busca booleana (("junior" OR "jr") AND "remoto" AND ("typescript" OR "node")), candidatura no site da empresa quando a vaga redireciona pro ATS dela, e contato direto com quem publicou. Easy Apply pode entrar na contagem; não pode ser o único canal. ProgramaThor: filtro bom, retorno depende da empresa No Google, nota 4,3 com elogio aos filtros por stack. Em listas de "sites pra dev" ela aparece com frequência, mas não há coro forte em fórum independente comparável ao desabafo sobre Gupy. O que os termos de uso deixam claro: a plataforma só intermedia. Candidatura feita, silêncio de duas semanas não é bug; é o modelo. Coodesh: teste reutilizável, depoimento curado Na página de desenvolvedores da própria Coodesh, quem foi contratado elogia faixa salarial visível cedo e desafio técnico que vale em mais de uma candidatura. Fora do site da empresa, a conversa é mais sobre assessment do que sobre "consegui emprego aqui". O atrito é real: teste comportamental no onboarding, Fast Challenge como triagem. Júnior que codifica bem mas tem currículo fraco às vezes entra pelo score; júnior sem base técnica trava na porta. Nota 4,8 no B2B Stack vem de review agregada, não de comunidade aberta. GeekHunter: porta fechada pra quem tem menos de dois anos A GeekHunter promete que empresas vêm até você. O que candidatos relatam depois do cadastro é triagem automática rígida: perfil com tempo de experiência abaixo do que as vagas ativas pedem cai em "estágio inicial de carreira" e some da visibilidade pras empresas. A própria central de ajuda manda quem está começando pra parceira Eureca.me. Quem passa na curadoria elogia convites e menos atrito que aplicar em massa. Quem não passa descreve rejeição opaca após cadastro completo. Pra júnior de verdade, a percepção é de plataforma pra pleno com portfólio já validável. Gupy: a que mais gera post de desabafo Em reportagem do Valor Econômico, candidatos descrevem a Gupy como desanimadora: vários testes, dedicação, zero retorno. Uma publicitária contou participar de mais de dez processos e só avançar até fit cultural e raciocínio lógico, sem entender o critério. Outra escreveu que a alegria cai "quando abre o link e vê que é Gupy". Matéria do Correio da Manhã citou a Gupy como "a mais frustrante de todas". Gustavo Diniz contou 570 candidaturas em dois anos sem contratação; só foi contratado falando direto com recrutadora no LinkedIn. Os sócios respondem que a IA só ordena a lista. Pro candidato na página 40, o efeito é o mesmo. Na internet, muita gente ainda defende encher o currículo de keywords pra "furar o robô". A Gupy nega que isso funcione e pede texto descritivo nos campos Experiência e Habilidades. Stone, Itaú, Ambev e boa parte das corporações centralizam aqui; ignorar fecha porta. Remotar: curadoria na origem, Gupy no destino O discurso positivo sobre filtrar golpe e vaga "remota" que é híbrido presencial vem sobretudo do blog da Remotar e de artigos parceiros. Candidatos independentes reclamam menos dela do que da Gupy, mas também raramente citam contratação direta por lá. O ponto documentado: muita vaga listada redireciona pro Gupy com tag "via Gupy". A Remotar cura o anúncio, não o processo. Volume menor que LinkedIn ou Indeed; radar diário, não funil único. Trampos: startup e digital, silêncio igual O Trampos é gratuito pro candidato e forte em transformação digital: dev aparece junto de marketing, design e social. Recrutadores elogiam assertividade; candidatos reclamam do mesmo problema de sempre, falta de retorno. O próprio FAQ do site admite: a plataforma não filtra nem seleciona, só entrega a candidatura pra empresa. Vaga de tech costuma descrever cultura e stack com mais cuidado que agregador genérico. Volume de júnior remoto é menor que ProgramaThor. Premium pago libera vaga antes da moderação e mostra se o recrutador abriu seu perfil; pra quem está desempregado, o plano grátis já resolve. Indeed: filtro de home office não confiável Avaliações no Trustpilot reclamam que vagas na aba home office são híbridas ou presenciais. Outros relatam alertas fora do estado e zero feedback. Não é dado agregado; é padrão que se repete em review. O lado bom, quando aparece: empresas de médio porte e agências fora do hype do LinkedIn publicam direto, com menos concorrência visível. Desconfie do rótulo remoto sem ler a descrição inteira. APinfo: feio, lento, mas gente contrata O fórum e o guestbook parecem saídos de outra década. Usuários antigos repetem: visual arcaico, vagas que existem, consultorias sérias, contato que às vezes vai direto pro gestor por e-mail. Depoimentos no próprio site citam contratação; no fórum de mercado o tom muda para salário baixo pra stack gigante e processo longo. Nerdin: polarizado, ceticismo no TabNews No site da Nerdin, depoimentos curados contam contratação depois de milhares de tentativas em outros portais e primeiro emprego como dev. No TabNews, thread recente pergunta se a plataforma é golpe: dev desconfia de vaga júnior com salário alto, site ruim e anúncio que não parece sério. O Nerdin deixa claro que só divulga, não participa do processo e alerta contra cobrança antecipada. Mantido por voluntários com doação via PIX. Vale monitorar pelo volume de vaga júnior com salário explícito, mas trate depoimento de sucesso como marketing até confirmar a empresa. Filtros inversos e prospecção ativa Startups menores no ProgramaThor, Trampos e Remotar tendem a ser mais flexíveis com júnior remoto do que corporação com vaga "remota" e presença obrigatória duas vezes por mês. Quando a vaga cair na Gupy, Coodesh ou Indeed, busque a empresa no LinkedIn, ache o tech lead e mande o repositório do seu projeto na stack deles. Nenhuma plataforma substitui chegar na pessoa que contrata.Nota ·
- SSR completo ou skeleton na tela: decida pelo dado, não pelo frameworkVocê monta um dashboard com quatro widgets. Três queries respondem em 80 ms; a quarta bate numa API de terceiros que às vezes leva dois segundos. Com SSR bloqueante, o usuário fica olhando tela branca até a mais lenta terminar. Não sabe se travou, se caiu a rede ou se o deploy quebrou. A pergunta "espero tudo no servidor ou mando skeleton e busco depois?" aparece em toda stack com App Router, RSC ou equivalente. A resposta errada é escolher um lado e aplicar em tudo. Skeleton com fetch assíncrono ganhou tração porque resolve dor real em apps autenticados: feed, inbox, painel de métricas, home pós-login. O layout sobe rápido, o menu fica clicável, o cérebro enxerga progresso em vez de vazio. TTFB e FCP melhoram porque o servidor não segura a resposta HTTP esperando o pior caso da cadeia de dependências. Para dados que mudam a cada refresh e vêm de fontes com latência imprevisível, travar o HTML inteiro é UX ruim. Mas generalizar isso para página de artigo, ficha de produto ou landing de campanha é desperdício. Esse conteúdo é idêntico para todos os visitantes. Não há ganho em mostrar skeleton do título e do corpo se você já tem o texto no banco ou no CMS. O usuário quer o artigo completo na primeira pintura, não um placeholder pulsando onde deveria estar o parágrafo. Robô de busca também quer HTML com conteúdo, não shell vazio hidratado depois. Conteúdo público: SSR completo e cache na borda Página de blog, documentação, e-commerce público: renderize tudo no servidor, envie HTML cheio, cacheie na CDN. O bloqueio no origin só acontece no cache miss; depois disso o TTFB cai para milissegundos na edge, independente do usuário estar em São Paulo ou Lisboa. Você fica com performance de estático e flexibilidade de template dinâmico. Headers típicos para conteúdo que muda pouco: s-maxage vale para proxies compartilhados (CDN); o browser ainda respeita max-age se você definir. stale-while-revalidate entrega resposta velha enquanto revalida em background, o que evita pico de latência quando o TTL expira. Página de produto com estoque que muda a cada minuto pede TTL menor; artigo de blog aguenta horas ou dias. O erro comum é tratar "SSR" como sinônimo de "lento". SSR lento é SSR sem cache em rota que deveria ser cacheável. Se o HTML varia por usuário logado, você não pode jogar na CDN sem pensar. Aí entra a divisão. | Tipo de dado | Abordagem | Por quê | |--------------|-----------|---------| | Artigo, LP, produto público | SSR completo + CDN | Mesmo HTML para todos; SEO e FCP com conteúdo real | | Feed, dashboard, inbox | Skeleton + fetch/stream | Várias fontes lentas; progresso visual | | Nome no header, carrinho, wishlist | Client fetch | Não contamina cache público | Dados do usuário ficam fora do HTML cacheável Avatar, badge de notificação, itens no carrinho, recomendação "pra você": isso não entra no payload que a CDN guarda. Você busca no client com fetch, SWR ou TanStack Query depois que o shell público já está na tela. O HTML base permanece Cache-Control: public sem risco de cache poisoning (usuário A receber fragmento de usuário B). O detalhe que derruba a experiência é CLS. Se o header reserva 40 px de altura e o nome do usuário empurra o layout quando chega, você troca tela branca por flicker. Reserve espaço fixo ou skeleton só na região privada: um retângulo cinza onde o avatar entra, largura mínima no slot do carrinho. O restante da página já veio completo do SSR. Streaming SSR não resolve confusão de responsabilidade React Server Components e streaming permitem mandar o shell cedo e completar blocos depois. Útil quando um trecho público é rápido e outro depende de query pesada na mesma página pública. Ainda assim: se o bloco lento é conteúdo que deveria estar no cache (corpo do post, preço do produto), o problema é arquitetura de dados, não falta de Suspense. Mover a query lenta para build time, ISR ou edge cache costuma ser mais simples que empilhar boundaries. Dashboard é outra história. Lá faz sentido streamar: sidebar estática primeiro, gráfico que depende de warehouse depois. O usuário já está dentro do app; expectativa de latência é outra. Misturar os dois mundos na mesma rota sem separar o que é público do que é privado é como usar SPA em landing de marketing porque o tutorial mandou. Na próxima revisão de arquitetura, liste o que cada rota entrega antes de escolher padrão de render. Conteúdo igual para anônimo e logado vai pro SSR cacheável. Só o que muda por sessão vai pro client, com espaço reservado. O framework segue a classificação dos dados; inverter essa ordem é o que gera página rápida sem conteúdo ou página cheia que demora dois segundos à toa.Nota ·
- Token assimétrico no SSR: role no HTML sem bater na APIO padrão que mais vejo em app com SSR autenticado: o servidor recebe o cookie, chama GET /me na API, espera 120 ms (ou 800 ms num dia ruim), lê role e permissions, e só então monta o layout. Cada navegação, cada refresh, cada prefetch do framework repete a viagem. A API vira gargalo de TTFB para algo que o HTML já deveria saber responder: "esse usuário é admin ou visitante?". Com token assimétrico (JWT assinado com par RSA ou ECDSA, validado com chave pública), o processo de render troca de "perguntar pra API" para "verificar assinatura localmente". O access token carrega sub, role, escopos, exp. O servidor SSR importa a chave pública (JWKS estático, fetch no boot, rotação via kid no header) e roda jwt.verify antes de renderizar. Zero round-trip de rede para decidir se a sidebar mostra painel de billing. O que muda no pipeline de render Fluxo típico hoje: Com validação assimétrica no edge ou no Node do SSR: A API continua emitindo o token no login e no refresh. O SSR não precisa de segredo compartilhado: só a chave pública, que pode ficar em cache por horas. Em deploy com múltiplas instâncias do frontend, cada uma valida sozinha. Latência de autenticação vira custo de CPU (microssegundos) em vez de fila na API de auth. O ganho aparece forte em rotas que misturam shell público e trecho privado. Você sabe no servidor se redireciona pra /login, se renderiza menu de admin ou se esconde botão de export sem esperar outro serviço acordar. Frameworks com middleware (Next, Astro com adapter Node, Hono na BFF) encaixam a verificação num único ponto antes do handler da rota. | Abordagem | Latência no SSR | Fonte da role | |-----------|-----------------|---------------| | fetch /me a cada request | RTT + fila da API | Banco, sempre atual | | JWT assimétrico local | Só crypto local | Claims no token | | JWT + introspection sob demanda | RTT só quando suspeito | API como árbitro | TTL curto e refresh fora do caminho crítico Token com exp de 5 a 15 minutos reduz a janela em que claims desatualizadas mentem pro usuário. O refresh roda em rota dedicada (/api/auth/refresh) ou no client após hidratação, não no meio do render da página de listagem. SSR lê o access token do cookie httpOnly; se expirou, redireciona pro login ou manda HTML mínimo de "sessão expirada" sem tentar refresh síncrono que segura o response inteiro. Claims devem ser mínimas: identificador, role principal, talvez tenant. Não coloque perfil completo, avatar URL nem lista de permissões fina que muda a cada clique. O que muda rápido continua no client com fetch depois do shell, como já faz sentido em dashboard. Blocklist: onde o token local mente A API que você deixou de consultar no SSR ainda pode manter blocklist de tokens revogados: logout forçado, troca de senha, desligamento de funcionário, comprometimento de sessão. O JWT continua criptograficamente válido até o exp; a assinatura não sabe que o emissor já o invalidou. Três saídas, cada uma com custo diferente: TTL agressivo. Access token de 5 minutos. Revogação efetiva no pior caso em cinco minutos. Funciona para muitos produtos internos; não serve se compliance exige corte imediato. Blocklist consultada no SSR. Redis ou cache compartilhado com jti ou hash do token. O render faz um GET rápido (sub-ms na mesma VPC) antes de confiar nas claims. Você recupera revogação imediata mas reintroduz dependência externa. Ainda costuma ser mais barato que /me completo porque a lookup é O(1) por id, sem join de usuário. Introspection só em rotas sensíveis. Dashboard com JWT local; endpoint de transferência bancária ou exclusão de conta chama a API pra confirmar sessão. Híbrido honesto: performance no caminho quente, autoridade da API no caminho que dói. Ignorar blocklist porque "JWT é stateless" é como cachear HTML público com dados de usuário: funciona no happy path até alguém é demitido e ainda acessa painel admin por onze minutos. Chave pública e rotação Publique JWKS num endpoint estável (/.well-known/jwks.json) ou embuta no deploy do SSR com versionamento. Quando a API rotaciona par de chaves, tokens antigos trazem kid no header; o verificador tenta a chave correspondente. SSR sem atualizar JWKS rejeita login legítimo após rotação. Trate JWKS como dependência de deploy ou cache com TTL baixo (5-10 min), não como arquivo esquecido no repositório. Se a API e o SSR são times diferentes, documente contrato de claims (role string vs array roles, nome do claim de tenant). Divergência quebra render silencioso: usuário vê UI de membro com token que a API considera admin. Na próxima rota autenticada com SSR, desenhe o que precisa ser verdadeiro no primeiro byte do HTML. Se for só role e id, token assimétrico local elimina a API do caminho crítico. Se revogação imediata for requisito, planeje blocklist ou introspection antes de celebrar o ganho de latência; assinatura válida não substitui emissor que já disse não.Nota ·
- Wayland, X11, GTK e Qt: mapa das camadas do desktop LinuxEscolher GNOME, KDE Plasma ou XFCE parece decisão de tema e atalhos. Você está empilhando três camadas: display server (quem fala com GPU e input), toolkit (como apps desenham widgets) e desktop environment (shell, settings, apps padrão). Confundir isso gera expectativa errada sobre compatibilidade, RAM e por que um app Qt "estranho" no GNOME não é bug isolado. Display server: X11 e Wayland X11 (X.Org) nasceu nos anos 80 com servidor centralizado e clientes remotos. Janelas pedem ao X server; qualquer processo com acesso ao socket X pode ler teclas de outro app. Segurança e composição viraram remendos (compositing manager, extensões). Ainda domina legado: screen sharing caprichoso, drivers antigos, apps que assumem xrandr e NETWM. Wayland inverte o modelo: o compositor é dono do framebuffer e do input. Apps recebem buffers prontos; não há "janela X" global para terceiros espionarem. Ganha-se tearing menor, gestos nativos e caminho limpo para HiDPI. Programas antigos rodam via XWayland dentro da sessão Wayland. Abrir o app costuma funcionar; gravar tela, compartilhar janela em videoconferência ou controlar outras janelas pedem permissão explícita, e cada desktop implementa de um jeito. No X11 essas funções eram mais soltas por padrão. echo $XDGSESSIONTYPE retorna wayland ou x11. Isso define capacidades reais do sistema, não só o nome do menu de login. Toolkit: GTK e Qt GTK e Qt são frameworks de UI: widgets, tema, acessibilidade, file picker, notificações. GTK (C, bindings em Python/Rust/etc.) é a stack do GNOME. Apps GTK seguem Libadwaita no GNOME 40+: uma lib visual, menos "bring your own theme". Fora do GNOME, GTK ainda roda, mas header bars, client-side decorations e tema podem divergir. Qt (C++, QML) sustenta KDE Plasma e apps cross-platform (VLC, OBS, Telegram desktop). Traz estilo próprio (Breeze), engine de ícones e abstração multiplataforma. KDE Plasma no Wayland é referência madura; Qt 6 consolidou suporte nativo. Toolkits não são DEs. Firefox e Chromium usam toolkits próprios; Electron empilha Chromium. Rodar app Qt no GNOME e GTK no Plasma funciona; o incômodo é visual. Desktop environment: o pacote completo DE = compositor + painel + settings + apps default + convenções (tipos MIME, atalhos, energia). | DE | Compositor típico | Toolkit base | Perfil | |----|-------------------|--------------|--------| | GNOME | Mutter (Wayland default) | GTK / Libadwaita | Fluxo simples, pouca customização | | KDE Plasma | KWin | Qt | Altamente configurável | | XFCE | Xfwm (X11; Wayland em progresso) | GTK | Leve, desktop clássico | | LXQt | Openbox ou compositor leve | Qt | Mínimo de RAM | | MATE | Marco (fork Metacity) | GTK | GNOME 2 preservado, X11-first | | Cinnamon | Muffin (fork Mutter) | GTK | Bandeja e menu tradicional | Cinnamon e MATE nasceram da reação ao GNOME 3: menu, bandeja e janelas clássicas. XFCE e LXQt competem no nicho de máquina velha ou servidor com monitor. Nenhuma DE impede apps de outra stack. O atrito é integração: tema escuro inconsistente, title bar diferente, portal de arquivo errado no Wayland. WM sozinho vs DE i3, Hyprland e Sway são window managers ou compositors tiling, não DEs completos. Entregam layout e atalhos; painel, polkit, rede e brilho você monta (polybar, waybar, scripts). Controle total e boot rápido, em troca de horas de config e gaps em laptop (suspend, Wi-Fi, Bluetooth). Sway é i3-like no Wayland; Hyprland adiciona animações e regras dinâmicas sobre wlroots. Escolha de quem trata desktop como código versionado. O que usar como usuário comum Se você só quer instalar e usar, fique no desktop padrão da distro. Fedora e Ubuntu recentes entregam GNOME no Wayland. Quer menu tradicional, bandeja cheia e sliders em todo lugar? KDE Plasma (Kubuntu, openSUSE ou plasma-desktop na mesma base) resolve direto. XFCE e LXQt fazem sentido em PC com pouca RAM ou CPU antiga. Cinnamon e MATE agradam quem sentiu falta do GNOME 2. Evite i3, Hyprland e Sway como primeiro Linux. Painel, rede, brilho e permissões ficam na sua mão; só vale se você gosta de configurar o ambiente. Na tela de login, tente Wayland primeiro. Se algum app falhar (screen share antigo, software corporativo legado), volte para X11 naquela sessão. Apps mistos funcionam; diferença visual entre app GNOME e app Qt no mesmo desktop é normal. Trocar de DE no meio da instalação gera pacote duplicado e tema inconsistente. Melhor escolher spin certo na instalação (Kubuntu, Xubuntu, Linux Mint Cinnamon) do que empilhar ambientes depois.Nota ·
- Sincronizar estado entre dispositivos sem backend: hash na URLlocalStorage resolve persistência no mesmo navegador, mas não sincroniza celular com notebook. Para ferramentas client-only (planejadores, editores, cadernos), subir backend só para copiar JSON entre dispositivos é custo desproporcional. Serializar estado, comprimir, colocar no hash da URL e restaurar na abertura do link. Por que hash e não query string A query (?foo=bar) vai na requisição HTTP. Proxies, logs de CDN e servidores antigos tratam 2 KB como teto prático. O fragmento (...) fica no cliente: o browser não envia o hash ao servidor. Browsers modernos aceitam dezenas de KB no fragmento. | Parte | Vai ao servidor? | Uso típico | |-------|------------------|------------| | ?state=... | Sim | Filtros, UTM, deep links server-side | | state=... | Não | Snapshot de estado client-only | Para transferir estado entre dispositivos, o hash é o canal certo. Pipeline de encode Envolva o payload com metadados mínimos. Campo v evolui o formato; app impede abrir link de planejador no editor errado: deflate costuma reduzir JSON repetitivo em 60-80%. base64url evita caracteres que quebram ao colar em chat ou e-mail. O botão "Copiar link" só chama esse pipeline. Import na abertura da página Quem recebe o link abre a mesma rota com state=... no final. Na inicialização: 1. Ler window.location.hash 2. Extrair token, decodificar base64url, descomprimir, JSON.parse 3. Validar v e app 4. Gravar em localStorage (ou store da UI) e limpar hash com history.replaceState Limpar o hash após importar evita reprocessar o mesmo estado a cada refresh. Sanitizar na entrada Estado vindo de link é entrada externa. O mesmo código que valida localStorage deve rodar no import: Campo ausente vira default; tipo errado descarta ou corrige; item inválido em lista filtra. Pular isso no import quebra a UI com link adulterado ou payload de versão antiga. Onde não usar Transferência de snapshot, não sync em tempo real. Não substitui WebSocket, CRDT ou backend com merge. Após compressão, 64 KB na URL completa é teto prático; apps de mensagem truncam links maiores. Fotos em base64 dentro do JSON explodem o payload. CompressionStream exige browser recente. O estado fica visível para quem recebe o link: não coloque segredos. Separe persistência local, serialização de link (encode/decode, limite, envelope) e UI (copiar link, detectar hash no mount). A camada de link transporta JSON; não precisa conhecer campos internos da app. Copiar link no desktop e abrir no celular reproduz o estado sem conta, API ou arquivo .json intermediário.Nota ·
- 6 livros essenciais para desenvolvedores de softwareTutorial resolve o ticket de hoje; livro resolve o padrão que você repete em dezenas de tickets. Quem escreveu pagou o custo de errar e deixou decisão de engenharia condensada. Estes seis cobrem lacunas que curso ignora: complexidade algorítmica, acoplamento de módulos, validação de hipótese, hierarquia visual e carreira além do commit. Entendendo Algoritmos Aditya Bhargava traduz estruturas de dados e complexidade para quem aprendeu programando, sem base formal. Não é CLRS: mapa visual de busca binária, grafos, programação dinâmica e recursão. Aparece em code review. Você para de escolher O(n²) em loop aninhado, reconhece quando Map substitui varredura linear e entende por que otimização prematura piora legibilidade sem ganho mensurável. Leia cedo se "funciona" mas Big O trava em entrevista. A Philosophy of Software Design John Ousterhout quantifica o que SOLID descreve vagamente: complexidade acidental versus essencial. Conceito central é deep module (interface estreita, implementação rica) versus módulo raso que expõe passos internos. Critica classitis e over-engineering de padrões. Foca micro: função, classe, arquivo. Antes de Arquitetura Limpa, calibra olho para dívida que nasce local. Arquitetura Limpa Robert C. Martin organiza codebase em camadas concêntricas: entidades no centro, casos de uso, adaptadores, frameworks por último. Domínio não importa Express, Prisma ou React. Valor está em testabilidade e trocar infra sem reescrever regra de negócio. Repository no domínio, Postgres no adapter. Controller fino, use case com lógica. Boilerplate é o preço. Monólito pequeno com um dev não precisa de cinco camadas por feature. API que sobrevive troca de banco, fila e auth precisa fronteira explícita. Leia quando projeto não cabe na cabeça de uma pessoa ou teste de integração virou única confiança. Refactoring UI Adam Wathan e Steve Schoger escrevem para dev que constrói UI sem designer. Não é Figma: hierarquia, espaçamento em escala, contraste e tipografia que comunicam prioridade. Fullstack empilha componente do design system e ainda entrega UI estranha. Tamanhos arbitrários, cinzas inconsistentes, falta de agrupamento visual. Aplica direto em PR de frontend. Não substitui design system nem WCAG. Complementa: você sabe quando quebrar regra e quando seguir escala 4/8px. O Teste Da Mãe Rob Fitzpatrick corrige erro caro antes do código: validar ideia com conversa que só coleta elogio. "Você usaria?" gera resposta socialmente desejável, não dado. Fale do passado do entrevistado, peça histórias de dor, nunca faça pitch na entrevista. "Como você resolve X hoje?" e "Quanto custou no mês passado?" valem mais que roadmap. Engenheiro acostumado a spec fechada precisa disso: commit sem evidência é aposta. Leia antes de MVP ou módulo "que o cliente pediu" sem checar se problema existe. Soft Skills John Sonmez cobre negociação salarial, marca técnica, produtividade, investimento e troca de emprego. Parte envelheceu em marketing; estrutura de carreira como sistema ainda presta. Habilidade técnica é necessária, não suficiente. Quem só otimiza código ignora alavancas de renda e rede. Seções sobre aprendizado e energia evitam burnout de quem trata hora extra como senioridade. Leia com filtro nas partes de autopromoção. Metas, finanças pessoais e comunicação com stakeholder não técnico valem guardar. | Fase | Livro | Por quê | |------|-------|---------| | Fundação | Entendendo Algoritmos | Vocabulário de complexidade | | Código diário | A Philosophy of Software Design | Módulos e complexidade local | | Sistema | Arquitetura Limpa | Fronteiras domínio/infra | | Entrega | Refactoring UI | Interface que usuário confia | | Produto | O Teste Da Mãe | Hipótese antes de sprint | | Carreira | Soft Skills | Alavancas além do teclado | Nenhum substitui prática. Cada um encurta distância entre ler sobre engenharia e reconhecer o padrão no diff.Nota ·
- NestJS: o melhor framework para APIs convencionais em TypeScriptRota que retorna JSON não basta. API convencional carrega transporte, regras de negócio, auth, validação, observabilidade e contrato estável entre times. Express e Fastify resolvem HTTP; o resto vira biblioteca solta e convenção oral. NestJS impõe estrutura desde o primeiro módulo, e isso aparece quando o side project vira produto. REST sobre HTTP, versionamento, DTOs validados, auth por token ou sessão, filas, cron e banco relacional. Times distintos consomem os mesmos endpoints. Deploy em container ou VM, não edge com cold start de milissegundos. ERP, marketplace, SaaS B2B, backend mobile. Não é BFF de 50 linhas. NestJS herda Node (Express ou Fastify como adapter) e organiza código em módulos, controllers, providers e pipes. DI nativa, não workaround Injeção no Express costuma ser manual: new UserService(new UserRepository()) espalhado ou container ad hoc com tsyringe. Funciona em projeto pequeno; em monólito com dezenas de serviços, testes viram pesadelo de mock. No NestJS, o container é first-class: Trocar implementação (mock em teste, adapter em prod) exige @Module({ providers: [...] }), não refactor em cascata. Validação e contrato na borda class-validator + class-transformer via ValidationPipe transformam body, query e params antes do handler: Sem pipe global, cada rota reimplementa sanitização. Com pipe global, 400 padronizado e tipagem alinhada ao runtime. Guards, interceptors e cross-cutting Auth, role, rate limit, serialização de resposta e logging de latência espalham fácil. Guards decidem se a request entra; interceptors envolvem execução; filters mapeiam exceção para HTTP status. @UseGuards(JwtAuthGuard, RolesGuard) no controller substitui middlewares encadeados sem ordem clara. Em API com dezenas de rotas, declaratividade evita rota sem auth passando silenciosa. Além do HTTP NestJS não prende você a REST. @nestjs/microservices abstrai RabbitMQ, Kafka, NATS e gRPC. @nestjs/schedule cobre cron. TypeORM, Prisma e MikroORM têm integração documentada. Evoluir para event-driven no mesmo codebase evita rewrite. Hono brilha em Workers e BFFs de baixa latência; NestJS troca overhead por previsibilidade. tRPC acopla contrato ao client TypeScript; NestJS mantém HTTP/OpenAPI consumível por qualquer consumer. API pública ou multi-client ainda converge em REST + @nestjs/swagger. Limites Edge, função de 20 linhas ou boot em <10 ms: NestJS não compete. Bootstrap carrega reflexão, decorators e grafo de módulos. Microsserviço enxuto na borda? Hono ou Fastify puro. Monólito modular, API corporativa ou backend com múltiplos squads? Custo de startup some frente a reestruturar Express sem convenções. Mais de um time, validação rigorosa, auth granular, testes com DI e possível fila ou gRPC: NestJS concentra o que você montaria manualmente. Não é a framework mais rápida para hello world. É a que menos exige reinventar arquitetura quando CRUD vira produto.Nota ·
- Pipeline híbrido: Biome e ESLint para segurança e performanceTime rodava ESLint completo no pre-commit. Commit de três linhas esperava 25 segundos. Dev desligava hook com --no-verify; regra de segurança virou sugestão. Rodar só Biome local e ESLint pesado no CI quebra o outro extremo: PR verde com any escapando porque subset do CI não espelhava o que importava. Divisão que funcionou: ferramenta rápida no caminho quente (save, commit), análise type-aware e SAST no PR. Não por branch, por gatilho do pipeline. Local: Biome no hook Biome formata e lint em milissegundos. Husky ou Lefthook chamam biome check --write ou biome ci antes do commit. Regra de estilo, import order, noUnusedVariables básico: feedback instantâneo. Dev não aprende a ignorar hook. ESLint completo com parserOptions.project no pre-commit punir quem commita often. Guarda ESLint para CI; local fica utilizável. CI: Biome + subset ESLint PR roda biome ci primeiro. Se alguém desabilitou hook, estilo inconsistente quebra cedo. Depois ESLint com config separada (eslint.ci.config.js ou --config) carregando só plugins que Biome não substitui: type-aware TypeScript e segurança. 40 segundos no CI não travam fluxo: quem pushou já seguiu em frente. Falha de no-floating-promises ou detect-object-injection bloqueia merge antes de main, que é onde barreira deve estar. Cache é obrigatório em repo grande: Segundo job sem cache em monorepo TypeScript vira gargalo de fila no GitHub Actions. Plugins que valem no subset do CI Biome cobre formatação e parte do lint estilo. ESLint no CI foca o que precisa de grafo de tipos ou heurística de segurança. @typescript-eslint com project: true pega Promise flutuando (no-floating-promises), await em valor não thenable e propagação de any silenciosa. Bug que só estoura em produção sob carga. eslint-plugin-security flagra require(variable), regex catastrophic backtracking e eval. eslint-plugin-no-unsanitized exige sanitização antes de innerHTML em código isomórfico que roda no server e no client. eslint-plugin-import-x com import/no-cycle evita ciclo A→B→A que vira undefined em runtime no Node. no-unused-modules acha export morto antes de virar dívida. eslint-plugin-sonarjs aponta função com complexidade cognitiva 40+ que ninguém revisa mais. eslint-plugin-unicorn é opcional no CI se Biome já cobre modern syntax; útil para replaceAll, preferência por Arrayat e padrões que Biome ainda não espelha. Manutenção Duas configs exigem disciplina: mudança de regra documentada em qual camada vive. Reunião de onboarding explica "Biome no commit, ESLint no PR". Sem isso, dev roda ESLint local achando que CI repete e perde tempo debugando diferença. Híbrido não é duplicar tudo duas vezes. É Biome como padrão de estilo no caminho quente e ESLint como auditor lento no portão de merge. Hook rápido vira hábito; hook lento vira --no-verify.Nota ·
- A falácia da API fullstack: use Hono e NestJSRoute Handler no Next.js parece backend de verdade até você precisar da mesma regra de negócio no app Flutter. Aí descobre que validação de pedido vive ao lado de layout JSX, teste de integração mocka next/headers e deploy de API amarra na mesma pipeline de preview do front. Extrair depois custa sprint inteiro: mover DTO, desfazer import circular com componente de UI, recriar auth que dependia de cookie do domínio do site. Server Actions pioram o acoplamento. Função callable do client vira superfície de API implícita, difícil de versionar e documentar para consumidor externo. Mobile, parceiro B2B ou worker em fila não chamam Server Action; precisam de HTTP estável com contrato explícito. Por que fullstack falha como API Framework de SSR nasceu para HTML. DI nativa, pipe de validação global, guard por role, módulo de fila: tudo existe como lib solta ou não existe. Time reinventa estrutura a cada projeto. Cold start em serverless pesa quando rota de API compartilha bundle com render de página. Hospedar na plataforma de front traz lock-in que backend puro evitaria. Separar API do repo de UI não é purismo. É permitir deploy, escala e contrato independentes. Hono: borda e microsserviços enxutos Hono usa Web Standard APIs (Request, Response, fetch). Roda em Cloudflare Workers, Bun, Deno e Node sem reescrever handler. RegExp Router evita árvore de middleware pesada; latência importa em BFF na borda e gateway na frente de serviço lento. Caso típico: front Next consome BFF Hono no Worker que agrega três APIs internas, cacheia resposta 30 s e esconde token de serviço. Binário pequeno, boot rápido, sem grafo de módulo Nest. Quando domínio cabe em dezenas de rotas e regra cabe na cabeça de uma pessoa, Hono segura. NestJS: domínios complexos Regra de pedido com desconto por segmento, estoque reservado, antifraude e fila de email não vive bem em arquivo route.ts de 400 linhas. NestJS impõe módulo, service, controller, guard, pipe. DI resolve grafo no bootstrap; teste troca repository por mock via @Module, não refactor em cascata. Monólito com cinco squads, fila RabbitMQ, cron de reconciliação e gRPC para serviço legado: convenção Nest evita cada time inventar pasta utils/ com lógica de negócio. @nestjs/microservices e @nestjs/schedule entram no mesmo padrão de handler HTTP. Hono e NestJS não competem no mesmo slot. Borda enxuta e BFF: Hono. Core de negócio que cresce por anos: NestJS. Repositório dedicado, OpenAPI publicado, CI só de backend. Front consome HTTP como qualquer outro client, sem importar tipo de app/actions/. Manter API dentro do Next só fecha enquanto único consumidor é o site na mesma base de código. Segundo consumidor aparece; dívida cobra juros.Nota ·
- Astro vs Next.js: o custo da preguiça no vibe codingPedi para a IA gerar landing page de produto SaaS. Veio Next.js com App Router, cinco dependências de UI e hidratação em página que só tinha hero, três cards e formulário de waitlist. Funcionava no preview. No 4G, First Contentful Paint passava de três segundos porque o bundle React precisava baixar antes de mostrar texto estático. O modelo aprendeu com milhões de repos React; npx create-next-app virou default para qualquer coisa com HTML. Blog, docs, portfolio e marketing site raramente precisam de SPA. São HTML com ilhas de interatividade: menu mobile, accordion de FAQ, toggle de tema. Next.js manda JavaScript para hidratar componente que poderia ser markup puro. Astro inverte: zero JS por default, componente interativo só onde você marcar com client:. O que muda no bundle Página Astro típica envia HTML renderizado no build. Componente React/Preact/Svelte entra só com diretiva explícita (client:load, client:visible, client:idle). Footer, header estático, copy de marketing ficam fora do bundle client-side. Next.js 13+ melhorou com Server Components, mas projeto pequeno ainda carrega runtime React, router client-side e chunk de hidratação para árvore que poderia ser string no build. Teste simples: abra DevTools, desabilite cache, recarregue landing estática em Next e em Astro equivalente. Compare transferred JS. Diferença de centenas de KB em página sem dashboard é comum. Astro, Preact e script inline Preact (3 kB gzip) cobre formulário com validação, carrinho mini, widget com estado. Reservar React 40 kB+ para isso é desperdício quando a página inteira não é app. Interação trivial não precisa de framework. Menu hamburger: Sem build step extra, sem hidratação, sem mismatch server/client. Astro aceita <script inline no .astro ou componente; escopo e bundling ficam sob controle. Onde Next.js ainda ganha Dashboard autenticado com tabelas filtráveis, drag-and-drop, estado global entre rotas: aí SPA ou Next com client components pesados faz sentido. Time já padronizado em React, design system pronto, deploy na Vercel com preview por PR. Forçar Astro nesse cenário só para "performance" vira gambiarra de islands em app que é app de verdade. O erro é usar Next por default no projeto que é 90% conteúdo estático porque o snippet da IA ou o tutorial de 2023 mandou. Astro + Preact + script inline força pergunta antes de cada client:load: isso precisa de JS no browser ou só no build? Rodapé, nav, seção de pricing estática não precisam de RSC com suspense boundary. Colocar React ali transfere complexidade para infra ou celular do visitante. Stack enxuta devolve performance que se perdeu quando gerar código ficou mais fácil que pensar arquitetura.Nota ·
- Go vs TypeScript: otimizando a runtime da APIMigrei serviço de thumbnail de Node para Go no mesmo hardware. CPU caiu de 80% para 25% sob carga igual. Não era código ruim em TypeScript: decode PNG, resize, encode WebP bloqueia event loop. Uma requisição pesada atrasa todas as outras no processo single-threaded. Go distribuiu trabalho em goroutines e usou núcleos que Node deixava ociosos. Escolher linguagem da API é escolher modelo de execução. V8 com event loop e JIT versus binário estático com scheduler nativo. Métrica manda mais que preferência de sintaxe. Modelo de execução Node brilha quando thread espera rede ou disco. Go brilha quando thread calcula. TypeScript compila para JS interpretado/JIT; Go compila para binário sem VM no caminho quente. Memória por conexão WebSocket aberta costuma ser menor em Go; milhares de sockets no mesmo pod viram problema de custo em Node antes de virar problema de código. Isso não torna Go "melhor". Torna Go adequado quando CPU ou concorrência densa é o gargalo medido, não chute. TypeScript: I/O bound e entrega rápida API que consulta Postgres, chama Stripe e monta JSON raramente morre na runtime. Latência P99 segue query lenta ou API externa. Tipos compartilhados com front (zod ou tRPC no monorepo) aceleram feature que muda toda semana. CRUD, webhook handler, orquestrador de fila leve: Node entrega rápido, contrata dev com stack conhecida, debug com ferramentas maduras. Microsserviço que só encadeia HTTP e publica evento não precisa de Go por reputação. Precisa de observabilidade e query indexada. Go: CPU bound e muitas conexões Proxy WebSocket com 20k clientes, worker de compressão, parser de log em stream, serviço de auth com bcrypt por requisição: event loop sofre. Goroutine barata permite um processo servir conexão longa sem thread OS por client. Cold start de binário pequeno ajuda em Lambda e Cloud Run quando função acorda frequente. Criptografia, serialização pesada, transformação de mídia: perfil clássico de extração para serviço Go enquanto resto do produto fica em TypeScript. Sinais para migrar (ou não) Olhe métricas antes de reescrever. CPU alta com I/O idle aponta compute no Node. Latência P99 subindo com throughput de conexões simultâneas aponta pressão no event loop. Memória linear com contagem de socket idem. | Sintoma | Provável gargalo | Caminho | |---------|------------------|---------| | CPU 70%+ com DB wait baixo | Compute JS | Extrair hot path para Go | | P99 domina query externa | I/O | Otimizar SQL/contrato, ficar em TS | | RAM sobe com WebSockets | Conexões | Go ou separar hub de conexão | | Time só TS, CRUD estável | Organizacional | Migrar só se métrica exigir | Reescrever tudo em Go por moda troca velocidade de produto por eficiência que talvez nunca use. Extrair um serviço quando flamegraph ou APM mostrar função quente: abordagem que já pagou em produção. TypeScript para entregar e iterar. Go para trecho que medidor apontou como limite de runtime.Nota ·