Preguntas para entrevista de Desarrollador Backend Senior (Node.js): Guía Completa

Milad Bonakdar
Autor
Domina el desarrollo backend avanzado con Node.js con 30 preguntas esenciales para entrevistas que cubren diseño de sistemas, patrones de arquitectura, optimización del rendimiento, escalabilidad, microservicios y liderazgo. Preparación perfecta para entrevistas de desarrollador backend senior.
Introducción
Esta guía completa contiene 30 preguntas esenciales para entrevistas que cubren el desarrollo backend avanzado con Node.js. Estas preguntas están diseñadas para ayudar a los desarrolladores backend senior a prepararse para las entrevistas, cubriendo conceptos clave en el Event Loop, Streams, Diseño de Sistemas y Optimización del Rendimiento. Cada pregunta incluye una respuesta detallada, una evaluación de rareza y una calificación de dificultad.
Como desarrollador senior, se espera que comprendas profundamente la naturaleza de un solo hilo de Node.js y cómo construir sistemas escalables y de alto rendimiento a su alrededor.
Conceptos Avanzados de Node.js (10 Preguntas)
1. Explica el Event Loop de Node.js en detalle. ¿Cuáles son las diferentes fases?
Respuesta: El Event Loop es lo que permite a Node.js realizar operaciones de E/S sin bloqueo a pesar de ser de un solo hilo. Descarga las operaciones al kernel del sistema siempre que sea posible.
- Fases:
- Timers: Ejecuta las devoluciones de llamada programadas por
setTimeout()ysetInterval(). . Pending Callbacks: Ejecuta las devoluciones de llamada de E/S diferidas a la siguiente iteración del bucle. . Idle, Prepare: Uso interno solamente. . Poll: Recupera nuevos eventos de E/S; ejecuta devoluciones de llamada relacionadas con E/S. Aquí es donde el bucle se bloquea si no hay otras tareas. . Check: Ejecuta las devoluciones de llamada programadas porsetImmediate(). . Close Callbacks: Ejecuta devoluciones de llamada de cierre (por ejemplo,socket.on('close', ...)).
- Timers: Ejecuta las devoluciones de llamada programadas por
Rareza: Muy Común Dificultad: Difícil
2. ¿Cuál es la diferencia entre process.nextTick() y setImmediate()?
Respuesta:
process.nextTick(): No forma parte del Event Loop. Se dispara inmediatamente después de que se completa la operación actual, pero antes de que continúe el Event Loop. Tiene mayor prioridad quesetImmediate(). El uso excesivo puede bloquear el Event Loop (inanición).setImmediate(): Forma parte de la fase Check del Event Loop. Se ejecuta después de la fase Poll.
Rareza: Común Dificultad: Media
3. ¿Cómo maneja Node.js la concurrencia si es de un solo hilo?
Respuesta: Node.js utiliza un modelo de E/S no bloqueante y basado en eventos.
- Hilo Principal: Ejecuta código JavaScript (motor V8).
- Libuv: Una biblioteca de C que proporciona el Event Loop y un pool de hilos (4 hilos por defecto).
- Mecanismo: Cuando se inicia una operación asíncrona (como E/S de archivos o una solicitud de red), Node.js la descarga a Libuv. Libuv utiliza su pool de hilos (para E/S de archivos, DNS) o mecanismos asíncronos del kernel del sistema (para la red). Cuando se completa la operación, la devolución de llamada se inserta en la cola del Event Loop para que la ejecute el hilo principal.
Rareza: Común Dificultad: Media
4. Explica los Streams en Node.js y sus tipos.
Respuesta: Los Streams son objetos que te permiten leer datos de una fuente o escribir datos en un destino en fragmentos continuos. Son eficientes en cuanto a memoria porque no necesitas cargar todos los datos en la memoria.
- Tipos:
- Readable: Para leer datos (por ejemplo,
fs.createReadStream). - Writable: Para escribir datos (por ejemplo,
fs.createWriteStream). - Duplex: Tanto legible como escribible (por ejemplo, sockets TCP).
- Transform: Streams dúplex donde la salida se calcula en función de la entrada (por ejemplo,
zlib.createGzip).
- Readable: Para leer datos (por ejemplo,
Rareza: Común Dificultad: Media
5. ¿Qué es Backpressure en Streams y cómo lo manejas?
Respuesta: El Backpressure se produce cuando un stream Readable envía datos más rápido de lo que el stream Writable puede procesarlos. Si no se maneja, el uso de la memoria aumentará hasta que el proceso se bloquee.
- Manejo:
.pipe(): La forma más fácil. Gestiona automáticamente el backpressure pausando el stream Readable cuando el búfer del stream Writable está lleno y reanudándolo cuando se vacía.- Manual: Escucha el evento
drainen el stream Writable y pausa/reanuda el stream Readable manualmente.
Rareza: Media Dificultad: Difícil
6. ¿Cómo funciona el módulo cluster?
Respuesta:
Dado que Node.js es de un solo hilo, se ejecuta en un solo núcleo de CPU. El módulo cluster te permite crear procesos hijo (workers) que comparten el mismo puerto del servidor.
- Proceso Maestro: Gestiona los workers.
- Procesos Worker: Cada uno ejecuta una instancia de tu aplicación.
- Beneficio: Te permite utilizar todos los núcleos de CPU disponibles, aumentando el rendimiento.
Rareza: Común Dificultad: Media
7. Worker Threads vs módulo Cluster: ¿Cuándo usar cuál?
Respuesta:
- Cluster: Crea procesos separados. Cada uno tiene su propio espacio de memoria e instancia de V8. Lo mejor para escalar servidores HTTP (ligado a E/S).
- Worker Threads: Crea hilos dentro de un único proceso. Comparten memoria (a través de
SharedArrayBuffer). Lo mejor para tareas intensivas en CPU (por ejemplo, procesamiento de imágenes, criptografía) para evitar bloquear el Event Loop principal.
Rareza: Media Dificultad: Difícil
8. ¿Cómo manejas las excepciones no capturadas y los rechazos de promesas no controlados?
Respuesta:
- Excepción No Capturada: Escucha
process.on('uncaughtException', cb). Por lo general, es mejor registrar el error y reiniciar el proceso (usando un administrador de procesos como PM2) porque el estado de la aplicación podría estar dañado. - Rechazo No Controlado: Escucha
process.on('unhandledRejection', cb). - Mejores Prácticas: Utiliza siempre bloques
try/catchy.catch()en las promesas.
Rareza: Común Dificultad: Fácil
9. ¿Cuál es el papel de package-lock.json?
Respuesta: Describe el árbol exacto que se generó, de modo que las instalaciones posteriores puedan generar árboles idénticos, independientemente de las actualizaciones de dependencias intermedias. Garantiza que tu proyecto funcione exactamente de la misma manera en todas las máquinas (CI/CD, otros desarrolladores).
Rareza: Común Dificultad: Fácil
10. Explica el concepto de Middleware en Express.js.
Respuesta:
Las funciones de middleware son funciones que tienen acceso al objeto de solicitud (req), al objeto de respuesta (res) y a la siguiente función de middleware en el ciclo de solicitud-respuesta de la aplicación (next).
- Tareas: Ejecutar código, modificar objetos req/res, finalizar el ciclo de solicitud-respuesta, llamar al siguiente middleware.
- Orden: Se ejecutan secuencialmente en el orden en que se definen.
Rareza: Común Dificultad: Fácil
Diseño y Arquitectura del Sistema (10 Preguntas)
11. ¿Cómo diseñarías una aplicación de chat en tiempo real?
Respuesta:
- Protocolo: WebSockets (usando
socket.ioows) para la comunicación full-duplex. - Backend: Node.js es ideal debido a su naturaleza basada en eventos que maneja muchas conexiones concurrentes.
- Escalado:
- Redis Pub/Sub: Si tienes varias instancias del servidor, un usuario conectado al Servidor A necesita enviar un mensaje a un usuario en el Servidor B. Redis Pub/Sub actúa como un intermediario de mensajes para transmitir mensajes a través de los servidores.
- Base de Datos:
- Mensajes: NoSQL (MongoDB/Cassandra) para un alto rendimiento de escritura.
- Usuarios: Relacional (PostgreSQL) o NoSQL.
Rareza: Muy Común Dificultad: Difícil
12. Microservicios en Node.js: Patrones de comunicación.
Respuesta:
- Síncrono: HTTP/REST o gRPC. Bueno para solicitudes/respuestas simples.
- Asíncrono: Colas de mensajes (RabbitMQ, Kafka, SQS). Bueno para desacoplar servicios y manejar picos de carga.
- Basado en Eventos: Los servicios emiten eventos, otros escuchan.
Rareza: Común Dificultad: Media
13. ¿Cómo manejas las Transacciones Distribuidas (Patrón Saga)?
Respuesta: En los microservicios, una transacción podría abarcar varios servicios. Es difícil garantizar ACID.
- Patrón Saga: Una secuencia de transacciones locales. Cada transacción local actualiza la base de datos y publica un evento o mensaje para activar la siguiente transacción local en la saga.
- Compensación: Si una transacción local falla, la saga ejecuta una serie de transacciones de compensación que deshacen los cambios realizados por las transacciones locales precedentes.
Rareza: Media Dificultad: Difícil
14. Explica el patrón Circuit Breaker.
Respuesta: Evita que una aplicación intente repetidamente ejecutar una operación que probablemente falle (por ejemplo, llamar a un microservicio inactivo).
- Estados:
- Cerrado: Las solicitudes pasan.
- Abierto: Las solicitudes fallan inmediatamente (fallo rápido) sin llamar al servicio.
- Semi-Abierto: Permite un número limitado de solicitudes para verificar si el servicio se ha recuperado.
Rareza: Media Dificultad: Media
15. ¿Cómo aseguras una API de Node.js?
Respuesta:
- Helmet: Establece varios encabezados HTTP para asegurar la aplicación.
- Limitación de Velocidad: Utiliza
express-rate-limitpara prevenir DDoS/Fuerza bruta. - Validación de Entrada: Utiliza bibliotecas como
JoioZod. - Autenticación: JWT o OAuth2.
- CORS: Configura correctamente para permitir solo dominios de confianza.
- Inyección NoSQL: Desinfecta las entradas contra la inyección de MongoDB.
Rareza: Común Dificultad: Media
16. ¿Qué es Serverless y cómo encaja con Node.js?
Respuesta: Serverless (por ejemplo, AWS Lambda) te permite ejecutar código sin aprovisionar ni administrar servidores.
- Encaje con Node.js: Node.js es excelente para serverless debido a su rápido tiempo de inicio (cold start) y su naturaleza ligera.
- Casos de Uso: Puntos finales de API, procesamiento de eventos (cargas de S3), tareas programadas.
Rareza: Media Dificultad: Media
17. Explica GraphQL vs REST. ¿Cuándo usar GraphQL?
Respuesta:
- REST: Múltiples puntos finales, sobre-extracción o sub-extracción de datos.
- GraphQL: Un único punto final, el cliente solicita exactamente lo que necesita.
- Usar GraphQL: Cuando tienes requisitos de datos complejos, varios clientes (web, móvil) que necesitan diferentes formas de datos, o para reducir los viajes de ida y vuelta de la red.
Rareza: Común Dificultad: Media
18. ¿Cómo implementas el Almacenamiento en Caché en Node.js?
Respuesta:
- En Memoria:
node-cache(bueno para una sola instancia, pero los datos se pierden al reiniciar y no se comparten). - Distribuido: Redis (estándar de la industria).
- Estrategias: Cache-Aside, Write-Through.
- Caché HTTP: Utiliza encabezados ETag, Cache-Control.
Rareza: Común Dificultad: Media
19. Pool de Conexiones de Base de Datos.
Respuesta: Abrir una nueva conexión de base de datos para cada solicitud es costoso.
- Pooling: Mantiene una caché de conexiones de base de datos que se pueden reutilizar.
- Node.js: Bibliotecas como
pg(PostgreSQL) omongoosemanejan el pooling automáticamente. Necesitas configurar el tamaño del pool en función de tu carga de trabajo y los límites de la DB.
Rareza: Media Dificultad: Media
20. ¿Cómo manejas las cargas de archivos en Node.js?
Respuesta:
- Multipart/form-data: La codificación estándar para las cargas de archivos.
- Bibliotecas:
multer(middleware para Express),formidable,busboy. - Almacenamiento: No almacenes archivos en el sistema de archivos del servidor (sin estado). Carga en el almacenamiento en la nube como AWS S3. Transmite el archivo directamente a S3 para evitar cargarlo en la memoria.
Rareza: Común Dificultad: Media
Rendimiento y Pruebas (10 Preguntas)
21. ¿Cómo depuras una fuga de memoria en Node.js?
Respuesta:
- Síntomas: Aumento del uso de memoria con el tiempo (RSS), eventual bloqueo.
- Herramientas:
- Node.js Inspector: Flag
--inspect, conéctate con Chrome DevTools. - Heap Snapshots: Toma snapshots del heap y compáralos para encontrar objetos que no están siendo recolectados por el recolector de basura.
process.memoryUsage(): Monitoriza programáticamente.
- Node.js Inspector: Flag
Rareza: Común Dificultad: Difícil
22. Perfilado de Aplicaciones Node.js.
Respuesta: El perfilado ayuda a identificar cuellos de botella de la CPU.
- Profiler Incorporado:
node --prof app.js. Genera un archivo de registro. Procésalo connode --prof-process isolate-0x...log. - Clinic.js: Un conjunto de herramientas (
clinic doctor,clinic flame,clinic bubbleprof) para diagnosticar problemas de rendimiento.
Rareza: Media Dificultad: Difícil
23. Explica la regla "No Bloquear el Event Loop".
Respuesta: Dado que solo hay un hilo, si ejecutas una operación síncrona de larga duración (por ejemplo, cálculo pesado, lectura síncrona de archivos, regex compleja), el Event Loop se detiene. No se pueden procesar otras solicitudes.
- Solución: Divide el cálculo (setImmediate), usa Worker Threads o descarga a un microservicio.
Rareza: Muy Común Dificultad: Fácil
24. Pruebas Unitarias vs Pruebas de Integración en Node.js.
Respuesta:
- Pruebas Unitarias: Probar funciones/módulos individuales de forma aislada. Simular dependencias. (Herramientas: Jest, Mocha, Chai).
- Pruebas de Integración: Probar cómo funcionan los módulos juntos (por ejemplo, punto final de API + Base de Datos). (Herramientas: Supertest).
Rareza: Común Dificultad: Fácil
25. ¿Qué es TDD (Desarrollo Dirigido por Pruebas)?
Respuesta: Un proceso de desarrollo donde escribes la prueba antes del código.
- Escribe una prueba que falle (Rojo).
- Escribe el código mínimo para pasar la prueba (Verde).
- Refactoriza el código (Refactorizar).
Rareza: Media Dificultad: Media
26. ¿Cómo manejas el registro en una aplicación Node.js de producción?
Respuesta:
- No uses
console.log: Es síncrono (bloqueante) al escribir en un terminal/archivo en algunos casos y carece de estructura. - Usa un Logger: Winston, Bunyan o Pino.
- Estructura: Formato JSON para facilitar el análisis por parte de las herramientas de gestión de registros (ELK Stack, Datadog).
- Niveles: Error, Warn, Info, Debug.
Rareza: Común Dificultad: Fácil
27. Explica el Versionamiento Semántico (SemVer).
Respuesta:
Formato: MAJOR.MINOR.PATCH (por ejemplo, 1.2.3).
- MAJOR: Cambios incompatibles en la API.
- MINOR: Funcionalidad compatible con versiones anteriores.
- PATCH: Correcciones de errores compatibles con versiones anteriores.
^vs~:^1.2.3actualiza a<2.0.0.~1.2.3actualiza a<1.3.0.
Rareza: Común Dificultad: Fácil
28. ¿Qué son las Variables de Entorno y cómo las gestionas?
Respuesta:
- Propósito: Configuración que varía entre entornos (Desarrollo, Staging, Producción) como URLs de DB, claves de API.
- Uso:
process.env.VARIABLE_NAME. - Gestión: Archivos
.env(usando el paquetedotenv) para el desarrollo local. En producción, configúralas en el sistema operativo o en la configuración del contenedor/plataforma.
Rareza: Común Dificultad: Fácil
29. ¿Cómo implementas una aplicación Node.js?
Respuesta:
- Administrador de Procesos: PM2 (mantiene la aplicación activa, maneja los reinicios, registra).
- Proxy Inverso: Nginx (maneja SSL, archivos estáticos, balanceo de carga) -> Aplicación Node.js.
- Contenedorización: Docker (estándar).
- Orquestación: Kubernetes.
- CI/CD: GitHub Actions, Jenkins.
Rareza: Común Dificultad: Media
30. ¿Qué es Event Emitter?
Respuesta:
El módulo events es el núcleo de la arquitectura basada en eventos de Node.js.
- Uso:
- Muchos módulos centrales lo extienden:
fs.ReadStream,net.Server,http.Server.
Rareza: Común Dificultad: Fácil


