dicembre 21, 2025
11 min di lettura

Domande Node.js per colloqui backend senior

interview
career-advice
job-search
Domande Node.js per colloqui backend senior
Milad Bonakdar

Milad Bonakdar

Autore

Preparati ai colloqui backend senior Node.js con 30 domande pratiche su event loop, stream, sicurezza API, system design, scalabilità, performance, testing e responsabilità in produzione.


Domande Node.js per colloqui backend senior

Nei colloqui senior su Node.js raramente basta conoscere la sintassi. Aspettati domande su event loop, backpressure, confini delle API, sistemi distribuiti, osservabilità, sicurezza e compromessi di produzione. Le risposte migliori collegano gli internals di Node.js a decisioni prese su servizi reali.

Usa queste 30 domande per allenare spiegazioni concise e di livello senior. Per ogni argomento prepara un esempio tuo: una scelta di scalabilità, un problema di performance, un incidente di affidabilità o un tradeoff architetturale che sai spiegare senza esagerare.


Concetti Avanzati di Node.js (10 Domande)

1. Spiega in dettaglio l'Event Loop di Node.js. Quali sono le diverse fasi?

Risposta: L’event loop permette a Node.js di eseguire I/O non bloccante mentre JavaScript gira, per impostazione predefinita, su un solo thread principale. Il sistema operativo o libuv gestiscono il lavoro in attesa, poi i callback vengono messi in coda per essere eseguiti da JavaScript.

  • Timers: Esegue callback di setTimeout() e setInterval(). Nelle versioni moderne di Node.js, il lavoro nella fase poll può influenzare il timing.
  • Pending callbacks: Esegue alcuni callback I/O di basso livello rimandati all’iterazione successiva.
  • Idle, prepare: Fasi interne di libuv.
  • Poll: Recupera nuovi eventi I/O ed esegue i callback collegati. Se la coda è vuota, Node.js può attendere I/O o avanzare quando ci sono callback setImmediate().
  • Check: Esegue callback setImmediate().
  • Close callbacks: Esegue handler di chiusura come socket.on('close', ...).

Una risposta senior cita anche le microtask queue: process.nextTick() viene eseguito prima delle promise, e entrambe prima che l’event loop continui. L’uso eccessivo può affamare l’I/O.

Rarità: Molto Comune Difficoltà: Difficile


2. Qual è la differenza tra process.nextTick() e setImmediate()?

Risposta:

  • process.nextTick(): Non fa parte dell'Event Loop. Si attiva immediatamente dopo il completamento dell'operazione corrente, ma prima che l'Event Loop continui. Ha una priorità più alta di setImmediate(). Un uso eccessivo può bloccare l'Event Loop (starvation).
  • setImmediate(): Fa parte della fase Check dell'Event Loop. Viene eseguito dopo la fase Poll.

Rarità: Comune Difficoltà: Media


3. Come gestisce Node.js la concorrenza se è single-threaded?

Risposta: Node.js utilizza un modello I/O non bloccante e guidato dagli eventi.

  • Thread Principale: Esegue codice JavaScript (motore V8).
  • Libuv: Una libreria C che fornisce l'Event Loop e un pool di thread (predefinito 4 thread).
  • Meccanismo: Quando viene avviata un'operazione asincrona (come I/O di file o richiesta di rete), Node.js la delega a Libuv. Libuv utilizza il suo pool di thread (per I/O di file, DNS) o meccanismi asincroni del kernel del sistema (per la rete). Quando l'operazione è completa, il callback viene inserito nella coda dell'Event Loop per essere eseguito dal thread principale.

Rarità: Comune Difficoltà: Media


4. Spiega gli Stream in Node.js e i loro tipi.

Risposta: Gli Stream sono oggetti che consentono di leggere dati da una sorgente o scrivere dati in una destinazione in blocchi continui. Sono efficienti in termini di memoria perché non è necessario caricare l'intero set di dati in memoria.

  • Tipi:
    1. Readable: Per la lettura di dati (ad esempio, fs.createReadStream).
    2. Writable: Per la scrittura di dati (ad esempio, fs.createWriteStream).
    3. Duplex: Sia leggibili che scrivibili (ad esempio, socket TCP).
    4. Transform: Stream Duplex in cui l'output viene calcolato in base all'input (ad esempio, zlib.createGzip).

Rarità: Comune Difficoltà: Media


5. Cos'è la Backpressure negli Stream e come la gestisci?

