Una richiesta può raggiungere il server senza che il client riceva la risposta. Per esempio, il timeout può scadere dopo la registrazione di un pagamento o la creazione di un ordine. Se il client ritenta alla cieca, il sistema potrebbe eseguire di nuovo l’operazione. L’idempotenza nelle API aiuta a prevenire questo risultato: permette di riconoscere più richieste come tentativi della stessa operazione e di limitarne gli effetti duplicati.
Non basta, però, aggiungere un’intestazione con una chiave. Occorre stabilire chi la genera, per quanto tempo è valida, quale risposta riproporre e come coordinare le scritture locali con i servizi esterni. L’obiettivo non è nascondere gli errori né promettere un’esecuzione esattamente una volta, ma rendere sicuri i nuovi tentativi in base a condizioni esplicite.
Che cosa significa idempotenza e quale problema risolve

Un’operazione è idempotente quando ripeterla con gli stessi dati non modifica il risultato finale dopo la prima esecuzione. Per esempio, impostare l’indirizzo di spedizione di un ordine su un valore preciso può essere idempotente: applicare due volte lo stesso aggiornamento lascia la risorsa nello stato che avrebbe raggiunto applicandolo una sola volta.
Al contrario, un’operazione come «aggiungere un’unità al carrello» non è idempotente di per sé: ripeterla può aumentare la quantità due volte. Anche creare un ordine o registrare un addebito può produrre effetti aggiuntivi se ogni richiesta viene interpretata come una nuova intenzione. In questi casi, una chiave idempotente può associare i nuovi tentativi allo stesso tentativo logico.
Idempotenza non significa che tutte le risposte siano identiche o che la richiesta non possa mai fallire. Significa controllare gli effetti delle ripetizioni secondo un contratto definito. Un client può ricevere un errore e, al nuovo tentativo, ottenere il risultato già registrato oppure ricevere nuovamente un errore definitivo. L’importante è evitare che il server produca inavvertitamente un secondo effetto.
Quando introdurre l’idempotenza e quando non serve
Il rischio si presenta quando un’operazione ha conseguenze rilevanti e il client non può sapere con certezza se il server l’ha completata. Una risposta persa, un timeout, una disconnessione o un nuovo tentativo automatico possono rendere ambiguo il risultato. È un aspetto particolarmente importante quando si creano ordini, si avviano pagamenti, si riservano scorte o si elaborano richieste.
Prima di introdurre chiavi, verifica il contratto esistente. Un aggiornamento che imposta uno stato preciso può essere idempotente per progettazione. Una normale operazione di lettura, di solito, non presenta questo problema. Un endpoint che crea ogni volta una nuova risorsa o un nuovo movimento, invece, ha bisogno di una strategia chiara se i client possono ritentare.
L’idempotenza ha un costo: archiviazione delle chiavi e dei risultati, regole di scadenza, gestione della concorrenza e ulteriori casi da testare. Non aggiungerla indiscriminatamente a ogni endpoint. Dai priorità ai casi in cui si verificano tutte queste condizioni:
- L’operazione può produrre un effetto duplicato difficile o costoso da annullare.
- Il client o l’infrastruttura ritentano in caso di errori temporanei.
- La perdita della risposta impedisce di capire se l’operazione è stata completata.
Definisci anche che cosa significa «stesso tentativo» per l’attività. Due acquisti intenzionalmente distinti non devono essere confusi solo perché hanno lo stesso importo e lo stesso contenuto. La deduplicazione deve basarsi su una chiave del tentativo, non su supposizioni circa la somiglianza delle richieste.
Progettare una chiave: origine, unicità, ambito e durata
Di norma, il client genera una chiave univoca per ogni operazione logica e la riutilizza per tutti i nuovi tentativi. Può inviarla tramite un’intestazione concordata o nel corpo della richiesta, purché il contratto sia esplicito. Se genera una chiave nuova a ogni tentativo, il server non potrà collegare le richieste. Se riutilizza la stessa chiave per un nuovo acquisto, il server potrebbe bloccare un’intenzione legittima.
La chiave identifica il tentativo, ma non sostituisce autenticazione e autorizzazione. Va associata a un ambito, per esempio all’account o all’esercente autenticato e al tipo di operazione. In questo modo si evita che una coincidenza tra chiavi di client diversi mescoli i risultati. Il server deve verificare questi limiti a ogni richiesta.
Conserva anche un’impronta della richiesta normalizzata: i campi che determinano l’effetto, applicando regole stabili per i valori predefiniti e la rappresentazione dei dati. Se una chiave esiste già e arriva una richiesta con contenuto incompatibile, rifiutala come conflitto: non considerarla un nuovo tentativo valido e non eseguire il nuovo contenuto. L’impronta deve escludere i dati irrilevanti, ma includere quelli che cambiano l’intenzione, come importo o valuta.
La durata dipende dal comportamento dei client, delle code e dei processi di ripristino. Una finestra troppo breve permette a un nuovo tentativo tardivo di ripetere l’effetto; una troppo lunga accumula record e può impedire riutilizzi legittimi. Stabilisci un periodo coerente con la durata massima ragionevole dei tentativi e comunica che cosa succede alla sua scadenza. Per operazioni finanziarie o ad alto impatto, può essere necessario conservare un riferimento durevole al risultato anche oltre la finestra operativa.
Risposte per chiavi ripetute e richieste in corso
Il contratto deve distinguere più situazioni. Se l’operazione associata alla chiave è terminata e l’impronta coincide, il server può restituire il risultato archiviato dell’operazione originale. In genere, questo comprende il codice e il corpo pertinenti, ma non necessariamente ogni intestazione di trasporto. La risposta dovrebbe consentire al client di identificare la risorsa creata o lo stato raggiunto.
Se l’elaborazione è ancora in corso, evita di avviare una seconda esecuzione. Puoi restituire uno stato che indichi che il processo continua oppure un conflitto temporaneo che inviti a controllare o ritentare più tardi. Il client ha bisogno di istruzioni chiare: quanto aspettare, se mantenere la stessa chiave e come ottenere il risultato finale. Non presentare come riuscita un’operazione che non è ancora stata confermata.
Se la chiave esiste ma l’impronta è diversa, restituisci un errore esplicito e non modificare il record originale. Se la prima esecuzione è fallita, stabilisci quali errori vanno conservati come esito definitivo e quali consentono una nuova elaborazione. Un errore di validazione, per esempio, può essere definitivo per quella richiesta, mentre un’interruzione prima della conferma degli effetti può consentire il ripristino. Non esiste una regola universale: la scelta deve riflettere il punto in cui il sistema può dimostrare che cosa è successo.
Persistenza, concorrenza ed effetti negli altri sistemi
Quando la prenotazione della chiave e la creazione dell’effetto locale condividono un database, è opportuno coordinarle in modo atomico. Un vincolo di unicità su ambito e chiave aiuta a impedire che due richieste simultanee superino entrambe un controllo iniziale. La logica deve gestire la collisione e leggere lo stato creato dalla richiesta vincente, senza affidarsi soltanto a una sequenza di «cerca e poi inserisci».
Registra stati comprensibili, per esempio in corso, completato, fallito con possibilità di ripristino o fallito in modo definitivo. Aggiungi date e orari e definisci come recuperare i record il cui processo è stato interrotto. Un blocco che non scade mai può lasciare operazioni bloccate; uno che scade senza controlli può permettere a due processi di agire contemporaneamente. Prima di riprendere l’elaborazione, la procedura di recupero deve verificare lo stato dell’effetto.
La transazione locale non include automaticamente un fornitore di pagamenti o un altro servizio remoto. Se il sistema registra l’ordine e poi non riesce a contattare il fornitore, oppure se il fornitore elabora il pagamento ma la risposta va persa, occorre riconciliare gli stati. Quando è adatto, usa una transactional outbox per pubblicare il lavoro dopo la conferma della modifica locale e trasmetti al sistema esterno un riferimento stabile, se supporta la deduplicazione. Registra gli identificativi esterni e prevedi verifiche o riconciliazioni.
Non promettere un’esecuzione «esattamente una volta» da un capo all’altro solo perché esiste una chiave. Tra reti, database e fornitori possono verificarsi errori ambigui. La garanzia effettiva deve specificare quali effetti vengono deduplicati, in quale ambito, per quanto tempo e quali casi richiedono un intervento o una riconciliazione.
Errori frequenti e lista di controllo

