← Blog
engineeringarchitecturebackendperformance

Il mindset asincrono

18 marzo 2026

Il mindset asincrono

La maggior parte dei problemi di performance non è causata da codice lento. È causata dal fare lavoro al momento sbagliato — in particolare, farlo in modo sincrono mentre l'utente aspetta, quando potrebbe accadere in background, prima o dopo la richiesta, senza bloccare nulla.

La soluzione di solito non è l'ottimizzazione. È un modello diverso di quando il lavoro accade.

Il default sincrono

Tutto parte sincrono perché il sincrono è più facile da ragionare. Arriva una richiesta. Fai il lavoro. Restituisci il risultato. Il codice si legge dall'alto verso il basso e il comportamento corrisponde all'ordine di lettura. Per operazioni semplici va bene.

La trappola è che il sincrono diventa il default anche quando non dovrebbe esserlo. Un utente invia un form — tu mandi un'email di conferma, generi un PDF, chiami un'API esterna, aggiorni tre record diversi, e poi restituisci una risposta. L'utente aspetta tutto questo. Se un passaggio è lento, tutto è lento. Se un passaggio fallisce, tutto fallisce. Hai trasformato il tempo di risposta in una funzione di tutto ciò che tocca quel percorso di codice.

Il modello sincrono confonde due cose che non devono per forza coincidere: ricevere una richiesta e completare il lavoro.

Ricevere non è completare

La maggior parte delle volte, gli utenti non hanno bisogno che il lavoro sia fatto — hanno bisogno di sapere che sarà fatto. Sono cose diverse, e confonderle è la fonte della maggior parte della latenza inutile.

Un'email di conferma non deve essere inviata prima di rispondere. Un report non deve essere generato prima che l'utente veda "il tuo report è in preparazione." Un'immagine non deve essere elaborata prima che l'upload venga confermato. In ciascun caso, l'utente ha bisogno di una ricevuta — la prova che il sistema ha ricevuto la richiesta e la gestirà. Non ha bisogno di aspettare che la gestione finisca.

Una volta separati il ricevere dal completare, un'intera classe di operazioni esce dal ciclo richiesta/risposta. La richiesta torna veloce. Il lavoro accade in background, al ritmo che gli serve, riprovato se fallisce, loggato se va in errore, senza nessun utente fermo davanti a uno spinner ad aspettarlo.

La coda come primitiva

I job in background hanno bisogno di un posto dove vivere. Una coda è l'astrazione giusta — non perché sia tecnicamente sofisticata, ma perché rende esplicite le cose giuste.

Una coda ti dà la retry logic gratis: se il job fallisce, riprovi, con backoff, fino a un limite. Ti dà isolamento: un picco in un tipo di lavoro non rallenta un altro. Ti dà visibilità: puoi vedere cosa è in attesa, cosa è in elaborazione, cosa è fallito. Queste sono proprietà che dovresti costruire a mano in un sistema sincrono, e di solito non lo fai, perché sei occupato a rilasciare feature.

Il pattern emerge a ogni scala. Al livello piccolo: un singolo processo Node.js con una coda in memoria, job che girano in background, niente bloccato. Al livello grande: worker dedicati, code distribuite, gestione dei dead-letter, meccanismi di replay. L'idea di fondo è la stessa — il lavoro che non deve accadere adesso non dovrebbe accadere adesso.

Quando applicarlo

L'asincrono non è sempre la risposta. Spostare tutto in background crea la propria complessità: eventual consistency da ragionare, stati di errore da comunicare, deduplicazione dei job da gestire. La disciplina è sapere quando il tradeoff vale.

Il test che uso: l'utente ha bisogno del risultato di questa operazione prima di poter fare altro? Se sì — deve essere veloce, o la UX va riprogettata in modo che non rimanga bloccato su di essa. Se no — è un candidato per il background.

Inviare una notifica: no, background. Addebitare una carta: sì, sincrono, e deve essere veloce. Generare un'email di riepilogo settimanale: no, pianificala. Validare un input del form: sì, l'utente aspetta feedback. La regola non è "asincrono quando possibile" — è "sincrono solo quando necessario."


Il mindset asincrono è l'abitudine di chiedersi, per ogni operazione: l'utente ha bisogno di questo risultato, o ha bisogno di sapere che la richiesta è stata ricevuta? La distinzione sembra piccola. Cambia il modo in cui si progetta quasi tutto.