Risposta: La backpressure si verifica quando uno stream readable produce dati più velocemente di quanto la parte writable riesca a consumarli. Se la ignori, i buffer crescono, la garbage collection lavora di più e il processo può esaurire memoria.

  • Usa stream.pipeline() o pipeline da node:stream/promises: Collega gli stream, propaga gli errori e ripulisce le risorse correttamente.
  • Rispetta .write(): Se restituisce false, aspetta drain prima di scrivere altro.
  • Tuning con misura: highWaterMark può aiutare in workload specifici, ma aumentarlo senza misure sposta solo pressione in memoria.
  • Per upload: Fai streaming diretto verso object storage o una pipeline di elaborazione invece di bufferizzare file interi in memoria.

Rarità: Media Difficoltà: Difficile


6. Come funziona il modulo cluster?

Risposta: Poiché Node.js è single-threaded, viene eseguito su un singolo core della CPU. Il modulo cluster consente di creare processi figlio (worker) che condividono la stessa porta del server.

  • Processo Master: Gestisce i worker.
  • Processi Worker: Ognuno esegue un'istanza della tua applicazione.
  • Vantaggio: Consente di utilizzare tutti i core della CPU disponibili, aumentando la velocità di trasmissione.

Rarità: Comune Difficoltà: Media


7. Worker Threads vs modulo Cluster: quando usare quale?

Risposta:

  • Cluster: Crea processi separati. Ognuno ha il proprio spazio di memoria e istanza V8. Ideale per scalare server HTTP (I/O bound).
  • Worker Threads: Crea thread all'interno di un singolo processo. Condividono la memoria (tramite SharedArrayBuffer). Ideale per attività ad alta intensità di CPU (ad esempio, elaborazione di immagini, crittografia) per evitare di bloccare l'Event Loop principale.

Rarità: Media Difficoltà: Difficile


8. Come gestisci le eccezioni non gestite e i rejection di promise non gestiti?

Risposta:

  • Eccezione Non Gestita: Ascolta process.on('uncaughtException', cb). Di solito è meglio registrare l'errore e riavviare il processo (utilizzando un gestore di processi come PM2) perché lo stato dell'applicazione potrebbe essere corrotto.
  • Rejection Non Gestito: Ascolta process.on('unhandledRejection', cb).
  • Best Practice: Usa sempre blocchi try/catch e .catch() sulle promise.

Rarità: Comune Difficoltà: Facile


9. Qual è il ruolo di package-lock.json?

Risposta: Descrive l'albero esatto che è stato generato, in modo che le installazioni successive siano in grado di generare alberi identici, indipendentemente dagli aggiornamenti delle dipendenze intermedie. Assicura che il tuo progetto funzioni esattamente allo stesso modo su ogni macchina (CI/CD, altri sviluppatori).

Rarità: Comune Difficoltà: Facile


10. Spiega il concetto di Middleware in Express.js.

Risposta: Le funzioni middleware sono funzioni che hanno accesso all'oggetto richiesta (req), all'oggetto risposta (res) e alla successiva funzione middleware nel ciclo richiesta-risposta dell'applicazione (next).

  • Compiti: Eseguire codice, modificare gli oggetti req/res, terminare il ciclo richiesta-risposta, chiamare il middleware successivo.
  • Ordine: Vengono eseguite sequenzialmente nell'ordine in cui sono definite.

Rarità: Comune Difficoltà: Facile


Progettazione del Sistema e Architettura (10 Domande)

11. Come progetteresti un'applicazione di chat in tempo reale?

Risposta:

  • Protocollo: WebSockets (utilizzando socket.io o ws) per la comunicazione full-duplex.
  • Backend: Node.js è ideale grazie alla sua natura guidata dagli eventi che gestisce molte connessioni simultanee.
  • Scaling:
    • Redis Pub/Sub: Se hai più istanze del server, un utente connesso al Server A deve inviare un messaggio a un utente sul Server B. Redis Pub/Sub funge da message broker per trasmettere messaggi tra i server.
  • Database:
    • Messaggi: NoSQL (MongoDB/Cassandra) per un'elevata velocità di scrittura.
    • Utenti: Relazionale (PostgreSQL) o NoSQL.

Rarità: Molto Comune Difficoltà: Difficile


12. Microservizi in Node.js: schemi di comunicazione.

Risposta:

  • Sincrono: HTTP/REST o gRPC. Buono per semplici richieste/risposte.
  • Asincrono: Code di messaggi (RabbitMQ, Kafka, SQS). Buono per disaccoppiare i servizi e gestire i picchi di carico.
  • Event-Driven: I servizi emettono eventi, altri ascoltano.

Rarità: Comune Difficoltà: Media


13. Come gestisci le transazioni distribuite (Saga Pattern)?

Risposta: Nei microservizi, una transazione potrebbe estendersi a più servizi. È difficile garantire ACID.

  • Saga Pattern: Una sequenza di transazioni locali. Ogni transazione locale aggiorna il database e pubblica un evento o un messaggio per attivare la transazione locale successiva nella saga.
  • Compensazione: Se una transazione locale fallisce, la saga esegue una serie di transazioni di compensazione che annullano le modifiche apportate dalle transazioni locali precedenti.

