Integrazione sicura di API di terze parti per mantenere l'app reattiva durante outage. Scopri timeout, retry, circuit breaker e controlli rapidi.

Un'API di terze parti può guastarsi in modi che non sembrano un classico "down". Il problema più comune è la lentezza: le richieste si bloccano, le risposte arrivano in ritardo e la tua app continua ad aspettare. Se quelle chiamate sono sulla strada critica, un piccolo intoppo esterno si accumula all'interno del tuo sistema.
È così che un rallentamento locale diventa un blackout completo. Thread o worker restano in attesa, le code crescono, le transazioni sul database restano aperte più a lungo e le nuove richieste iniziano a scadere. Presto anche pagine che non usano l'API esterna sembrano rotte perché il sistema è sovraccarico di lavoro in attesa.
L'impatto è concreto. Un identity provider instabile blocca registrazioni e accessi. Un timeout del gateway di pagamento congela il checkout, lasciando gli utenti incerti se sono stati addebitati. Un ritardo nei messaggi ferma i reset password e le conferme d'ordine, causando una seconda ondata di retry e ticket di supporto.
L'obiettivo è semplice: isolare i guasti esterni in modo che i workflow core continuino a muoversi. Questo può significare permettere a un utente di piazzare un ordine mentre confermi il pagamento dopo, o consentire la registrazione anche se l'email di benvenuto fallisce.
Una metrica pratica di successo: quando un provider è lento o giù, la tua app dovrebbe comunque rispondere in modo rapido e chiaro, e l'area interessata dovrebbe rimanere piccola. Per esempio, la maggior parte delle richieste core termina ancora entro il tuo budget di latenza normale, gli errori restano confinati alle funzionalità che dipendono davvero da quell'API, gli utenti vedono uno stato chiaro (in coda, in attesa, riprova più tardi) e il recupero avviene automaticamente quando il provider torna.
La maggior parte dei guasti è prevedibile, anche se non è prevedibile quando avverrà. Nominali subito e puoi decidere cosa ritentare, cosa fermare e cosa mostrare all'utente.
Categorie comuni:
Non tutti gli errori sono uguali. I problemi transitori spesso vale la pena ritentarli perché la chiamata successiva può avere successo (interruzioni di rete, timeout, 502/503 e alcuni 429 dopo attesa). I problemi permanenti di solito non si risolvono da soli (credenziali non valide, endpoint sbagliati, richieste malformate, negazioni di permesso).
Trattare ogni errore allo stesso modo trasforma un piccolo incidente in downtime. Ritentare guasti permanenti spreca tempo, consuma i limiti di rate più velocemente e crea un arretrato che rallenta tutto il resto. Non ritentare mai i guasti transitori costringe gli utenti a ripetere azioni e fa perdere lavoro che avrebbe potuto completarsi poco dopo.
Presta particolare attenzione ai workflow dove una pausa sembra una rottura: checkout, login, reset password e notifiche (email/SMS/push). Un picco di 2 secondi in un'API di marketing è fastidioso. Un picco di 2 secondi nell'autorizzazione di pagamento blocca il fatturato.
Un test utile è: "Questa chiamata deve terminare per completare il compito principale dell'utente adesso?" Se sì, servono timeout stretti, retry attenti e un percorso di errore chiaro. Se no, spostala in una coda e mantieni l'app reattiva.
Un timeout è il tempo massimo che sei disposto ad aspettare prima di fermarti e andare avanti. Senza un limite chiaro, un provider lento può accumulare richieste in attesa e bloccare il lavoro importante.
Aiuta separare due tipi di attesa:
Scegliere i numeri non è questione di perfezione. Si tratta di allinearsi alla pazienza umana e al tuo workflow.
Un modo pratico per scegliere i timeout è partire dall'esperienza:
Il compromesso è reale. Troppo lungo e tieni occupati thread, worker e connessioni al DB. Troppo corto e crei falsi fallimenti e scateno retry inutili.
I retry aiutano quando un errore è probabilmente temporaneo: un breve problema di rete, un hiccup DNS o un 500/502/503 occasionale. In questi casi, un secondo tentativo può avere successo e gli utenti non se ne accorgeranno.
Il rischio è una tempesta di retry. Quando molti client falliscono insieme e ritentano in massa, possono sovraccaricare il provider (e i tuoi worker). Backoff e jitter prevengono questo comportamento.
Un budget di retry mantiene le cose sobrie. Mantieni pochi tentativi e limita il tempo totale in modo che i workflow core non restino bloccati aspettando qualcun altro.
Non ritentare errori cliente prevedibili come 400/422 (validazione), 401/403 (auth) o 404. Quelli quasi sempre falliranno di nuovo e aggiungono solo carico.
Un altro guardrail: ritenta le scritture (POST/PUT) solo quando hai idempotenza, altrimenti rischi addebiti doppi o record duplicati.
Idempotenza significa poter eseguire la stessa richiesta due volte e ottenere lo stesso risultato finale. Questo è importante perché i retry sono normali: le reti cadono, i server si riavviano e i client scadono. Senza idempotenza, un "utile" retry crea duplicati e problemi reali con soldi.
Immagina il checkout: l'API di pagamento è lenta, la tua app va in timeout e ritenti. Se la prima chiamata era andata a buon fine, il retry potrebbe causare un secondo addebito. Lo stesso rischio vale per azioni come creare un ordine, avviare un abbonamento, inviare un'email/SMS, emettere un rimborso o creare un ticket di supporto.
La soluzione è allegare una chiave di idempotenza (o un request ID) a ogni chiamata che "fa qualcosa". Deve essere unica per l'azione dell'utente, non per il tentativo. Il provider (o il tuo servizio) usa quella chiave per rilevare duplicati e restituire lo stesso esito invece di eseguire nuovamente l'azione.
Tratta la chiave di idempotenza come parte del modello dati, non come un header che speri nessuno dimentichi.
Genera una chiave quando l'utente avvia l'azione (per esempio quando clicca Pay), poi salvala con il tuo record locale.
Ad ogni tentativo:
Se sei il "provider" per chiamate interne, applica lo stesso comportamento lato server.
Un circuit breaker è un interruttore di sicurezza. Quando un servizio esterno inizia a fallire, smetti di chiamarlo per un breve periodo invece di accumulare richieste che probabilmente andranno in timeout.
I circuit breaker hanno solitamente tre stati:
Quando il breaker è aperto, la tua app dovrebbe comportarsi in modo prevedibile. Se un'API di validazione indirizzi è giù durante la registrazione, accetta l'indirizzo e segnalo per una revisione successiva. Se un controllo rischio pagamento è giù, metti l'ordine in coda per revisione manuale o disabilita temporaneamente quell'opzione e spiegalo.
Scegli soglie che combacino con l'impatto sull'utente:
Mantieni i cooldown brevi (secondi fino a un minuto) e limita le probe in half-open. L'obiettivo è proteggere prima i workflow core, poi recuperare rapidamente.
Quando un'API esterna è lenta o giù, l'obiettivo è far muovere l'utente. Questo significa avere un Piano B onesto su ciò che è successo.
Un fallback è ciò che fa la tua app quando l'API non risponde in tempo. Le opzioni includono usare dati in cache, passare a una modalità degradate (nascondere widget non essenziali, disabilitare azioni opzionali), chiedere input all'utente invece di chiamare l'API (inserimento manuale dell'indirizzo), o mostrare un messaggio chiaro con il passo successivo.
Sii onesto: non dire che qualcosa è stato completato se non lo è stato.
Se il lavoro non deve terminare dentro la richiesta utente, spostalo in una coda e rispondi velocemente. Casi comuni: invio email, sincronizzazione con CRM, generazione report e invio di eventi analytics.
Fallisci velocemente per azioni core. Se un'API non è necessaria per completare il checkout (o la creazione account), non bloccare la richiesta. Accetta l'ordine, metti la chiamata esterna in coda e riconcilia dopo. Se l'API è richiesta (per esempio autorizzazione pagamento), fallisci rapidamente con un messaggio chiaro e non far aspettare l'utente.
Quello che l'utente vede dovrebbe combaciare con quello che succede dietro le quinte: uno stato chiaro (completato, in sospeso, fallito), una promessa che puoi mantenere (ricevuta ora, conferma dopo), un modo per ritentare e un record visibile nell'UI (registro attività, badge "in sospeso").
I rate limit sono il modo del provider per dire: "Puoi chiamarci, ma non troppo spesso." Li raggiungerai prima di quanto pensi: picchi di traffico, job in background che partono insieme o un bug che loopa sugli errori.
Inizia controllando quante richieste generi. Raggruppa quando possibile, metti in cache le risposte anche per 30-60 secondi quando è sicuro, e applica throttling lato client così la tua app non burst più velocemente di quanto il provider permetta.
Quando ricevi un 429 Too Many Requests, trattalo come un segnale per rallentare.
Retry-After quando viene fornito.Limita anche la concorrenza. Un singolo workflow (come sincronizzare contatti) non dovrebbe consumare tutti gli slot worker e soffocare flussi critici come login o checkout. Pool separati o limiti per funzionalità aiutano.
Ogni chiamata di terze parti ha bisogno di un piano per i guasti. Non serve la perfezione. Serve comportamento prevedibile quando il provider ha una giornata no.
Decidi cosa succede se la chiamata fallisce ora. Un calcolo tasse durante il checkout potrebbe essere must-have. Sincronizzare un contatto marketing può di solito aspettare. Questa scelta guida tutto il resto.
Scegli timeout per tipo di chiamata e mantienili coerenti. Poi imposta un budget di retry così non continui a picchiare un'API lenta.
Se una richiesta può creare qualcosa o addebitare soldi, aggiungi chiavi di idempotenza e salva un record della richiesta. Se una richiesta di pagamento va in timeout, un retry non dovrebbe doppiare l'addebito. Il tracciamento aiuta anche il supporto a rispondere: "È passato?"
Quando gli errori aumentano, smetti di chiamare il provider per un breve periodo. Per le chiamate must-have, mostra un percorso "Riprova" chiaro. Per quelle che possono aspettare, metti il lavoro in coda e processalo dopo.
Traccia latenza, tasso di errori ed eventi di apertura/chiusura del breaker. Allerta sui cambiamenti sostenuti, non sui singoli blip.
La maggior parte degli outage API non parte grande. Diventa grande perché la tua app reagisce nel modo peggiore: aspetta troppo, ritenta troppo aggressivamente e occupa gli stessi worker che mantengono tutto il resto in funzione.
Questi pattern causano cascade:
Piccole correzioni prevengono grandi outage: ritenta solo errori probabilmente temporanei (timeout, alcuni 429, alcuni 5xx) e limita i tentativi con backoff e jitter; mantieni timeout brevi e intenzionali; richiedi idempotenza per ogni operazione che crea o addebita; e progetta per outage parziali.
Prima di mettere un'integrazione in produzione, fai una passata veloce con mentalità da guasto. Se non riesci a rispondere "sì" a un punto, considera un blocco al rilascio per i workflow core come registrazione, checkout o invio di messaggi.
Se un provider di pagamenti inizia a andare in timeout, il comportamento giusto è "il checkout si carica comunque, l'utente vede un messaggio chiaro e non resti appeso per sempre", non "tutto resta bloccato fino al timeout".
Immagina un checkout che chiama tre servizi: un'API di pagamento per addebitare la carta, un'API tasse per calcolare l'imposta e un'API email per inviare la ricevuta.
La chiamata al pagamento è l'unica che deve essere sincrona. Problemi nella tassa o nell'email non dovrebbero bloccare l'acquisto.
Supponiamo che l'API tasse a volte impieghi 8-15 secondi. Se il checkout aspetta, gli utenti abbandonano il carrello e la tua app occupa worker.
Un flusso più sicuro:
Risultato: meno carrelli abbandonati e meno ordini bloccati quando il provider tasse è lento.
L'email di ricevuta è importante, ma non dovrebbe mai bloccare la cattura del pagamento. Se l'API email fallisce, il circuit breaker dovrebbe aprirsi dopo pochi fallimenti rapidi e bloccare le chiamate per una finestra di cooldown.
Invece di inviare l'email inline, metti un job "invia ricevuta" in coda con una chiave di idempotenza (per esempio order_id + email_type). Se il provider è giù, la coda ritenta in background e il cliente vede comunque un acquisto riuscito.
Risultato: meno ticket di supporto per conferme mancanti e niente perdita di fatturato perché il checkout falliva per ragioni non legate al pagamento.
Scegli un workflow che crea più problemi quando si rompe (checkout, registrazione, fatturazione) e rendilo la tua integrazione di riferimento. Poi copia gli stessi default ovunque.
Un ordine semplice di rollout:
Documenta i tuoi default e mantienili noiosi: un connect timeout, un request timeout, conteggio massimo di retry, intervallo di backoff, cooldown del breaker e le regole su cosa è retryabile.
Esegui un drill di guasto prima di estendere al workflow successivo. Forza i timeout (o blocca il provider in un ambiente di test), poi conferma che l'utente vede un messaggio utile, i fallback funzionano e i retry in coda non si accumulano all'infinito.
Se stai costruendo nuovi prodotti velocemente, vale la pena trasformare questi default di affidabilità in un template riutilizzabile. Per i team che usano Koder.ai (koder.ai), questo spesso significa definire timeout, retry, idempotenza e regole del breaker una volta, poi applicare lo stesso pattern tra i nuovi servizi mentre generi e iteri.