Perguntas Node.js para entrevista backend sênior

Milad Bonakdar
Autor
Prepare-se para entrevistas backend sênior com Node.js com 30 perguntas práticas sobre event loop, streams, segurança de API, design de sistemas, escala, desempenho, testes e produção.
Perguntas Node.js para entrevista backend sênior
Entrevistas sênior de Node.js normalmente avaliam mais do que sintaxe. Espere perguntas sobre como você raciocina sobre event loop, backpressure, limites de API, sistemas distribuídos, observabilidade, segurança e decisões de produção. As melhores respostas conectam os detalhes internos do Node.js a decisões tomadas em serviços reais.
Use estas 30 perguntas para praticar explicações claras de nível sênior. Para cada tema, prepare um exemplo seu: uma decisão de escala, um problema de desempenho, um incidente de confiabilidade ou um tradeoff de design que você consiga explicar sem exagerar.
Conceitos Avançados de Node.js (10 Perguntas)
1. Explique o Loop de Eventos do Node.js em detalhes. Quais são as diferentes fases?
Resposta: O event loop permite que o Node.js execute E/S não bloqueante enquanto o JavaScript roda, por padrão, em uma única thread principal. O sistema operacional ou a libuv lidam com trabalho que fica aguardando, e depois os callbacks entram na fila para o JavaScript executar.
- Timers: Executa callbacks de
setTimeout()esetInterval(). Em versões modernas do Node.js, o trabalho na fase poll pode afetar o momento exato da execução. - Pending callbacks: Executa alguns callbacks de E/S de baixo nível adiados para a próxima iteração.
- Idle, prepare: Fases internas da libuv.
- Poll: Recupera novos eventos de E/S e executa callbacks relacionados. Se a fila estiver vazia, o Node.js pode esperar E/S ou avançar quando houver callbacks de
setImmediate(). - Check: Executa callbacks de
setImmediate(). - Close callbacks: Executa handlers de fechamento como
socket.on('close', ...).
Uma resposta sênior também menciona as filas de microtasks: process.nextTick() roda antes das promises, e ambas rodam antes de o event loop continuar. Uso excessivo pode deixar a E/S sem vez.
Raridade: Muito Comum Dificuldade: Difícil
2. Qual é a diferença entre process.nextTick() e setImmediate()?
Resposta:
process.nextTick(): Não faz parte do Loop de Eventos. Ele é acionado imediatamente após a conclusão da operação atual, mas antes que o Loop de Eventos continue. Tem maior prioridade do quesetImmediate(). O uso excessivo pode bloquear o Loop de Eventos (inanição).setImmediate(): Faz parte da fase Check do Loop de Eventos. É executado após a fase Poll.
Raridade: Comum Dificuldade: Média
3. Como o Node.js lida com a concorrência se ele é single-threaded?
Resposta: O Node.js usa um modelo de E/S não bloqueante e orientado a eventos.
- Thread Principal: Executa código JavaScript (motor V8).
- Libuv: Uma biblioteca C que fornece o Loop de Eventos e um pool de threads (padrão de 4 threads).
- Mecanismo: Quando uma operação assíncrona (como E/S de arquivo ou solicitação de rede) é iniciada, o Node.js a descarrega para a Libuv. A Libuv usa seu pool de threads (para E/S de arquivo, DNS) ou mecanismos assíncronos do kernel do sistema (para rede). Quando a operação é concluída, o callback é adicionado à fila do Loop de Eventos para ser executado pela thread principal.
Raridade: Comum Dificuldade: Média
4. Explique Streams no Node.js e seus tipos.
Resposta: Streams são objetos que permitem ler dados de uma fonte ou gravar dados em um destino em blocos contínuos. Eles são eficientes em termos de memória porque você não precisa carregar todos os dados na memória.
- Tipos:
- Readable: Para leitura de dados (por exemplo,
fs.createReadStream). - Writable: Para gravação de dados (por exemplo,
fs.createWriteStream). - Duplex: Leitura e gravação (por exemplo, sockets TCP).
- Transform: Streams Duplex onde a saída é calculada com base na entrada (por exemplo,
zlib.createGzip).
- Readable: Para leitura de dados (por exemplo,
Raridade: Comum Dificuldade: Média
5. O que é Backpressure em Streams e como você o lida?
Resposta: Backpressure ocorre quando um stream readable produz dados mais rápido do que a parte writable consegue consumir. Se você ignorar isso, os buffers crescem, o garbage collector trabalha mais e o processo pode ficar sem memória.
- Use
stream.pipeline()oupipelinedenode:stream/promises: Conecta streams, propaga erros e limpa recursos corretamente. - Respeite
.write(): Se retornarfalse, espere o eventodrainantes de escrever mais. - Ajuste com cuidado:
highWaterMarkpode ajudar em workloads específicos, mas aumentar sem medir só desloca a pressão para a memória. - Para uploads: Faça streaming direto para object storage ou uma pipeline de processamento em vez de carregar arquivos inteiros na memória.
Raridade: Média Dificuldade: Difícil
6. Como o módulo cluster funciona?
Resposta:
Como o Node.js é single-threaded, ele é executado em um único núcleo da CPU. O módulo cluster permite criar processos filhos (workers) que compartilham a mesma porta do servidor.
- Processo Master: Gerencia os workers.
- Processos Worker: Cada um executa uma instância do seu aplicativo.
- Benefício: Permite utilizar todos os núcleos de CPU disponíveis, aumentando a taxa de transferência.
Raridade: Comum Dificuldade: Média
7. Worker Threads vs módulo Cluster: Quando usar qual?
Resposta:
- Cluster: Cria processos separados. Cada um tem seu próprio espaço de memória e instância do V8. Melhor para escalar servidores HTTP (ligado a E/S).
- Worker Threads: Cria threads dentro de um único processo. Eles compartilham memória (via
SharedArrayBuffer). Melhor para tarefas intensivas em CPU (por exemplo, processamento de imagem, criptografia) para evitar bloquear o Loop de Eventos principal.
Raridade: Média Dificuldade: Difícil
8. Como você lida com exceções não capturadas e rejeições de promise não tratadas?
Resposta:
- Exceção Não Capturada: Ouça
process.on('uncaughtException', cb). Geralmente é melhor registrar o erro e reiniciar o processo (usando um gerenciador de processos como o PM2) porque o estado do aplicativo pode estar corrompido. - Rejeição Não Tratada: Ouça
process.on('unhandledRejection', cb). - Melhor Prática: Sempre use blocos
try/catche.catch()em promises.
Raridade: Comum Dificuldade: Fácil
9. Qual é o papel do package-lock.json?
Resposta: Ele descreve a árvore exata que foi gerada, de modo que as instalações subsequentes sejam capazes de gerar árvores idênticas, independentemente das atualizações de dependência intermediárias. Ele garante que seu projeto funcione exatamente da mesma maneira em todas as máquinas (CI/CD, outros desenvolvedores).
Raridade: Comum Dificuldade: Fácil
10. Explique o conceito de Middleware no Express.js.
Resposta:
Funções de middleware são funções que têm acesso ao objeto de solicitação (req), ao objeto de resposta (res) e à próxima função de middleware no ciclo de solicitação-resposta do aplicativo (next).
- Tarefas: Executar código, modificar objetos req/res, finalizar o ciclo de solicitação-resposta, chamar o próximo middleware.
- Ordem: Eles são executados sequencialmente na ordem em que são definidos.
Raridade: Comum Dificuldade: Fácil
Design e Arquitetura de Sistemas (10 Perguntas)
11. Como você projetaria um aplicativo de chat em tempo real?
Resposta:
- Protocolo: WebSockets (usando
socket.ioouws) para comunicação full-duplex. - Backend: O Node.js é ideal devido à sua natureza orientada a eventos, lidando com muitas conexões simultâneas.
- Escalonamento:
- Redis Pub/Sub: Se você tiver várias instâncias de servidor, um usuário conectado ao Servidor A precisa enviar uma mensagem para um usuário no Servidor B. O Redis Pub/Sub atua como um message broker para transmitir mensagens entre os servidores.
- Banco de Dados:
- Mensagens: NoSQL (MongoDB/Cassandra) para alta taxa de transferência de gravação.
- Usuários: Relacional (PostgreSQL) ou NoSQL.
Raridade: Muito Comum Dificuldade: Difícil
12. Microsserviços no Node.js: Padrões de comunicação.
Resposta:
- Síncrono: HTTP/REST ou gRPC. Bom para solicitação/resposta simples.
- Assíncrono: Filas de Mensagens (RabbitMQ, Kafka, SQS). Bom para desacoplar serviços e lidar com picos de carga.
- Orientado a Eventos: Os serviços emitem eventos, outros ouvem.
Raridade: Comum Dificuldade: Média
13. Como você lida com Transações Distribuídas (Padrão Saga)?
Resposta: Em microsserviços, uma transação pode abranger vários serviços. ACID é difícil de garantir.
- Padrão Saga: Uma sequência de transações locais. Cada transação local atualiza o banco de dados e publica um evento ou mensagem para acionar a próxima transação local na saga.
- Compensação: Se uma transação local falhar, a saga executa uma série de transações compensatórias que desfazem as alterações feitas pelas transações locais precedentes.
Raridade: Média Dificuldade: Difícil
14. Explique o padrão Circuit Breaker.
Resposta: Ele impede que um aplicativo tente repetidamente executar uma operação que provavelmente falhará (por exemplo, chamar um microsserviço inativo).
- Estados:
- Fechado: As solicitações passam.
- Aberto: As solicitações falham imediatamente (fast fail) sem chamar o serviço.
- Semi-Aberto: Permite um número limitado de solicitações para verificar se o serviço se recuperou.
Raridade: Média Dificuldade: Média
15. Como você protege uma API Node.js?
Resposta: Uma resposta forte começa com modelagem de ameaças, não apenas uma lista de pacotes. Para uma API Node.js, cubra:
- Autenticação e autorização: Valide identidade, aplique permissões no nível do objeto e não confie em IDs de usuário ou tenant enviados pelo cliente.
- Validação de entrada: Valide tipo, formato, intervalo, content type e tamanho da requisição na borda com Zod, Joi ou similares.
- Transporte e headers: Use HTTPS, cookies seguros quando aplicável, allowlists de CORS e headers com Helmet ou controles da plataforma.
- Controles de abuso: Rate limits, timeouts, limites de tamanho do body e reverse proxy contra clientes lentos ou de alto volume.
- Dependências e segredos: Trave dependências, monitore pacotes vulneráveis, mantenha segredos fora do código e rotacione credenciais comprometidas.
- Observabilidade: Registre falhas relevantes para segurança sem vazar dados sensíveis.
Raridade: Comum Dificuldade: Média
16. O que é Serverless e como ele se encaixa com o Node.js?
Resposta: Serverless (por exemplo, AWS Lambda) permite que você execute código sem provisionar ou gerenciar servidores.
- Encaixe com Node.js: O Node.js é excelente para serverless devido ao seu rápido tempo de inicialização (cold start) e natureza leve.
- Casos de Uso: Pontos de extremidade de API, processamento de eventos (uploads S3), tarefas agendadas.
Raridade: Média Dificuldade: Média
17. Explique GraphQL vs REST. Quando usar GraphQL?
Resposta:
- REST: Vários endpoints, over-fetching ou under-fetching de dados.
- GraphQL: Endpoint único, o cliente pede exatamente o que precisa.
- Use GraphQL: Quando você tem requisitos de dados complexos, vários clientes (web, mobile) precisando de diferentes formatos de dados ou para reduzir as viagens de ida e volta na rede.
Raridade: Comum Dificuldade: Média
18. Como você implementa o Caching no Node.js?
Resposta:
- In-Memory:
node-cache(bom para instância única, mas os dados são perdidos na reinicialização e não são compartilhados). - Distribuído: Redis (padrão da indústria).
- Estratégias: Cache-Aside, Write-Through.
- HTTP Caching: Use os cabeçalhos ETag, Cache-Control.
Raridade: Comum Dificuldade: Média
19. Pool de Conexões do Banco de Dados.
Resposta: Abrir uma nova conexão de banco de dados para cada solicitação é caro.
- Pooling: Mantém um cache de conexões de banco de dados que podem ser reutilizadas.
- Node.js: Bibliotecas como
pg(PostgreSQL) oumongooselidam com o pooling automaticamente. Você precisa configurar o tamanho do pool com base na sua carga de trabalho e nos limites do DB.
Raridade: Média Dificuldade: Média
20. Como você lida com uploads de arquivos no Node.js?
Resposta:
- Multipart/form-data: A codificação padrão para uploads de arquivos.
- Bibliotecas:
multer(middleware para Express),formidable,busboy. - Armazenamento: Não armazene arquivos no sistema de arquivos do servidor (statelessness). Faça upload para o armazenamento em nuvem como AWS S3. Transmita o arquivo diretamente para o S3 para evitar carregá-lo na memória.
Raridade: Comum Dificuldade: Média
Desempenho e Testes (10 Perguntas)
21. Como você depura um vazamento de memória no Node.js?
Resposta:
- Sintomas: Aumento do uso de memória ao longo do tempo (RSS), eventual travamento.
- Ferramentas:
- Node.js Inspector: flag
--inspect, conecte-se com o Chrome DevTools. - Heap Snapshots: Tire snapshots e compare-os para encontrar objetos que não estão sendo coletados pelo garbage collector.
process.memoryUsage(): Monitore programaticamente.
- Node.js Inspector: flag
Raridade: Comum Dificuldade: Difícil
22. Profiling de Aplicações Node.js.
Resposta: O profiling ajuda a identificar gargalos de CPU.
- Profiler Embutido:
node --prof app.js. Gera um arquivo de log. Processe-o comnode --prof-process isolate-0x...log. - Clinic.js: Um conjunto de ferramentas (
clinic doctor,clinic flame,clinic bubbleprof) para diagnosticar problemas de desempenho.
Raridade: Média Dificuldade: Difícil
23. Explique a regra "Não Bloqueie o Loop de Eventos".
Resposta: Como existe apenas uma thread, se você executar uma operação síncrona de longa duração (por exemplo, cálculo pesado, leitura síncrona de arquivo, regex complexo), o Loop de Eventos para. Nenhuma outra solicitação pode ser processada.
- Solução: Divida o cálculo (setImmediate), use Worker Threads ou descarregue para um microsserviço.
Raridade: Muito Comum Dificuldade: Fácil
24. Teste Unitário vs Teste de Integração no Node.js.
Resposta:
- Teste Unitário: Testar funções/módulos individuais isoladamente. Mockar dependências. (Ferramentas: Jest, Mocha, Chai).
- Teste de Integração: Testar como os módulos funcionam juntos (por exemplo, endpoint da API + Banco de Dados). (Ferramentas: Supertest).
Raridade: Comum Dificuldade: Fácil
25. O que é TDD (Test Driven Development)?
Resposta: Um processo de desenvolvimento onde você escreve o teste antes do código.
- Escreva um teste que falhe (Red).
- Escreva o código mínimo para passar no teste (Green).
- Refatore o código (Refactor).
Raridade: Média Dificuldade: Média
26. Como você lida com o logging em um aplicativo Node.js de produção?
Resposta: Logging em produção deve ser estruturado, pesquisável e seguro para ferramentas de observabilidade.
- Use um logger: Pino, Winston ou o logger da plataforma em vez de
console.logespalhados. - Estrutura: Emita JSON com request ID, identificadores seguros de usuário ou tenant, rota, status, latência e metadados do erro.
- Níveis: Use error, warn, info e debug de forma consistente.
- Redação: Nunca registre tokens, senhas, dados completos de pagamento ou conteúdo privado de usuários.
- Correlação: Conecte logs com métricas e traces para depurar incidentes entre serviços.
Raridade: Comum Dificuldade: Fácil
27. Explique o Versionamento Semântico (SemVer).
Resposta:
Formato: MAJOR.MINOR.PATCH (por exemplo, 1.2.3).
- MAJOR: Mudanças incompatíveis na API.
- MINOR: Funcionalidade compatível com versões anteriores.
- PATCH: Correções de bugs compatíveis com versões anteriores.
^vs~:^1.2.3atualiza para<2.0.0.~1.2.3atualiza para<1.3.0.
Raridade: Comum Dificuldade: Fácil
28. O que são Variáveis de Ambiente e como você as gerencia?
Resposta:
- Propósito: Configuração que varia entre ambientes (Dev, Staging, Prod) como URLs de DB, chaves de API.
- Uso:
process.env.VARIABLE_NAME. - Gerenciamento: Arquivos
.env(usando o pacotedotenv) para desenvolvimento local. Em produção, defina-os nas configurações do SO ou do contêiner/plataforma.
Raridade: Comum Dificuldade: Fácil
29. Como você implanta um aplicativo Node.js?
Resposta:
- Gerenciador de Processos: PM2 (mantém o aplicativo ativo, lida com reinicializações, logs).
- Reverse Proxy: Nginx (lida com SSL, arquivos estáticos, balanceamento de carga) -> Aplicativo Node.js.
- Containerização: Docker (padrão).
- Orquestração: Kubernetes.
- CI/CD: GitHub Actions, Jenkins.
Raridade: Comum Dificuldade: Média
30. O que é Event Emitter?
Resposta:
O módulo events é o núcleo da arquitetura orientada a eventos do Node.js.
- Uso:
- Muitos módulos principais o estendem:
fs.ReadStream,net.Server,http.Server.
Raridade: Comum Dificuldade: Fácil