Rarità: Media Difficoltà: Difficile


14. Spiega il pattern Circuit Breaker.

Risposta: Impedisce a un'applicazione di tentare ripetutamente di eseguire un'operazione che probabilmente fallirà (ad esempio, chiamare un microservizio inattivo).

  • Stati:
    • Closed: Le richieste passano attraverso.
    • Open: Le richieste falliscono immediatamente (fast fail) senza chiamare il servizio.
    • Half-Open: Consente a un numero limitato di richieste di verificare se il servizio si è ripreso.

Rarità: Media Difficoltà: Media


15. Come proteggi un'API Node.js?

Risposta: Una risposta forte parte dal threat modeling, non da un elenco di pacchetti. Per un’API Node.js copri:

  • Autenticazione e autorizzazione: Valida l’identità, applica permessi a livello oggetto e non fidarti degli ID utente o tenant inviati dal client.
  • Validazione input: Valida tipo, formato, range, content type e dimensione della richiesta al confine con Zod, Joi o simili.
  • Trasporto e header: Usa HTTPS, cookie sicuri quando servono, allowlist CORS e header tramite Helmet o controlli di piattaforma.
  • Controlli anti-abuso: Rate limit, timeout, limiti di body size e reverse proxy contro client lenti o ad alto volume.
  • Dipendenze e segreti: Blocca le dipendenze, monitora pacchetti vulnerabili, tieni i segreti fuori dal codice e ruota credenziali compromesse.
  • Osservabilità: Logga errori rilevanti per la sicurezza senza esporre dati sensibili.

Rarità: Comune Difficoltà: Media


16. Cos'è Serverless e come si adatta a Node.js?

Risposta: Serverless (ad esempio, AWS Lambda) ti consente di eseguire codice senza provisioning o gestione di server.

  • Adattamento Node.js: Node.js è eccellente per serverless grazie al suo rapido tempo di avvio (cold start) e alla sua natura leggera.
  • Casi d'uso: Endpoint API, elaborazione di eventi (caricamenti S3), attività pianificate.

Rarità: Media Difficoltà: Media


17. Spiega GraphQL vs REST. Quando usare GraphQL?

Risposta:

  • REST: Endpoint multipli, over-fetching o under-fetching dei dati.
  • GraphQL: Endpoint singolo, il client chiede esattamente ciò di cui ha bisogno.
  • Usa GraphQL: Quando hai requisiti di dati complessi, più client (web, mobile) che necessitano di forme di dati diverse o per ridurre i round trip di rete.

Rarità: Comune Difficoltà: Media


18. Come implementi la Caching in Node.js?

Risposta:

  • In-Memory: node-cache (buono per una singola istanza, ma i dati vengono persi al riavvio e non vengono condivisi).
  • Distribuito: Redis (standard industriale).
  • Strategie: Cache-Aside, Write-Through.
  • HTTP Caching: Usa gli header ETag, Cache-Control.

Rarità: Comune Difficoltà: Media


19. Database Connection Pooling.

Risposta: Aprire una nuova connessione al database per ogni richiesta è costoso.

  • Pooling: Mantiene una cache di connessioni al database che possono essere riutilizzate.
  • Node.js: Librerie come pg (PostgreSQL) o mongoose gestiscono automaticamente il pooling. È necessario configurare la dimensione del pool in base al carico di lavoro e ai limiti del DB.

Rarità: Media Difficoltà: Media


20. Come gestisci il caricamento di file in Node.js?

Risposta:

  • Multipart/form-data: La codifica standard per il caricamento di file.
  • Librerie: multer (middleware per Express), formidable, busboy.
  • Storage: Non archiviare i file nel filesystem del server (statelessness). Carica su storage cloud come AWS S3. Trasmetti il file direttamente a S3 per evitare di caricarlo in memoria.

Rarità: Comune Difficoltà: Media


Prestazioni & Testing (10 Domande)

21. Come esegui il debug di una perdita di memoria in Node.js?

Risposta:

  • Sintomi: Aumento dell'utilizzo della memoria nel tempo (RSS), eventuale crash.
  • Strumenti:
    • Node.js Inspector: Flag --inspect, connettiti con Chrome DevTools.
    • Heap Snapshots: Scatta snapshot e confrontali per trovare oggetti che non vengono garbage collected.
    • process.memoryUsage(): Monitora programmaticamente.

Rarità: Comune Difficoltà: Difficile


22. Profilazione delle applicazioni Node.js.

Risposta: La profilazione aiuta a identificare i colli di bottiglia della CPU.

  • Profiler Integrato: node --prof app.js. Genera un file di log. Elaboralo con node --prof-process isolate-0x...log.
  • Clinic.js: Una suite di strumenti (clinic doctor, clinic flame, clinic bubbleprof) per diagnosticare problemi di prestazioni.