- Generare una chiave nuova a ogni tentativo: il client deve conservare e riutilizzare quella del tentativo originale.
- Usare una chiave globale senza ambito: associala al client autenticato e all’operazione pertinente.
- Riutilizzare la chiave con contenuti diversi: confronta un’impronta e rifiuta gli usi incompatibili.
- Impostare la scadenza senza valutare i tentativi tardivi: documenta la finestra e decidi come recuperare le operazioni più vecchie.
- Rispondere in modo ambiguo in caso di concorrenza: spiega come verificare lo stato o ritentare mentre la prima richiesta è ancora attiva.
- Affidarsi alla chiave per coprire i sistemi esterni: prevedi riferimenti, riconciliazione e gestione degli errori parziali.
Prima di pubblicare l’endpoint, verifica che il client generi una chiave per ogni intenzione e la conservi dopo un timeout; che due richieste simultanee con la stessa chiave non duplichino l’effetto; e che la stessa chiave con dati diversi non avvii una nuova operazione. Testa anche gli errori prima e dopo la scrittura, i riavvii del processo, la scadenza e le risposte perse.
Infine, monitora il numero di chiavi ripetute, i conflitti tra impronte, le operazioni bloccate e le discrepanze con i servizi esterni. Questi segnali aiutano a individuare problemi di integrazione e a regolare il periodo di conservazione. Un’implementazione utile non elimina gli errori: chiarisce quali operazioni possono essere ripetute senza duplicarne gli effetti e offre un percorso sicuro per risolvere ciò che rimane incerto.