Rarità: Media Difficoltà: Difficile


23. Spiega la regola "Non bloccare l'Event Loop".

Risposta: Poiché c'è solo un thread, se esegui un'operazione sincrona a lunga esecuzione (ad esempio, calcolo pesante, lettura sincrona di file, regex complessa), l'Event Loop si ferma. Nessun'altra richiesta può essere elaborata.

  • Soluzione: Partiziona il calcolo (setImmediate), usa Worker Threads o delega a un microservizio.

Rarità: Molto Comune Difficoltà: Facile


24. Unit Testing vs Integration Testing in Node.js.

Risposta:

  • Unit Testing: Test di singole funzioni/moduli in isolamento. Mock delle dipendenze. (Strumenti: Jest, Mocha, Chai).
  • Integration Testing: Test di come i moduli lavorano insieme (ad esempio, endpoint API + Database). (Strumenti: Supertest).

Rarità: Comune Difficoltà: Facile


25. Cos'è TDD (Test Driven Development)?

Risposta: Un processo di sviluppo in cui scrivi il test prima del codice.

  1. Scrivi un test che fallisce (Red).
  2. Scrivi il codice minimo per superare il test (Green).
  3. Riorganizza il codice (Refactor).

Rarità: Media Difficoltà: Media


26. Come gestisci il logging in un'app Node.js di produzione?

Risposta: Il logging in produzione deve essere strutturato, ricercabile e sicuro per gli strumenti di osservabilità.

  • Usa un logger: Pino, Winston o il logger della piattaforma invece di console.log sparsi.
  • Struttura: Emetti JSON con request ID, identificatori utente o tenant quando sicuro, route, stato, latenza e metadati dell’errore.
  • Livelli: Usa error, warn, info e debug in modo coerente.
  • Redazione: Non loggare token, password, dati di pagamento completi o contenuti privati degli utenti.
  • Correlazione: Collega log, metriche e trace per diagnosticare incidenti tra servizi.

Rarità: Comune Difficoltà: Facile


27. Spiega il Semantic Versioning (SemVer).

Risposta: Formato: MAJOR.MINOR.PATCH (ad esempio, 1.2.3).

  • MAJOR: Modifiche API incompatibili.
  • MINOR: Funzionalità retrocompatibile.
  • PATCH: Correzioni di bug retrocompatibili.
  • ^ vs ~: ^1.2.3 si aggiorna a <2.0.0. ~1.2.3 si aggiorna a <1.3.0.

Rarità: Comune Difficoltà: Facile


28. Cosa sono le Variabili d'Ambiente e come le gestisci?

Risposta:

  • Scopo: Configurazione che varia tra gli ambienti (Dev, Staging, Prod) come URL DB, chiavi API.
  • Utilizzo: process.env.VARIABLE_NAME.
  • Gestione: File .env (utilizzando il pacchetto dotenv) per lo sviluppo locale. In produzione, impostale nel sistema operativo o nelle impostazioni del container/piattaforma.

Rarità: Comune Difficoltà: Facile


29. Come distribuisci un'applicazione Node.js?

Risposta:

  • Process Manager: PM2 (mantiene l'app attiva, gestisce i riavvii, i log).
  • Reverse Proxy: Nginx (gestisce SSL, file statici, bilanciamento del carico) -> App Node.js.
  • Containerizzazione: Docker (standard).
  • Orchestrazione: Kubernetes.
  • CI/CD: GitHub Actions, Jenkins.

Rarità: Comune Difficoltà: Media


30. Cos'è Event Emitter?

Risposta: Il modulo events è il cuore dell'architettura event-driven di Node.js.

  • Utilizzo:
    const EventEmitter = require('events');
    const myEmitter = new EventEmitter();
    myEmitter.on('event', () => console.log('an event occurred!'));
    myEmitter.emit('event');
  • Molti moduli core lo estendono: fs.ReadStream, net.Server, http.Server.

Rarità: Comune Difficoltà: Facile

Newsletter subscription

Consigli di carriera settimanali che funzionano davvero

Ricevi le ultime idee direttamente nella tua casella di posta

Il Tuo Prossimo Colloquio Dista Solo un Curriculum

Crea un curriculum professionale e ottimizzato in pochi minuti. Non servono competenze di design—solo risultati comprovati.

Crea il mio curriculum

Condividi questo post

Raddoppia le Tue Chiamate per Colloqui

I candidati che personalizzano il loro curriculum in base alla descrizione del lavoro ottengono 2,5 volte più colloqui. Usa la nostra IA per personalizzare automaticamente il tuo CV per ogni singola candidatura istantaneamente.